chore: generate
This commit is contained in:
@@ -165,7 +165,7 @@ export namespace MCP {
|
|||||||
function defs(key: string, client: MCPClient, timeout?: number) {
|
function defs(key: string, client: MCPClient, timeout?: number) {
|
||||||
return Effect.tryPromise({
|
return Effect.tryPromise({
|
||||||
try: () => withTimeout(client.listTools(), timeout ?? DEFAULT_TIMEOUT),
|
try: () => withTimeout(client.listTools(), timeout ?? DEFAULT_TIMEOUT),
|
||||||
catch: (err) => err instanceof Error ? err : new Error(String(err)),
|
catch: (err) => (err instanceof Error ? err : new Error(String(err))),
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Effect.map((result) => result.tools),
|
Effect.map((result) => result.tools),
|
||||||
Effect.catch((err) => {
|
Effect.catch((err) => {
|
||||||
@@ -267,15 +267,15 @@ export namespace MCP {
|
|||||||
},
|
},
|
||||||
catch: (e) => (e instanceof Error ? e : new Error(String(e))),
|
catch: (e) => (e instanceof Error ? e : new Error(String(e))),
|
||||||
}),
|
}),
|
||||||
(t, exit) =>
|
(t, exit) => (Exit.isFailure(exit) ? Effect.tryPromise(() => t.close()).pipe(Effect.ignore) : Effect.void),
|
||||||
Exit.isFailure(exit)
|
|
||||||
? Effect.tryPromise(() => t.close()).pipe(Effect.ignore)
|
|
||||||
: Effect.void,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const DISABLED_RESULT: CreateResult = { status: { status: "disabled" } }
|
const DISABLED_RESULT: CreateResult = { status: { status: "disabled" } }
|
||||||
|
|
||||||
const connectRemote = Effect.fn("MCP.connectRemote")(function* (key: string, mcp: Config.Mcp & { type: "remote" }) {
|
const connectRemote = Effect.fn("MCP.connectRemote")(function* (
|
||||||
|
key: string,
|
||||||
|
mcp: Config.Mcp & { type: "remote" },
|
||||||
|
) {
|
||||||
const oauthDisabled = mcp.oauth === false
|
const oauthDisabled = mcp.oauth === false
|
||||||
const oauthConfig = typeof mcp.oauth === "object" ? mcp.oauth : undefined
|
const oauthConfig = typeof mcp.oauth === "object" ? mcp.oauth : undefined
|
||||||
let authProvider: McpOAuthProvider | undefined
|
let authProvider: McpOAuthProvider | undefined
|
||||||
@@ -333,21 +333,25 @@ export namespace MCP {
|
|||||||
status: "needs_client_registration" as const,
|
status: "needs_client_registration" as const,
|
||||||
error: "Server does not support dynamic client registration. Please provide clientId in config.",
|
error: "Server does not support dynamic client registration. Please provide clientId in config.",
|
||||||
}
|
}
|
||||||
return bus.publish(TuiEvent.ToastShow, {
|
return bus
|
||||||
title: "MCP Authentication Required",
|
.publish(TuiEvent.ToastShow, {
|
||||||
message: `Server "${key}" requires a pre-registered client ID. Add clientId to your config.`,
|
title: "MCP Authentication Required",
|
||||||
variant: "warning",
|
message: `Server "${key}" requires a pre-registered client ID. Add clientId to your config.`,
|
||||||
duration: 8000,
|
variant: "warning",
|
||||||
}).pipe(Effect.ignore, Effect.as(undefined))
|
duration: 8000,
|
||||||
|
})
|
||||||
|
.pipe(Effect.ignore, Effect.as(undefined))
|
||||||
} else {
|
} else {
|
||||||
pendingOAuthTransports.set(key, transport)
|
pendingOAuthTransports.set(key, transport)
|
||||||
lastStatus = { status: "needs_auth" as const }
|
lastStatus = { status: "needs_auth" as const }
|
||||||
return bus.publish(TuiEvent.ToastShow, {
|
return bus
|
||||||
title: "MCP Authentication Required",
|
.publish(TuiEvent.ToastShow, {
|
||||||
message: `Server "${key}" requires authentication. Run: opencode mcp auth ${key}`,
|
title: "MCP Authentication Required",
|
||||||
variant: "warning",
|
message: `Server "${key}" requires authentication. Run: opencode mcp auth ${key}`,
|
||||||
duration: 8000,
|
variant: "warning",
|
||||||
}).pipe(Effect.ignore, Effect.as(undefined))
|
duration: 8000,
|
||||||
|
})
|
||||||
|
.pipe(Effect.ignore, Effect.as(undefined))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +373,10 @@ export namespace MCP {
|
|||||||
if (lastStatus?.status === "needs_auth" || lastStatus?.status === "needs_client_registration") break
|
if (lastStatus?.status === "needs_auth" || lastStatus?.status === "needs_client_registration") break
|
||||||
}
|
}
|
||||||
|
|
||||||
return { client: undefined as MCPClient | undefined, status: (lastStatus ?? { status: "failed", error: "Unknown error" }) as Status }
|
return {
|
||||||
|
client: undefined as MCPClient | undefined,
|
||||||
|
status: (lastStatus ?? { status: "failed", error: "Unknown error" }) as Status,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const connectLocal = Effect.fn("MCP.connectLocal")(function* (key: string, mcp: Config.Mcp & { type: "local" }) {
|
const connectLocal = Effect.fn("MCP.connectLocal")(function* (key: string, mcp: Config.Mcp & { type: "local" }) {
|
||||||
@@ -392,7 +399,10 @@ export namespace MCP {
|
|||||||
|
|
||||||
const connectTimeout = mcp.timeout ?? DEFAULT_TIMEOUT
|
const connectTimeout = mcp.timeout ?? DEFAULT_TIMEOUT
|
||||||
return yield* connectTransport(transport, connectTimeout).pipe(
|
return yield* connectTransport(transport, connectTimeout).pipe(
|
||||||
Effect.map((client): { client: MCPClient | undefined; status: Status } => ({ client, status: { status: "connected" } })),
|
Effect.map((client): { client: MCPClient | undefined; status: Status } => ({
|
||||||
|
client,
|
||||||
|
status: { status: "connected" },
|
||||||
|
})),
|
||||||
Effect.catch((error): Effect.Effect<{ client: MCPClient | undefined; status: Status }> => {
|
Effect.catch((error): Effect.Effect<{ client: MCPClient | undefined; status: Status }> => {
|
||||||
const msg = error instanceof Error ? error.message : String(error)
|
const msg = error instanceof Error ? error.message : String(error)
|
||||||
log.error("local mcp startup failed", { key, command: mcp.command, cwd, error: msg })
|
log.error("local mcp startup failed", { key, command: mcp.command, cwd, error: msg })
|
||||||
@@ -409,9 +419,10 @@ export namespace MCP {
|
|||||||
|
|
||||||
log.info("found", { key, type: mcp.type })
|
log.info("found", { key, type: mcp.type })
|
||||||
|
|
||||||
const { client: mcpClient, status } = mcp.type === "remote"
|
const { client: mcpClient, status } =
|
||||||
? yield* connectRemote(key, mcp as Config.Mcp & { type: "remote" })
|
mcp.type === "remote"
|
||||||
: yield* connectLocal(key, mcp as Config.Mcp & { type: "local" })
|
? yield* connectRemote(key, mcp as Config.Mcp & { type: "remote" })
|
||||||
|
: yield* connectLocal(key, mcp as Config.Mcp & { type: "local" })
|
||||||
|
|
||||||
if (!mcpClient) {
|
if (!mcpClient) {
|
||||||
return { status } satisfies CreateResult
|
return { status } satisfies CreateResult
|
||||||
@@ -493,9 +504,7 @@ export namespace MCP {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = yield* create(key, mcp).pipe(
|
const result = yield* create(key, mcp).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||||
Effect.catch(() => Effect.succeed(undefined)),
|
|
||||||
)
|
|
||||||
if (!result) return
|
if (!result) return
|
||||||
|
|
||||||
s.status[key] = result.status
|
s.status[key] = result.status
|
||||||
@@ -643,9 +652,7 @@ export namespace MCP {
|
|||||||
return Effect.forEach(
|
return Effect.forEach(
|
||||||
Object.entries(s.clients).filter(([name]) => s.status[name]?.status === "connected"),
|
Object.entries(s.clients).filter(([name]) => s.status[name]?.status === "connected"),
|
||||||
([clientName, client]) =>
|
([clientName, client]) =>
|
||||||
fetchFromClient(clientName, client, listFn, label).pipe(
|
fetchFromClient(clientName, client, listFn, label).pipe(Effect.map((items) => Object.entries(items ?? {}))),
|
||||||
Effect.map((items) => Object.entries(items ?? {})),
|
|
||||||
),
|
|
||||||
{ concurrency: "unbounded" },
|
{ concurrency: "unbounded" },
|
||||||
).pipe(Effect.map((results) => Object.fromEntries<T & { client: string }>(results.flat())))
|
).pipe(Effect.map((results) => Object.fromEntries<T & { client: string }>(results.flat())))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -673,7 +673,6 @@ test(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Test: transport leak — local stdio timeout (#19168)
|
// Test: transport leak — local stdio timeout (#19168)
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user