fix(sequentialthinking): keep nextThoughtNeeded in inputSchema required - #4701
Open
Golchic wants to merge 1 commit into
Open
fix(sequentialthinking): keep nextThoughtNeeded in inputSchema required#4701Golchic wants to merge 1 commit into
required#4701Golchic wants to merge 1 commit into
Conversation
The `coercedBoolean` helper used `z.preprocess(fn, z.boolean())` to accept string "true"/"false" from clients. With Zod v4, `z.preprocess` widens the input type to `unknown`, so the SDK's JSON Schema generation (io: "input") drops the field from `required` even though it is not `.optional()`. Clients that build arguments from the advertised schema then omit `nextThoughtNeeded` and get `-32602 Input validation error`, which contradicts the schema. Regression from modelcontextprotocol#3533. Reimplement `coercedBoolean` as a union of `z.boolean()` and a string transform. The union keeps a concrete input type, so required fields stay in `required`, while still coercing "true"/"false" (case-insensitively) and now rejecting other strings with a clearer message. - Move `coercedBoolean` into lib.ts and export it so it can be unit-tested - Declare zod as an explicit dependency (already imported directly; matches the everything server) - Add schema.test.ts: round-trips a probe tool through the SDK and asserts a required coercedBoolean field stays in `required` while an optional one does not, plus value-coercion cases Fixes modelcontextprotocol#4651
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
nextThoughtNeededis declared as a non-optional field on thesequentialthinkingtool, but it is missing from the generated JSON Schema's
requiredarray. Clientsthat build call arguments from the advertised schema omit it and get
-32602 Input validation error, which contradicts the schema.Root cause: the
coercedBooleanhelper wasz.preprocess(fn, z.boolean()). UnderZod v4,
z.preprocesswidens the input type tounknown, so the SDK's JSONSchema generation (
io: "input") treats the field as optional and drops it fromrequired— even though it is not.optional(). (Regression from #3533.)Fix: reimplement
coercedBooleanasz.union([z.boolean(), z.string().transform(...)]).The union keeps a concrete input type, so non-optional fields stay in
required,while still coercing
"true"/"false"(case-insensitively) and now rejecting otherstrings with a clearer message.
isRevision/needsMoreThoughtsremain optional.The only schema shape change: these boolean fields now serialize as
{ "anyOf": [{ "type": "boolean" }, { "type": "string" }] }, which accuratelyreflects the accepted input.
Server Details
sequentialthinking)Motivation and Context
Fixes #4651. Without this, schema-driven MCP clients cannot call the tool without
manually knowing
nextThoughtNeededis required despite the schema saying otherwise.How Has This Been Tested?
src/sequentialthinking/__tests__/schema.test.ts:McpServer+ClientoverInMemoryTransport) and asserts a requiredcoercedBooleanfield stays ininputSchema.required, while an.optional()one does not.true/false,"true"/"false"/"TRUE"/"False", andrejection of
"yes","",1,null.npm run buildandnpm testpass insrc/sequentialthinking(19 tests).zod/v4-minitoJSONSchema,io: "input"), not a hand-rolled converter.Breaking Changes
None for tool callers — the field was always meant to be required and the runtime
validation already required it. MCP client configs need no changes.
Types of changes
Checklist
already lists
nextThoughtNeededas a non-optional(boolean)inputautomated tests
Additional context
coercedBooleanmoved fromindex.tstolib.ts(exported) so it can be unittested;
index.tsnow imports it.zodis added as an explicit dependency of thepackage since it is imported directly (matching the
everythingserver).