Skip to content

fix(mcp): serve tools on a fresh index and tolerate pre-handshake probes - #117

Closed
ViniciosLugli wants to merge 3 commits into
bartolli:mainfrom
ViniciosLugli:fix/tolerate-pre-handshake-probe
Closed

fix(mcp): serve tools on a fresh index and tolerate pre-handshake probes#117
ViniciosLugli wants to merge 3 commits into
bartolli:mainfrom
ViniciosLugli:fix/tolerate-pre-handshake-probe

Conversation

@ViniciosLugli

@ViniciosLugli ViniciosLugli commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Two independent defects, both ending in a server that lists zero tools.

1. A metadata-less index directory is treated as stale

persistence.exists() is satisfied by a bare tantivy/meta.json, which serve writes on startup before anything is indexed. That directory has no index.meta, so there are no stored emission semantics to conflict with, yet the gate refuses it. The server rejects the skeleton it just created itself.

$ cd /tmp && mkdir fresh && cd fresh && git init -q .
$ codanna serve   # 9 tools
$ codanna serve   # 0 tools, "INDEX STALE - ALL TOOLS DISABLED"

Any directory that resolves to a shared ~/.codanna is affected permanently, since $HOME is rarely indexed.

Fix: gate only when index.meta exists. An index predating emission stamping still has index.meta without the field, so it keeps gating and healing.

2. A pre-handshake client probe kills the server

rmcp requires initialize as the first stdio message and treats anything else as fatal. Antigravity CLI opens with server/discover, so serve exits before it can serve anyone:

Failed to start MCP server: expect initialized request, but received:
Some(Request(JsonRpcRequest { id: Number(1), request: CustomRequest(
CustomRequest { method: "server/discover", ... }) }))

Fix: filter stdin until initialize. Unknown pre-handshake requests get JSON-RPC -32601, pre-handshake notifications are dropped, and after initialize every byte is forwarded untouched.

Tests

cargo test: 1581 passed, 0 failed. The emission-gate tests that guard existing users are unchanged and still pass: read_path_refuses_stale_index_with_heal_command, index_heals_unstamped_index_with_full_rebuild, force_bypasses_gate_and_restamps, serve_stale_stdio_completes_degraded_handshake.

Verified end-to-end against Claude Code, Codex, OpenCode and Antigravity.

rmcp requires initialize as the first stdio message and treats anything
else as fatal, killing the server before it can serve. Antigravity opens
with a non-standard server/discover request, so codanna never starts.

Filter stdin until initialize arrives: answer unknown pre-handshake
requests with JSON-RPC -32601 and drop pre-handshake notifications.
After initialize every byte is forwarded untouched.
persistence.exists() is satisfied by a bare tantivy/meta.json, which serve
writes on startup before anything is indexed. That directory has no
index.meta, so there are no stored emission semantics to conflict with,
yet the gate refused it: a server rejected the skeleton it had just
created, disabling every tool until the workspace was indexed by hand.

Gate only when index.meta exists. An index predating emission stamping
still has index.meta without the field, so it keeps gating and healing.
@bartolli

bartolli commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Thanks for the report and fix.

I migrated to rmcp 3.x, which completely replaces the code your PR modifies. If we merged this, it would be overwritten immediately.
I applied your fixes directly to the new codebase:

  • Probe fix: Merged to main (922ab97).
  • Fresh-index fix: Queued as a separate change.

Both updates will ship in the next release and include a Co-authored-by tag to give you full credit. I am closing this PR due to the migration. Thanks again!

@bartolli bartolli closed this Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants