fix(core): ensure standardSchemaToJsonSchema emits type:object#1796
fix(core): ensure standardSchemaToJsonSchema emits type:object#1796felixweinberger wants to merge 1 commit intomainfrom
Conversation
MCP requires tool inputSchema/outputSchema and prompt argument schemas
to have `type: "object"` at the root. Zod's discriminated unions emit
`{oneOf: [...]}` without a top-level type, which caused clients to
reject those tools.
The fix is applied in standardSchemaToJsonSchema (the SDK-owned
conversion utility, not the vendored Standard Schema interfaces) so it
covers all three call sites: tool inputSchema, tool outputSchema, and
prompt argument extraction. Previously #1675 patched only two of these
at the call site level.
Also throws a clear error when given an explicitly non-object schema
(e.g. z.string()) rather than silently producing an invalid schema.
Fixes #1643. Supersedes #1675.
🦋 Changeset detectedLatest commit: 65fc7fa The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
There was a problem hiding this comment.
LGTM, clean bug fix centralizing type:object enforcement for MCP schema compliance.
Extended reasoning...
Overview
This PR modifies standardSchemaToJsonSchema in packages/core/src/util/standardSchema.ts to ensure the returned JSON Schema always includes type: "object" at the root, as required by the MCP spec. It also adds a guard that throws for explicitly non-object schemas (e.g. z.string()). A new test file and changeset are included.
Security risks
None. The change is purely about schema shape validation at registration time. It makes the system stricter (rejecting invalid schemas earlier) rather than more permissive.
Level of scrutiny
Low scrutiny is appropriate. This is a small, focused bug fix (6 lines of logic + JSDoc) that centralizes existing behavior. The spread pattern { type: "object", ...result } correctly lets Zod's own type: "object" win when present, while defaulting it in for discriminated unions that omit it. The error path for non-object schemas is a clear fail-fast improvement.
Other factors
- All three call sites (
mcp.ts:145,mcp.ts:153,promptArgumentsFromStandardSchema) are covered by this single fix point. - Unit tests cover the four key cases: plain object, discriminated union, non-object rejection, and no double-wrap.
- No outstanding reviewer comments. No CODEOWNERS concerns.
- The "breaking change" (rejecting
z.string()as a tool schema) was never valid per the MCP spec, so this is a correctness improvement.
Supersedes #1675. Fixes #1643.
Motivation and Context
MCP requires
type: "object"at the root of tool inputSchema/outputSchema and prompt argument schemas. Zod's discriminated unions emit{oneOf: [...]}without a top-leveltype, causing clients to reject those tools.#1675 patched this at the call site in
mcp.ts, but that only covered 2 of 3 consumers.promptArgumentsFromStandardSchemaatstandardSchema.ts:185reads.propertiesdirectly and has the same latent bug.This puts the fix in
standardSchemaToJsonSchema(the SDK-owned conversion utility, not the vendored Standard Schema interfaces) so all three call sites are covered. Also throws a clear error for explicitly non-object schemas (z.string(),z.array()) rather than silently producing invalid output.How Has This Been Tested?
Unit tests covering: plain
z.object, discriminated union, non-object schemas (throw), and no double-wrap of existingtype:object.Breaking Changes
Passing
z.string()or other non-object schemas as tool/prompt schemas now throws at registration instead of producing an invalid schema that fails downstream. This was never valid per the MCP spec.Types of changes
Checklist