From c2cda552848f86cdc4a000aeccddd993832d13c8 Mon Sep 17 00:00:00 2001 From: kmaclip Date: Wed, 8 Apr 2026 09:41:04 -0400 Subject: [PATCH] fix: handle outputSchema compilation errors in MCP tool discovery When an MCP server returns outputSchema in its tools/list response, the SDK's default AjvJsonSchemaValidator can throw during schema compilation, causing listTools() to fail entirely. This makes the server show "Failed to get tools" even though the tools themselves are valid. opencode doesn't use output schemas (only inputSchema is read in convertMcpTool), so validation failures should be non-fatal. Wrap AjvJsonSchemaValidator with a TolerantJsonSchemaValidator that catches compilation errors and returns a permissive fallback. Pass it to both Client constructors via the SDK's jsonSchemaValidator option. Closes #21373 --- packages/opencode/src/mcp/index.ts | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 8c92bb6b2e6d..6cd3e6016dd0 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -4,6 +4,8 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js" +import { AjvJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/ajv" +import type { jsonSchemaValidator as JsonSchemaValidatorProvider, JsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/types.js" import { CallToolResultSchema, type Tool as MCPToolDef, @@ -30,6 +32,29 @@ import { makeRuntime } from "@/effect/run-service" import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner" +/** + * Wraps AjvJsonSchemaValidator to catch schema compilation errors. + * opencode doesn't validate tool output schemas, so compilation failures + * (e.g. from complex or unsupported schemas) should not prevent tool discovery. + */ +class TolerantJsonSchemaValidator implements JsonSchemaValidatorProvider { + private inner = new AjvJsonSchemaValidator() + + getValidator(schema: any): JsonSchemaValidator { + try { + return this.inner.getValidator(schema) + } catch { + return (input: unknown) => ({ + valid: true as const, + data: input as T, + errorMessage: undefined, + }) + } + } +} + +const tolerantValidator = new TolerantJsonSchemaValidator() + export namespace MCP { const log = Log.create({ service: "mcp" }) const DEFAULT_TIMEOUT = 30_000 @@ -260,7 +285,7 @@ export namespace MCP { (t) => Effect.tryPromise({ try: () => { - const client = new Client({ name: "opencode", version: Installation.VERSION }) + const client = new Client({ name: "opencode", version: Installation.VERSION }, { jsonSchemaValidator: tolerantValidator }) return withTimeout(client.connect(t), timeout).then(() => client) }, catch: (e) => (e instanceof Error ? e : new Error(String(e))), @@ -743,7 +768,7 @@ export namespace MCP { return yield* Effect.tryPromise({ try: () => { - const client = new Client({ name: "opencode", version: Installation.VERSION }) + const client = new Client({ name: "opencode", version: Installation.VERSION }, { jsonSchemaValidator: tolerantValidator }) return client.connect(transport).then(() => ({ authorizationUrl: "", oauthState })) }, catch: (error) => error,