[BUG] MCP tool integer parameters serialized as JSON strings (sequentialthinking thoughtNumber rejected)
Summary
Claude Code appears to serialize integer-typed MCP tool parameters as JSON strings, breaking servers that strict-validate the type with typeof !== 'number'. The most reproducible failure is mcp__MCP_DOCKER__sequentialthinking, where every call rejects thoughtNumber as not-a-number even when the model emits it as an integer.
Reproduction
Call mcp__MCP_DOCKER__sequentialthinking with parameters that match its schema (thoughtNumber: integer ≥ 1, totalThoughts: integer ≥ 1, nextThoughtNeeded: boolean, thought: string):
mcp__MCP_DOCKER__sequentialthinking(
thought: "first thought",
thoughtNumber: 1,
totalThoughts: 1,
nextThoughtNeeded: false
)
Expected: success — server returns the next-thought response.
Actual:
{"error": "Invalid thoughtNumber: must be a number", "status": "failed"}
Reproduces 3-for-3 in the same session, varying thought content. Confirmed against both npm @modelcontextprotocol/server-sequential-thinking@2025.12.18 (latest published) and the Docker bundle.
Evidence the parameters are stringified by the client
modelcontextprotocol/servers#2905 (now closed as a duplicate of #2792) contains a verbatim repro from another user where Claude Code's tool call line shows the integers as strings:
next_thought_needed: "true", thought_number: "1", total_thoughts: "10", is_revision: "false"
That user's diagnosis matches ours: "It attempts to use strings like \"1\" instead of integer value 1". The pattern is consistent with the symptom we observe.
Asymmetry / partial coercion
In our reproductions, nextThoughtNeeded (boolean) passed through fine while integers failed. The booleans-as-strings case in #2905 (is_revision: "false") suggests the same root cause hits booleans too on some code paths, but on our path booleans escape the defect. Worth narrowing whether the serialization differs by parameter position, by depth, or by model output formatting.
Server-side context
The strict validator on the server side at modelcontextprotocol/servers/src/sequentialthinking/index.ts (commit ec91421) is:
if (!data.thoughtNumber || typeof data.thoughtNumber !== 'number') {
throw new Error('Invalid thoughtNumber: must be a number');
}
This was already softened to z.coerce.number().int().min(1) in PR modelcontextprotocol/servers#3533 (merged 2026-03-15, commit 1cdf806d21c3afdeb203c04705e8892d0f9d832f), but the fix has not been published — npm latest is still 2025.12.18. Tracked upstream in modelcontextprotocol/servers#3856. Once that release ships, the server will absorb the stringified params and this bug will become invisible — but the client-side stringification would remain a defect for any future MCP server that strict-validates types.
Impact
Blocks the canonical sequentialthinking branching/revision/synthesis pattern in our framework's skills (/audit-framework, /codify-issue, any new skill that adopts it). Workaround is inline-RCA in the main thread, which loses the auditable thought-by-thought reasoning trace.
Cross-references
modelcontextprotocol/servers#2905(closed, dup of #2792, contains the verbatim stringified repro)modelcontextprotocol/servers#2792(open, parent issue)modelcontextprotocol/servers#3856(open, awaiting npm release of the coercion fix)modelcontextprotocol/servers#3533(merged, the coercion fix)
Environment
- Claude Code on
darwin 25.5.0 - Model: Opus 4.7 (1M context)
- MCP server:
@modelcontextprotocol/server-sequential-thinking@2025.12.18(Docker bundle)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗