Use lenient MCP output schema validator (#13521)

This commit is contained in:
cornmander
2025-11-20 16:51:25 -05:00
committed by GitHub
parent 8e531dc029
commit 9937fb2220
8 changed files with 57 additions and 13 deletions
+1 -1
View File
@@ -31,7 +31,7 @@
"@google/gemini-cli-core": "file:../core",
"@google/genai": "1.30.0",
"@iarna/toml": "^2.2.5",
"@modelcontextprotocol/sdk": "^1.15.1",
"@modelcontextprotocol/sdk": "^1.22.0",
"@types/update-notifier": "^6.0.8",
"ansi-regex": "^6.2.2",
"clipboardy": "^5.0.0",
@@ -12,7 +12,7 @@
"@types/node": "^20.11.25"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.11.0",
"@modelcontextprotocol/sdk": "^1.22.0",
"zod": "^3.22.4"
}
}
+1 -1
View File
@@ -26,7 +26,7 @@
"@google/genai": "1.30.0",
"@iarna/toml": "^2.2.5",
"@joshua.litt/get-ripgrep": "^0.0.3",
"@modelcontextprotocol/sdk": "^1.11.0",
"@modelcontextprotocol/sdk": "^1.22.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/exporter-logs-otlp-grpc": "^0.203.0",
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
+48 -4
View File
@@ -5,6 +5,12 @@
*/
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { AjvJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/ajv';
import type {
jsonSchemaValidator,
JsonSchemaType,
JsonSchemaValidator,
} from '@modelcontextprotocol/sdk/validation/types.js';
import type { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
@@ -533,6 +539,38 @@ export async function discoverMcpTools(
}
}
/**
* A tolerant JSON Schema validator for MCP tool output schemas.
*
* Some MCP servers (e.g. thirdparty extensions) return complex schemas that
* include `$defs` / `$ref` chains which can occasionally trip AJV's resolver,
* causing discovery to fail. This wrapper keeps the default AJV validator for
* normal operation but falls back to a noop validator any time schema
* compilation throws, so we can still list and use the tool while emitting a
* debug log.
*/
class LenientJsonSchemaValidator implements jsonSchemaValidator {
private readonly ajvValidator = new AjvJsonSchemaValidator();
getValidator<T>(schema: JsonSchemaType): JsonSchemaValidator<T> {
try {
return this.ajvValidator.getValidator<T>(schema);
} catch (error) {
debugLogger.warn(
`Failed to compile MCP tool output schema (${
(schema as Record<string, unknown>)?.['$id'] ?? '<no $id>'
}): ${error instanceof Error ? error.message : String(error)}. ` +
'Skipping output validation for this tool.',
);
return (input: unknown) => ({
valid: true as const,
data: input as T,
errorMessage: undefined,
});
}
}
}
/** Visible for Testing */
export function populateMcpServerCommand(
mcpServers: Record<string, MCPServerConfig>,
@@ -892,10 +930,16 @@ export async function connectToMcpServer(
debugMode: boolean,
workspaceContext: WorkspaceContext,
): Promise<Client> {
const mcpClient = new Client({
name: 'gemini-cli-mcp-client',
version: '0.0.1',
});
const mcpClient = new Client(
{
name: 'gemini-cli-mcp-client',
version: '0.0.1',
},
{
// Use a tolerant validator so bad output schemas don't block discovery.
jsonSchemaValidator: new LenientJsonSchemaValidator(),
},
);
mcpClient.registerCapabilities({
roots: {
+1 -1
View File
@@ -137,7 +137,7 @@
"vitest": "^3.2.4"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.15.1",
"@modelcontextprotocol/sdk": "^1.22.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"zod": "^3.25.76"