mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 21:03:05 -07:00
Use lenient MCP output schema validator (#13521)
This commit is contained in:
@@ -108,7 +108,7 @@ jobs:
|
||||
- name: 'Link Checker'
|
||||
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
|
||||
with:
|
||||
args: '--verbose ./**/*.md'
|
||||
args: '--verbose --accept 200,503 ./**/*.md'
|
||||
fail: true
|
||||
test_linux:
|
||||
name: 'Test (Linux)'
|
||||
|
||||
@@ -20,4 +20,4 @@ jobs:
|
||||
id: 'lychee'
|
||||
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
|
||||
with:
|
||||
args: '--verbose --no-progress ./**/*.md'
|
||||
args: '--verbose --no-progress --accept 200,503 ./**/*.md'
|
||||
|
||||
Generated
+3
-3
@@ -17915,7 +17915,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",
|
||||
@@ -18019,7 +18019,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",
|
||||
@@ -18170,7 +18170,7 @@
|
||||
"version": "0.18.0-nightly.20251120.2231497b1",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.15.1",
|
||||
"@modelcontextprotocol/sdk": "^1.22.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.1.0",
|
||||
"zod": "^3.25.76"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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. third‑party 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 no‑op 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: {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user