v1.108.292 - The one string that survives tool deferral
The one string that survives tool deferral
The MCP initialize response now carries an instructions string. It did not before: all three transports called create_initialization_options() bare, so the field went out empty and nothing anywhere said so.
The cost is invisible in a normal session and concentrated in a deferred one. A host over its schema budget sends tool NAMES and withholds the JSONSchemas until a ToolSearch-style lookup fetches them. On the default surface that is 91 bare strings, so every tool description we budget and smell-test is absent at exactly the moment an agent has the least to go on. The spec delivers instructions on a separate track from the tool list, so it arrives whole. In a plain MCP client with no hooks and no skill listing, it is the entire steering budget the server gets.
It does two things, in this order: it says to load the working set in ONE lookup, a round trip for the session instead of two calls per use, and it gives a decision rule per tool rather than a feature summary.
Surface-aware. counter names route/menu/order and nothing else, because on that surface the other 91 names are unreachable and pointing an agent at one strands it. 931 characters on full, 599 on counter, against a 1,000 cap. Nothing proves a longer string survives un-truncated.
Fixed: serverInfo named the SDK's version, not ours
Server("jcodemunch-mcp") was constructed with no version=, so the SDK filled the field with its own package version. Every host that displays a server version displayed 1.26.0 while we shipped 1.108.x.
Found by smoke-testing the handshake the change above touches, not by a report. Nothing errors, nothing logs, and the field was wrong on every single initialize, which is the same shape as the empty instructions beside it. One probe found both.
The tests
tests/test_mcp_instructions.py binds the prose to the catalog, because prose is what rots. It fails when the string names a tool the server does not dispatch on that surface, when the select: query and the bullet list disagree about what to load, and, parsing the dispatcher's own AST, when any of the three server.run() sites goes back to a bare create_initialization_options().
A handshake that sends nothing raises no error and serves every request normally, so the only witness is a test. Each guard was verified by reintroducing the defect it names and watching it fail, not only by watching it pass.
Tests: 8161 passed, 17 skipped, 0 failed on 3.10 and 3.13, same total and same skip split. ruff clean.
pip install --upgrade jcodemunch-mcp