Skip to content

Conversation

@ksinder
Copy link
Contributor

@ksinder ksinder commented Jul 28, 2025

Motivation and Context

See #588. This PR adds a backwards-compatible change to registerTool on McpServer to allow any Zod schema extending ZodType<object> instead of requiring inputSchema and outputSchema to conform to ZodRawShape (:= Record<string, ZodType<any>>).

The underlying schema validation and zod-to-json-schema dependency already support this, so adding this plumbing expands SDK expressiveness and flexibility. Specifically, this unblocks MCP servers' ability to define tool schemas that use z.union(...) and z.intersection(...). Historically, some creators have had to wrap existing schemas in some wrapper object that has fixed keys, e.g. {bodyParams: z.union(...)} to work around this limitation.

How Has This Been Tested?

Added automated tests in mcp.test.ts and confirmed they pass locally with npm run test.

Also manually tried changing the test locally to use inputSchemas for types that aren't objects at the top level -- e.g. z.number() and z.union([z.number(), z.object({})]), and confirmed typechecking fails (which is good, since I'm guessing we still want guardrails to push toward objects for params and responses. If this ever changes in the future it's easy enough to change all ZodType<object> to ZodTypeAny!)

image

Breaking Changes

Should be backwards-compatible in the SDK with existing server code. This is purely additive since we can easily dynamically distinguish between existing ZodRawShapes being passed in vs. direct ZodType<object> and branch accordingly. ZodType<object> is added as a union type variant for server.registerTool, and isn't mandatory.

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

Additional context

N/A

@jjn1056
Copy link

jjn1056 commented Aug 21, 2025

+1 on merging this or at least getting feedback from the team. I ran into this problem myself just today. Some of the error conditions that the API I'm wrapping for LLM consumption are complex and the better I can hint the LLM in the response, the better we'll be able to properly recover.

@akshaylingamaneni
Copy link

guys, can we get this merged

@davidgilbertson
Copy link

It's disappointing that @ksinder went to the trouble of creating a PR for this and it's been ignored for many months.

@ksinder ksinder requested a review from a team as a code owner November 5, 2025 19:10
@ksinder ksinder force-pushed the main branch 2 times, most recently from 47678e4 to d839335 Compare November 5, 2025 19:21
Copy link
Member

@pcarleton pcarleton left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@pcarleton pcarleton merged commit 806d7cf into modelcontextprotocol:main Nov 7, 2025
2 checks passed
@demian85
Copy link

Can I get help with this please? #1148

@RahulLanjewar93
Copy link

RahulLanjewar93 commented Nov 27, 2025

This change was released in @modelcontextprotocol/[email protected]
I am using @modelcontextprotocol/[email protected] but still doesn't work for me
zod is a direct dependency in my project [email protected]

const schema = z.strictObject({
  key1: z.literal('value1'),
  key2: z.literal('value2'),
});

const mcpTool: Tool = {
  name: 'Test tool',
  description: 'Test tool',
  title: 'Test tool',
  // just a dummy input and output schema
  inputSchema: schema,
  outputSchema: schema,
};

InputSchema and outputSchema have the following error. TLDR: 'type' is missing

Property 'type' is missing in type 'ZodObject<{ key1: ZodLiteral<"value1">; key2: ZodLiteral<"value2">; }, "strict", ZodTypeAny, { key1: "value1"; key2: "value2"; }, { key1: "value1"; key2: "value2"; }>' but required in type '{ [x: string]: unknown; type: "object"; properties?: { [x: string]: object; } | undefined; required?: string[] | undefined; }'.

I think this pr is supposed to support the schema mentioned above directly. Please correct me if I am wrong
The code below works fine, without any errors. But i doubt if it is the correct version

const mcpTool: Tool = {
  name: 'Test tool',
  description: 'Test tool',
  title: 'Test tool',
  // just a dummy input and output schema
  inputSchema: { ...schema, type: 'object' },
  outputSchema: { ...schema, type: 'object' },
};

@RahulLanjewar93
Copy link

This change was released in @modelcontextprotocol/[email protected] I am using @modelcontextprotocol/[email protected] but still doesn't work for me zod is a direct dependency in my project [email protected]

const schema = z.strictObject({
  key1: z.literal('value1'),
  key2: z.literal('value2'),
});

const mcpTool: Tool = {
  name: 'Test tool',
  description: 'Test tool',
  title: 'Test tool',
  // just a dummy input and output schema
  inputSchema: schema,
  outputSchema: schema,
};

InputSchema and outputSchema have the following error. TLDR: 'type' is missing

Property 'type' is missing in type 'ZodObject<{ key1: ZodLiteral<"value1">; key2: ZodLiteral<"value2">; }, "strict", ZodTypeAny, { key1: "value1"; key2: "value2"; }, { key1: "value1"; key2: "value2"; }>' but required in type '{ [x: string]: unknown; type: "object"; properties?: { [x: string]: object; } | undefined; required?: string[] | undefined; }'.

I think this pr is supposed to support the schema mentioned above directly. Please correct me if I am wrong The code below works fine, without any errors. But i doubt if it is the correct version

const mcpTool: Tool = {
  name: 'Test tool',
  description: 'Test tool',
  title: 'Test tool',
  // just a dummy input and output schema
  inputSchema: { ...schema, type: 'object' },
  outputSchema: { ...schema, type: 'object' },
};

The issue was due to a difference in exported types from the sdk. I was using low-level server, and the type I used for creating the tool was Tool, where it should have been RegisteredTool don't know why there are two of them. Should be documented somewhere, or need to create a bug if these two are supposed to be used interchangeably

// GOOD 
import { RegisteredTool, RegisteredResource } from '@modelcontextprotocol/sdk/server/mcp';
// BAD
import {  Tool, Resource } from '@modelcontextprotocol/sdk/types';

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.

7 participants