mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
feat: ability to toggle MCP Servers in TUI (#4509)
This commit is contained in:
parent
0c77c46dc7
commit
203f3312ee
13 changed files with 562 additions and 5 deletions
|
|
@ -12,6 +12,7 @@ import { SDKProvider, useSDK } from "@tui/context/sdk"
|
|||
import { SyncProvider, useSync } from "@tui/context/sync"
|
||||
import { LocalProvider, useLocal } from "@tui/context/local"
|
||||
import { DialogModel, useConnected } from "@tui/component/dialog-model"
|
||||
import { DialogMcp } from "@tui/component/dialog-mcp"
|
||||
import { DialogStatus } from "@tui/component/dialog-status"
|
||||
import { DialogThemeList } from "@tui/component/dialog-theme-list"
|
||||
import { DialogHelp } from "./ui/dialog-help"
|
||||
|
|
@ -301,6 +302,14 @@ function App() {
|
|||
dialog.replace(() => <DialogAgent />)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Toggle MCPs",
|
||||
value: "mcp.list",
|
||||
category: "Agent",
|
||||
onSelect: () => {
|
||||
dialog.replace(() => <DialogMcp />)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Agent cycle",
|
||||
value: "agent.cycle",
|
||||
|
|
|
|||
86
packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
Normal file
86
packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { createMemo, createSignal } from "solid-js"
|
||||
import { useLocal } from "@tui/context/local"
|
||||
import { useSync } from "@tui/context/sync"
|
||||
import { map, pipe, entries, sortBy } from "remeda"
|
||||
import { DialogSelect, type DialogSelectRef, type DialogSelectOption } from "@tui/ui/dialog-select"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { Keybind } from "@/util/keybind"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
import { useSDK } from "@tui/context/sdk"
|
||||
|
||||
function Status(props: { enabled: boolean; loading: boolean }) {
|
||||
const { theme } = useTheme()
|
||||
if (props.loading) {
|
||||
return <span style={{ fg: theme.textMuted }}>⋯ Loading</span>
|
||||
}
|
||||
if (props.enabled) {
|
||||
return <span style={{ fg: theme.success, attributes: TextAttributes.BOLD }}>✓ Enabled</span>
|
||||
}
|
||||
return <span style={{ fg: theme.textMuted }}>○ Disabled</span>
|
||||
}
|
||||
|
||||
export function DialogMcp() {
|
||||
const local = useLocal()
|
||||
const sync = useSync()
|
||||
const sdk = useSDK()
|
||||
const [, setRef] = createSignal<DialogSelectRef<unknown>>()
|
||||
const [loading, setLoading] = createSignal<string | null>(null)
|
||||
|
||||
const options = createMemo(() => {
|
||||
// Track sync data and loading state to trigger re-render when they change
|
||||
const mcpData = sync.data.mcp
|
||||
const loadingMcp = loading()
|
||||
|
||||
return pipe(
|
||||
mcpData ?? {},
|
||||
entries(),
|
||||
sortBy(([name]) => name),
|
||||
map(([name, status]) => ({
|
||||
value: name,
|
||||
title: name,
|
||||
description: status.status === "failed" ? "failed" : status.status,
|
||||
footer: <Status enabled={local.mcp.isEnabled(name)} loading={loadingMcp === name} />,
|
||||
category: undefined,
|
||||
})),
|
||||
)
|
||||
})
|
||||
|
||||
const keybinds = createMemo(() => [
|
||||
{
|
||||
keybind: Keybind.parse("space")[0],
|
||||
title: "toggle",
|
||||
onTrigger: async (option: DialogSelectOption<string>) => {
|
||||
// Prevent toggling while an operation is already in progress
|
||||
if (loading() !== null) return
|
||||
|
||||
setLoading(option.value)
|
||||
try {
|
||||
await local.mcp.toggle(option.value)
|
||||
// Refresh MCP status from server
|
||||
const status = await sdk.client.mcp.status()
|
||||
if (status.data) {
|
||||
sync.set("mcp", status.data)
|
||||
} else {
|
||||
console.error("Failed to refresh MCP status: no data returned")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to toggle MCP:", error)
|
||||
} finally {
|
||||
setLoading(null)
|
||||
}
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<DialogSelect
|
||||
ref={setRef}
|
||||
title="MCPs"
|
||||
options={options()}
|
||||
keybind={keybinds()}
|
||||
onSelect={(option) => {
|
||||
// Don't close on select, only on escape
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -307,10 +307,14 @@ export function Autocomplete(props: {
|
|||
},
|
||||
{
|
||||
display: "/status",
|
||||
aliases: ["/mcp"],
|
||||
description: "show status",
|
||||
onSelect: () => command.trigger("opencode.status"),
|
||||
},
|
||||
{
|
||||
display: "/mcp",
|
||||
description: "toggle MCPs",
|
||||
onSelect: () => command.trigger("mcp.list"),
|
||||
},
|
||||
{
|
||||
display: "/theme",
|
||||
description: "toggle theme",
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ import { createSimpleContext } from "./helper"
|
|||
import { useToast } from "../ui/toast"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { useArgs } from "./args"
|
||||
import { useSDK } from "./sdk"
|
||||
import { RGBA } from "@opentui/core"
|
||||
|
||||
export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
name: "Local",
|
||||
init: () => {
|
||||
const sync = useSync()
|
||||
const sdk = useSDK()
|
||||
const toast = useToast()
|
||||
|
||||
function isModelValid(model: { providerID: string; modelID: string }) {
|
||||
|
|
@ -310,9 +312,27 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||
}
|
||||
})
|
||||
|
||||
const mcp = {
|
||||
isEnabled(name: string) {
|
||||
const status = sync.data.mcp[name]
|
||||
return status?.status === "connected"
|
||||
},
|
||||
async toggle(name: string) {
|
||||
const status = sync.data.mcp[name]
|
||||
if (status?.status === "connected") {
|
||||
// Disable: disconnect the MCP
|
||||
await sdk.client.mcp.disconnect({ name })
|
||||
} else {
|
||||
// Enable/Retry: connect the MCP (handles disabled, failed, and other states)
|
||||
await sdk.client.mcp.connect({ name })
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const result = {
|
||||
model,
|
||||
agent,
|
||||
mcp,
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,8 +24,12 @@ export function Home() {
|
|||
return Object.values(sync.data.mcp).some((x) => x.status === "failed")
|
||||
})
|
||||
|
||||
const connectedMcpCount = createMemo(() => {
|
||||
return Object.values(sync.data.mcp).filter((x) => x.status === "connected").length
|
||||
})
|
||||
|
||||
const Hint = (
|
||||
<Show when={Object.keys(sync.data.mcp).length > 0}>
|
||||
<Show when={connectedMcpCount() > 0}>
|
||||
<box flexShrink={0} flexDirection="row" gap={1}>
|
||||
<text fg={theme.text}>
|
||||
<Switch>
|
||||
|
|
@ -35,7 +39,7 @@ export function Home() {
|
|||
</Match>
|
||||
<Match when={true}>
|
||||
<span style={{ fg: theme.success }}>•</span>{" "}
|
||||
{Locale.pluralize(Object.values(sync.data.mcp).length, "{} mcp server", "{} mcp servers")}
|
||||
{Locale.pluralize(connectedMcpCount(), "{} mcp server", "{} mcp servers")}
|
||||
</Match>
|
||||
</Switch>
|
||||
</text>
|
||||
|
|
@ -85,7 +89,7 @@ export function Home() {
|
|||
<span style={{ fg: theme.success }}>⊙ </span>
|
||||
</Match>
|
||||
</Switch>
|
||||
{Object.keys(sync.data.mcp).length} MCP
|
||||
{connectedMcpCount()} MCP
|
||||
</text>
|
||||
<text fg={theme.textMuted}>/status</text>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,12 @@ export namespace MCP {
|
|||
|
||||
await Promise.all(
|
||||
Object.entries(config).map(async ([key, mcp]) => {
|
||||
// If disabled by config, mark as disabled without trying to connect
|
||||
if (mcp.enabled === false) {
|
||||
status[key] = { status: "disabled" }
|
||||
return
|
||||
}
|
||||
|
||||
const result = await create(key, mcp).catch(() => undefined)
|
||||
if (!result) return
|
||||
|
||||
|
|
@ -319,18 +325,73 @@ export namespace MCP {
|
|||
}
|
||||
|
||||
export async function status() {
|
||||
return state().then((state) => state.status)
|
||||
const s = await state()
|
||||
const cfg = await Config.get()
|
||||
const config = cfg.mcp ?? {}
|
||||
const result: Record<string, Status> = {}
|
||||
|
||||
// Include all MCPs from config, not just connected ones
|
||||
for (const key of Object.keys(config)) {
|
||||
result[key] = s.status[key] ?? { status: "disabled" }
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function clients() {
|
||||
return state().then((state) => state.clients)
|
||||
}
|
||||
|
||||
export async function connect(name: string) {
|
||||
const cfg = await Config.get()
|
||||
const config = cfg.mcp ?? {}
|
||||
const mcp = config[name]
|
||||
if (!mcp) {
|
||||
log.error("MCP config not found", { name })
|
||||
return
|
||||
}
|
||||
|
||||
const result = await create(name, { ...mcp, enabled: true })
|
||||
|
||||
if (!result) {
|
||||
const s = await state()
|
||||
s.status[name] = {
|
||||
status: "failed",
|
||||
error: "Unknown error during connection",
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const s = await state()
|
||||
s.status[name] = result.status
|
||||
if (result.mcpClient) {
|
||||
s.clients[name] = result.mcpClient
|
||||
}
|
||||
}
|
||||
|
||||
export async function disconnect(name: string) {
|
||||
const s = await state()
|
||||
const client = s.clients[name]
|
||||
if (client) {
|
||||
await client.close().catch((error) => {
|
||||
log.error("Failed to close MCP client", { name, error })
|
||||
})
|
||||
delete s.clients[name]
|
||||
}
|
||||
s.status[name] = { status: "disabled" }
|
||||
}
|
||||
|
||||
export async function tools() {
|
||||
const result: Record<string, Tool> = {}
|
||||
const s = await state()
|
||||
const clientsSnapshot = await clients()
|
||||
|
||||
for (const [clientName, client] of Object.entries(clientsSnapshot)) {
|
||||
// Only include tools from connected MCPs (skip disabled ones)
|
||||
if (s.status[clientName]?.status !== "connected") {
|
||||
continue
|
||||
}
|
||||
|
||||
const tools = await client.tools().catch((e) => {
|
||||
log.error("failed to get tools", { clientName, error: e.message })
|
||||
const failedStatus = {
|
||||
|
|
|
|||
|
|
@ -1984,6 +1984,52 @@ export namespace Server {
|
|||
return c.json({ success: true as const })
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/mcp/:name/connect",
|
||||
describeRoute({
|
||||
description: "Connect an MCP server",
|
||||
operationId: "mcp.connect",
|
||||
responses: {
|
||||
200: {
|
||||
description: "MCP server connected successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(z.boolean()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
validator("param", z.object({ name: z.string() })),
|
||||
async (c) => {
|
||||
const { name } = c.req.valid("param")
|
||||
await MCP.connect(name)
|
||||
return c.json(true)
|
||||
},
|
||||
)
|
||||
.post(
|
||||
"/mcp/:name/disconnect",
|
||||
describeRoute({
|
||||
description: "Disconnect an MCP server",
|
||||
operationId: "mcp.disconnect",
|
||||
responses: {
|
||||
200: {
|
||||
description: "MCP server disconnected successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(z.boolean()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
validator("param", z.object({ name: z.string() })),
|
||||
async (c) => {
|
||||
const { name } = c.req.valid("param")
|
||||
await MCP.disconnect(name)
|
||||
return c.json(true)
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/lsp",
|
||||
describeRoute({
|
||||
|
|
|
|||
|
|
@ -3996,6 +3996,88 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/mcp/{name}/connect": {
|
||||
"post": {
|
||||
"operationId": "mcp.connect",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "directory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"description": "Connect an MCP server",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MCP server connected successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.mcp.connect({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/mcp/{name}/disconnect": {
|
||||
"post": {
|
||||
"operationId": "mcp.disconnect",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "directory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"description": "Disconnect an MCP server",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MCP server disconnected successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.mcp.disconnect({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/lsp": {
|
||||
"get": {
|
||||
"operationId": "lsp.status",
|
||||
|
|
|
|||
|
|
@ -160,6 +160,10 @@ import type {
|
|||
McpAuthAuthenticateData,
|
||||
McpAuthAuthenticateResponses,
|
||||
McpAuthAuthenticateErrors,
|
||||
McpConnectData,
|
||||
McpConnectResponses,
|
||||
McpDisconnectData,
|
||||
McpDisconnectResponses,
|
||||
LspStatusData,
|
||||
LspStatusResponses,
|
||||
FormatterStatusData,
|
||||
|
|
@ -945,6 +949,27 @@ class Mcp extends _HeyApiClient {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect an MCP server
|
||||
*/
|
||||
public connect<ThrowOnError extends boolean = false>(options: Options<McpConnectData, ThrowOnError>) {
|
||||
return (options.client ?? this._client).post<McpConnectResponses, unknown, ThrowOnError>({
|
||||
url: "/mcp/{name}/connect",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect an MCP server
|
||||
*/
|
||||
public disconnect<ThrowOnError extends boolean = false>(options: Options<McpDisconnectData, ThrowOnError>) {
|
||||
return (options.client ?? this._client).post<McpDisconnectResponses, unknown, ThrowOnError>({
|
||||
url: "/mcp/{name}/disconnect",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
auth = new Auth({ client: this._client })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3494,6 +3494,46 @@ export type McpAuthAuthenticateResponses = {
|
|||
|
||||
export type McpAuthAuthenticateResponse = McpAuthAuthenticateResponses[keyof McpAuthAuthenticateResponses]
|
||||
|
||||
export type McpConnectData = {
|
||||
body?: never
|
||||
path: {
|
||||
name: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/mcp/{name}/connect"
|
||||
}
|
||||
|
||||
export type McpConnectResponses = {
|
||||
/**
|
||||
* MCP server connected successfully
|
||||
*/
|
||||
200: boolean
|
||||
}
|
||||
|
||||
export type McpConnectResponse = McpConnectResponses[keyof McpConnectResponses]
|
||||
|
||||
export type McpDisconnectData = {
|
||||
body?: never
|
||||
path: {
|
||||
name: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/mcp/{name}/disconnect"
|
||||
}
|
||||
|
||||
export type McpDisconnectResponses = {
|
||||
/**
|
||||
* MCP server disconnected successfully
|
||||
*/
|
||||
200: boolean
|
||||
}
|
||||
|
||||
export type McpDisconnectResponse = McpDisconnectResponses[keyof McpDisconnectResponses]
|
||||
|
||||
export type LspStatusData = {
|
||||
body?: never
|
||||
path?: never
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ import type {
|
|||
McpAuthRemoveResponses,
|
||||
McpAuthStartErrors,
|
||||
McpAuthStartResponses,
|
||||
McpConnectResponses,
|
||||
McpDisconnectResponses,
|
||||
McpLocalConfig,
|
||||
McpRemoteConfig,
|
||||
McpStatusResponses,
|
||||
|
|
@ -2077,6 +2079,62 @@ export class Mcp extends HeyApiClient {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect an MCP server
|
||||
*/
|
||||
public connect<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
name: string
|
||||
directory?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "name" },
|
||||
{ in: "query", key: "directory" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<McpConnectResponses, unknown, ThrowOnError>({
|
||||
url: "/mcp/{name}/connect",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect an MCP server
|
||||
*/
|
||||
public disconnect<ThrowOnError extends boolean = false>(
|
||||
parameters: {
|
||||
name: string
|
||||
directory?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "path", key: "name" },
|
||||
{ in: "query", key: "directory" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<McpDisconnectResponses, unknown, ThrowOnError>({
|
||||
url: "/mcp/{name}/disconnect",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
auth = new Auth({ client: this.client })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3503,6 +3503,46 @@ export type McpAuthAuthenticateResponses = {
|
|||
|
||||
export type McpAuthAuthenticateResponse = McpAuthAuthenticateResponses[keyof McpAuthAuthenticateResponses]
|
||||
|
||||
export type McpConnectData = {
|
||||
body?: never
|
||||
path: {
|
||||
name: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/mcp/{name}/connect"
|
||||
}
|
||||
|
||||
export type McpConnectResponses = {
|
||||
/**
|
||||
* MCP server connected successfully
|
||||
*/
|
||||
200: boolean
|
||||
}
|
||||
|
||||
export type McpConnectResponse = McpConnectResponses[keyof McpConnectResponses]
|
||||
|
||||
export type McpDisconnectData = {
|
||||
body?: never
|
||||
path: {
|
||||
name: string
|
||||
}
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/mcp/{name}/disconnect"
|
||||
}
|
||||
|
||||
export type McpDisconnectResponses = {
|
||||
/**
|
||||
* MCP server disconnected successfully
|
||||
*/
|
||||
200: boolean
|
||||
}
|
||||
|
||||
export type McpDisconnectResponse = McpDisconnectResponses[keyof McpDisconnectResponses]
|
||||
|
||||
export type LspStatusData = {
|
||||
body?: never
|
||||
path?: never
|
||||
|
|
|
|||
|
|
@ -3996,6 +3996,88 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/mcp/{name}/connect": {
|
||||
"post": {
|
||||
"operationId": "mcp.connect",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "directory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"description": "Connect an MCP server",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MCP server connected successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.mcp.connect({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/mcp/{name}/disconnect": {
|
||||
"post": {
|
||||
"operationId": "mcp.disconnect",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "directory",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"description": "Disconnect an MCP server",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MCP server disconnected successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.mcp.disconnect({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/lsp": {
|
||||
"get": {
|
||||
"operationId": "lsp.status",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue