diff --git a/.changeset/fix-openai-strict-required.md b/.changeset/fix-openai-strict-required.md new file mode 100644 index 000000000..96a162fb7 --- /dev/null +++ b/.changeset/fix-openai-strict-required.md @@ -0,0 +1,5 @@ +--- +"@modelcontextprotocol/sdk": patch +--- + +fix: include required field in JSON Schema when Zod .optional() is absent diff --git a/packages/core/src/util/schema.ts b/packages/core/src/util/schema.ts index adecee361..156aa76f1 100644 --- a/packages/core/src/util/schema.ts +++ b/packages/core/src/util/schema.ts @@ -26,7 +26,22 @@ export type SchemaOutput = z.output; * Converts a Zod schema to JSON Schema. */ export function schemaToJson(schema: AnySchema, options?: { io?: 'input' | 'output' }): Record { - return z.toJSONSchema(schema, options) as Record; + const jsonSchema = z.toJSONSchema(schema, options) as Record; + + // OpenAI strict JSON schema mode requires the `required` field to always + // be present on object schemas, even when empty. Zod's toJSONSchema omits + // it for empty objects (e.g. z.object({}).strict()), causing tool + // registration to fail with OpenAI's strict mode. + // See: https://github.com/modelcontextprotocol/typescript-sdk/issues/1659 + if ( + jsonSchema.type === 'object' && + jsonSchema.properties !== undefined && + !Array.isArray(jsonSchema.required) + ) { + jsonSchema.required = []; + } + + return jsonSchema; } /**