fix(schema): strip contentEncoding from MCP tool schemas for Gemini compatibility
The existing normalizeToolArgSchemas only applies to omo plugin tools (via tool-registry.ts), but MCP server tool schemas bypass this sanitization entirely. MCP schemas with contentEncoding/contentMediaType cause Gemini 400 errors. Add sanitizeJsonSchema() to strip unsupported keywords from MCP tool inputSchema before serialization in formatMcpCapabilities. Fixes #2200 Supersedes #2666
This commit is contained in:
@@ -40,3 +40,37 @@ export function normalizeToolArgSchemas<TDefinition extends Pick<ToolDefinition,
|
|||||||
|
|
||||||
return toolDefinition
|
return toolDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Schema keywords unsupported by Gemini — strip them from MCP tool schemas
|
||||||
|
const UNSUPPORTED_SCHEMA_KEYWORDS = new Set(["contentEncoding", "contentMediaType"])
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sanitizeJsonSchema(value: unknown, depth = 0, isPropertyName = false): unknown {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((item) => sanitizeJsonSchema(item, depth + 1, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitized: Record<string, unknown> = {}
|
||||||
|
|
||||||
|
for (const [key, nestedValue] of Object.entries(value)) {
|
||||||
|
if (!isPropertyName && UNSUPPORTED_SCHEMA_KEYWORDS.has(key)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth === 0 && key === "$schema") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const childIsPropertyName = key === "properties" && !isPropertyName
|
||||||
|
sanitized[key] = sanitizeJsonSchema(nestedValue, depth + 1, childIsPropertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sanitized
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { getAllSkills, extractSkillTemplate, clearSkillCache } from "../../featu
|
|||||||
import { injectGitMasterConfig } from "../../features/opencode-skill-loader/skill-content"
|
import { injectGitMasterConfig } from "../../features/opencode-skill-loader/skill-content"
|
||||||
import type { SkillMcpManager, SkillMcpClientInfo, SkillMcpServerContext } from "../../features/skill-mcp-manager"
|
import type { SkillMcpManager, SkillMcpClientInfo, SkillMcpServerContext } from "../../features/skill-mcp-manager"
|
||||||
import type { Tool, Resource, Prompt } from "@modelcontextprotocol/sdk/types.js"
|
import type { Tool, Resource, Prompt } from "@modelcontextprotocol/sdk/types.js"
|
||||||
|
import { sanitizeJsonSchema } from "../../plugin/normalize-tool-arg-schemas"
|
||||||
import { discoverCommandsSync } from "../slashcommand/command-discovery"
|
import { discoverCommandsSync } from "../slashcommand/command-discovery"
|
||||||
import type { CommandInfo } from "../slashcommand/types"
|
import type { CommandInfo } from "../slashcommand/types"
|
||||||
import { formatLoadedCommand } from "../slashcommand/command-output-formatter"
|
import { formatLoadedCommand } from "../slashcommand/command-output-formatter"
|
||||||
@@ -155,7 +156,7 @@ async function formatMcpCapabilities(
|
|||||||
sections.push("")
|
sections.push("")
|
||||||
sections.push("**inputSchema:**")
|
sections.push("**inputSchema:**")
|
||||||
sections.push("```json")
|
sections.push("```json")
|
||||||
sections.push(JSON.stringify(t.inputSchema, null, 2))
|
sections.push(JSON.stringify(sanitizeJsonSchema(t.inputSchema), null, 2))
|
||||||
sections.push("```")
|
sections.push("```")
|
||||||
sections.push("")
|
sections.push("")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user