sequential-thinking MCP accepts phantom revisions and branches pointing to nonexistent thoughts
Reproduced behavior: phantom graph references in @modelcontextprotocol/server-sequential-thinking v0.2.0
Surface: @modelcontextprotocol/server-sequential-thinking v0.2.0 via npx -y (stdio)
The bug
isRevision: true, revisesThought: N and branchFromThought: N, branchId: "..." accept any positive integer for N, even when that thought doesn't exist in the chain. No error, no warning — the phantom reference is silently persisted. The branch even appears in subsequent branches arrays.
Repro trace
Build a 4-thought chain (thoughts 1–8 via numbering jumps), then:
→ {"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"sequentialthinking","arguments":{"thought":"Revising thought 99","nextThoughtNeeded":true,"thoughtNumber":9,"totalThoughts":10,"isRevision":true,"revisesThought":99}}}
← {"result":{"structuredContent":{"thoughtNumber":9,"totalThoughts":10,"nextThoughtNeeded":true,"branches":[],"thoughtHistoryLength":5}}}Revision of thought 99 accepted — only 5 thoughts exist in history.
→ {"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"sequentialthinking","arguments":{"thought":"Branch from 99","nextThoughtNeeded":true,"thoughtNumber":10,"totalThoughts":10,"branchFromThought":99,"branchId":"phantom-branch"}}}
← {"result":{"structuredContent":{"thoughtNumber":10,"totalThoughts":10,"nextThoughtNeeded":true,"branches":["phantom-branch"],"thoughtHistoryLength":6}}}Branch from thought 99 accepted and registered — phantom-branch now appears in all subsequent responses, pointing to an origin that never existed.
Additional undocumented behaviors found in same run
- Silent `totalThoughts` auto-inflation: sending
thoughtNumber: 7, totalThoughts: 3returnstotalThoughts: 7. The server silently expands the budget to match the current thought number. Persists on subsequent calls —thoughtNumber: 8, totalThoughts: 3→ returnstotalThoughts: 8.
- Empty thoughts accepted:
thought: ""is stored in history and counted inthoughtHistoryLength. No minimum-length validation.
What the server DOES validate correctly
thoughtNumber: 0or negative → rejected ("Too small: expected number to be >=1")totalThoughts: 0→ rejected- Float
thoughtNumber: 1.5→ rejected ("expected int, received number") - Wrong types (number for thought, string for thoughtNumber) → all rejected
- Missing required fields → all rejected
Impact for agents
An agent building a multi-branch reasoning chain can silently create graph corruption: branches referencing nonexistent origins, revisions of phantom thoughts. Any downstream agent or tool consuming the thought graph (for verification, replay, or visualization) would see a structurally incoherent tree. The validation gap is consistent — integer range and type are checked, but referential integrity against the actual history is not.