-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Open
Labels
P2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featureenhancementRequest for a new feature that's not currently supportedRequest for a new feature that's not currently supportedready for workEnough information for someone to start working onEnough information for someone to start working on
Description
Problem
Models occasionally send tool arguments with incorrect JSON types - for example, numbers serialized as strings ({"thoughtNumber": "1"} instead of {"thoughtNumber": 1}). This causes Zod validation to reject otherwise valid requests.
This issue affects many MCP servers. For example, modelcontextprotocol/servers#2812 added a specific workaround to one part of the sequential-thinking server, but the maintainers noted this feels like the wrong layer to fix it.
The "proper" fix is probably smarter models, or as a consistent layer in the SDKs. This would avoid server authors either having to implement ad-hoc coercion logic OR suffer from these problems.
Proposal
The SDK could provide type coercion for tool arguments, either:
- Built-in coercion - automatically coerce string→number, string→boolean etc. when the schema expects a different primitive type (e.g. similar to https://ajv.js.org/coercion.html)
- Composable helper - export a
coerceToolArgs(schema, args)utility that servers can use - Documentation - guidance on how to handle this with Zod's
.coerceor.preprocess(but doing so safely so that e.g."thing"doesn't becometruewhen coerced to bool)
Example
// Current: fails validation
const args1 = { thoughtNumber: "1" } // model sent number string
const args2 = { thoughtNumber: "one" } // model sent string
const schema = z.object({ thoughtNumber: z.number() })
schema.parse(args1) // ZodError: Expected number, received string
schema.parse(args2) // ZodError: Expected number, received string
// With coercion: works
const coercedSchema = z.object({ thoughtNumber: z.coerce.number() })
coercedSchema.parse(args1) // { thoughtNumber: 1 }
coercedSchema.parse(args2) // ZodError: Expected number, received stringMetadata
Metadata
Assignees
Labels
P2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featureenhancementRequest for a new feature that's not currently supportedRequest for a new feature that's not currently supportedready for workEnough information for someone to start working onEnough information for someone to start working on