From d0d53cc5a7700b03cde018173c51c4577b26526c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Publio=20Estupi=C3=B1=C3=A1n?= Date: Sat, 11 Apr 2026 20:09:09 -0500 Subject: [PATCH] fix(mcp): preserve JSON Schema types and required fields in createSchemaModel The createSchemaModel function was mapping every MCP tool property to z.any(), discarding all type information. This caused two problems: 1. The required array from inputSchema was ignored, making all parameters optional. LLMs would then call tools with empty arguments {}. 2. Type information (string, integer, boolean, array, object, anyOf) was lost. When converted back to JSON Schema for the OpenAI API, property type keys were missing, causing strict mode rejections. Fixes: - Add jsonSchemaToZod() helper that maps JSON Schema types to proper Zod types (z.string, z.number, z.boolean, z.array, z.record) and handles anyOf/nullable patterns. - Read the required array from inputSchema and mark non-required fields as .optional() in the resulting Zod schema. Fixes #5109 Co-Authored-By: Claude Sonnet 4.6 --- packages/components/nodes/tools/MCP/core.ts | 38 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/components/nodes/tools/MCP/core.ts b/packages/components/nodes/tools/MCP/core.ts index 16f9f050f88..488b8bf9cb6 100644 --- a/packages/components/nodes/tools/MCP/core.ts +++ b/packages/components/nodes/tools/MCP/core.ts @@ -177,6 +177,37 @@ export async function MCPTool({ ) } +function jsonSchemaToZod(schema: unknown): ZodTypeAny { + if (!schema || typeof schema !== 'object') return z.any() + const s = schema as Record + + if (Array.isArray(s['anyOf'])) { + const types = (s['anyOf'] as unknown[]).filter((t) => { + if (!t || typeof t !== 'object') return false + return (t as Record)['type'] !== 'null' + }) + if (types.length === 0) return z.any().nullable().optional() + if (types.length === 1) return jsonSchemaToZod(types[0]).nullable().optional() + return z.any().nullable().optional() + } + + switch (s['type']) { + case 'string': + return z.string() + case 'integer': + case 'number': + return z.number() + case 'boolean': + return z.boolean() + case 'array': + return z.array(s['items'] ? jsonSchemaToZod(s['items']) : z.any()) + case 'object': + return z.record(z.any()) + default: + return z.any() + } +} + function createSchemaModel( inputSchema: { type: 'object' @@ -187,8 +218,11 @@ function createSchemaModel( throw new Error('Invalid schema type or missing properties') } - const schemaProperties = Object.entries(inputSchema.properties).reduce((acc, [key]) => { - acc[key] = z.any() + const required = Array.isArray(inputSchema['required']) ? (inputSchema['required'] as string[]) : [] + + const schemaProperties = Object.entries(inputSchema.properties).reduce((acc, [key, value]) => { + const zodType = jsonSchemaToZod(value) + acc[key] = required.includes(key) ? zodType : zodType.optional() return acc }, {} as Record)