Skip to content

fix(server): auto-unwrap z.object() schemas in server.tool()#1603

Closed
giulio-leone wants to merge 2 commits intomodelcontextprotocol:mainfrom
giulio-leone:fix/zod-object-schema-unwrap
Closed

fix(server): auto-unwrap z.object() schemas in server.tool()#1603
giulio-leone wants to merge 2 commits intomodelcontextprotocol:mainfrom
giulio-leone:fix/zod-object-schema-unwrap

Conversation

@giulio-leone
Copy link
Contributor

Problem

When z.object({...}) is passed as inputSchema to server.tool(), the SDK silently interprets it as ToolAnnotations instead of an input schema. The tool registers with empty parameters and arguments are stripped without any error.

This is because isZodRawShapeCompat() correctly identifies ZodObject instances as not raw shapes (they have internal _def/_zod properties, not field schemas as values). But the fallback branch at line 1026 unconditionally treats any remaining object as ToolAnnotations.

Root Cause

if (isZodRawShapeCompat(firstArg)) {
    inputSchema = rest.shift();       // z.object({...}) fails this check
} else if (typeof firstArg === 'object') {
    annotations = rest.shift();       // ← z.object({...}) silently lands here
}

Fix

Added extractZodObjectShape() that detects ZodObject schemas (both Zod v3 and v4) by checking for a .shape property whose values are Zod type instances. When detected, the raw shape is extracted and used as inputSchema.

Both forms now work identically:

// Raw shape (always worked)
server.tool('test', { message: z.string() }, handler);

// ZodObject (now also works)
server.tool('test', z.object({ message: z.string() }), handler);

Tests

  • Added test for z.object() auto-unwrap with schema verification and argument passing
  • Added test for z.object() combined with annotations
  • All 1551 tests pass (2 consecutive clean runs)

Fixes #1291

@changeset-bot
Copy link

changeset-bot bot commented Feb 28, 2026

🦋 Changeset detected

Latest commit: 758565a

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

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

pkg-pr-new bot commented Feb 28, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@modelcontextprotocol/sdk@1603

commit: 1f12bd3

@giulio-leone
Copy link
Contributor Author

Friendly ping — CI is green and this is ready for review. Happy to address any feedback. Thanks!

@giulio-leone
Copy link
Contributor Author

All CI checks pass. Ready for review.

g97iulio1609 added 2 commits March 6, 2026 17:20
When z.object({...}) is passed as inputSchema, the SDK would silently
interpret it as ToolAnnotations instead of an input schema, resulting in
the tool being registered with empty parameters. Arguments passed to the
tool would be silently stripped.

Added extractZodObjectShape() that detects ZodObject schemas (both Zod
v3 and v4) via their .shape property and extracts the raw shape for
proper registration.

Fixes modelcontextprotocol#1291
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.

Bug: server.tool() silently ignores ZodObject schemas and strips all arguments

1 participant