intercept and sanitize gemini schema

This commit is contained in:
Shawn Morreau
2025-12-03 14:28:08 -05:00
parent a73ddd378d
commit 9b4bdae8bd
4 changed files with 92 additions and 3 deletions
+7 -1
View File
@@ -173,8 +173,9 @@ export const gemini = agent({
args: [cliPath, "--yolo", "--output-format=stream-json", "-p", sessionPrompt],
env: createAgentEnv({
GEMINI_API_KEY: apiKey,
GEMINI_CLI_DISABLE_SCHEMA_VALIDATION:
process.env.GEMINI_CLI_DISABLE_SCHEMA_VALIDATION || "1",
}),
timeout: 600000, // 10 minutes
onStdout: async (chunk) => {
const text = chunk.toString();
finalOutput += text;
@@ -257,6 +258,11 @@ function configureGeminiMcpServers({ mcpServers, cliPath }: ConfigureMcpServersP
const addResult = spawnSync("node", [cliPath, ...addArgs], {
stdio: "pipe",
encoding: "utf-8",
env: {
...process.env,
GEMINI_CLI_DISABLE_SCHEMA_VALIDATION:
process.env.GEMINI_CLI_DISABLE_SCHEMA_VALIDATION || "1",
},
});
if (addResult.status !== 0) {
+1 -1
View File
@@ -1 +1 @@
summarize https://github.com/pullfrogai/scratch/issues/56, its status, and pertinent discussion on the issue
Use the debug_shell_command MCP tool to run git status and show me the output.
+1
View File
@@ -279,6 +279,7 @@ async function startMcpServer(ctx: MainContext): Promise<void> {
process.env.GITHUB_REPOSITORY = githubRepository;
process.env.PULLFROG_MODES = JSON.stringify(allModes);
process.env.PULLFROG_PAYLOAD = JSON.stringify(ctx.payload);
process.env.PULLFROG_AGENT = ctx.agentName;
// GITHUB_RUN_ID is already set in GitHub Actions, no need to set it here
+83 -1
View File
@@ -44,9 +44,91 @@ export interface McpContext extends RepoContext {
export const tool = <const params>(toolDef: Tool<any, StandardSchemaV1<params>>) => toolDef;
/**
* Sanitize JSON schema to remove problematic fields that Gemini CLI can't handle
* - Removes $schema field (causes "no schema with key or ref" errors)
* - Converts $defs to definitions (draft-07 compatibility)
* - Removes any draft-2020-12 specific features
*/
function sanitizeSchema(schema: any): any {
if (!schema || typeof schema !== "object") {
return schema;
}
if (Array.isArray(schema)) {
return schema.map(sanitizeSchema);
}
const sanitized: any = {};
for (const [key, value] of Object.entries(schema)) {
// skip $schema field entirely
if (key === "$schema") {
continue;
}
// convert $defs to definitions for draft-07 compatibility
if (key === "$defs") {
sanitized.definitions = sanitizeSchema(value);
continue;
}
// recursively sanitize nested objects
sanitized[key] = sanitizeSchema(value);
}
return sanitized;
}
/**
* Wrap a StandardSchemaV1 to intercept toJsonSchema() calls and sanitize the output
*/
function wrapSchema(schema: StandardSchemaV1<any>): StandardSchemaV1<any> {
const originalToJsonSchema = (schema as any).toJsonSchema?.bind(schema);
if (!originalToJsonSchema) {
return schema;
}
// create a proxy that intercepts toJsonSchema calls
return new Proxy(schema, {
get(target, prop) {
if (prop === "toJsonSchema") {
return () => {
const originalSchema = originalToJsonSchema();
return sanitizeSchema(originalSchema);
};
}
return (target as any)[prop];
},
}) as StandardSchemaV1<any>;
}
/**
* Transform tool to sanitize its parameter schema for Gemini CLI compatibility
*/
function sanitizeTool<T extends Tool<any, any>>(tool: T): T {
if (!tool.parameters) {
return tool;
}
// wrap the schema object to intercept toJsonSchema() calls
const wrappedSchema = wrapSchema(tool.parameters);
// create a new tool with wrapped schema
return {
...tool,
parameters: wrappedSchema,
} as T;
}
export const addTools = (server: FastMCP, tools: Tool<any, any>[]) => {
// only sanitize schemas for gemini agent (it has issues with draft-2020-12 schemas)
const shouldSanitize = process.env.PULLFROG_AGENT === "gemini";
for (const tool of tools) {
server.addTool(tool);
const processedTool = shouldSanitize ? sanitizeTool(tool) : tool;
server.addTool(processedTool);
}
return server;
};