Skip to content

fix(core): ensure standardSchemaToJsonSchema emits type:object#1796

Open
felixweinberger wants to merge 1 commit intomainfrom
fweinberger/fix-schema-object-type
Open

fix(core): ensure standardSchemaToJsonSchema emits type:object#1796
felixweinberger wants to merge 1 commit intomainfrom
fweinberger/fix-schema-object-type

Conversation

@felixweinberger
Copy link
Copy Markdown
Contributor

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-level type, causing clients to reject those tools.

#1675 patched this at the call site in mcp.ts, but that only covered 2 of 3 consumers. promptArgumentsFromStandardSchema at standardSchema.ts:185 reads .properties directly 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 existing type: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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

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.
@felixweinberger felixweinberger requested a review from a team as a code owner March 27, 2026 19:04
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 27, 2026

🦋 Changeset detected

Latest commit: 65fc7fa

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@modelcontextprotocol/core Patch

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 27, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@1796

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@1796

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@1796

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@1796

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@1796

commit: 65fc7fa

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

registerTool() silently drops inputSchema for z.discriminatedUnion() — normalizeObjectSchema only passes z.object()

1 participant