Skip to content

fix(mcp): complete the initialize handshake instead of exiting on stdio (#211) - #217

Closed
Frankie-Xu wants to merge 2 commits into
trailhq:mainfrom
Frankie-Xu:fix/211-mcp-initialize-handshake
Closed

Frankie-Xu wants to merge 2 commits into
trailhq:mainfrom
Frankie-Xu:fix/211-mcp-initialize-handshake

Conversation

@Frankie-Xu

@Frankie-Xu Frankie-Xu commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #211
Closes #212

Follow-up (still not this PR)

Pagination, resources, prompts, result-envelope fields (resultType, cache hints on list results), and subscriptions/listen were not verified in those issues because the handshake never completed. They are out of scope here.

After this lands they may start running in @hasmcp/mcp-spec-test and fail for real capability gaps rather than “server exited”. Local run of 2026-07-28 against this branch already shows that: handshake / discover / SDK interop pass; 4 remaining failures are envelope fields on tools/list and tools/call (resultType, cacheScope, ttlMs, _meta serverInfo).

Test plan

  • npm run build && npm test — 923/923
  • Fake-stdin unit tests: version-less initialize, unsupported version refused, tools/list, server/discover without a session
  • Spawned graft mcp <tmpdir>: initialize → initialized → tools/list → tools/call; process still running on a quiet stdin pipe
  • @hasmcp/mcp-spec-test@0.1.1 -c "node dist/cli.js mcp <abs>" --spec-version 2025-11-25: 14 passed, 0 failed (handshake, version negotiation, official SDK initialize + tools/list)
  • same suite --spec-version 2026-07-28: handshake, server/discover, and official SDK interop pass; 4 envelope failures left as follow-up

Made with Cursor

@github-actions

github-actions Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

🌱 graft blast radius

3 areas changed → 3 areas can be affected. 9 dependent symbols, depth 2.
Tests: Source Parsing has tests the diff did not touch; 1 area updated its tests.
Tag: @anirudhkumar-nanonets — 3 of 6 areas · @shhdwi — Source Parsing, Graph Engine · @afeddersen — MCP Protocol

flowchart TB
  A0(("Graph Management<br/>5 symbols"))
  A1(("Graph Engine<br/>2 symbols"))
  A2(("Pull Request Review<br/>2 symbols"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2 reached;
Loading
Can be affected Symbols Nearest hop Reached from
Graph Management 5 src/graph/build.ts:L151-L410 buildGraph — calls, depth 1 Source Parsing
Graph Engine 2 src/engine.ts:L91-L101 graph — calls, depth 2 Source Parsing
Pull Request Review 2 src/app/brain-build.ts:L251-L358 readRepository — calls, depth 2 Source Parsing
Who knows this code — 5 people across 6 areas
Area Who knows it
Source Parsing · changed @shhdwi — 1 commit, last 15d ago
MCP Protocol · changed @anirudhkumar-nanonets — 2 commits, last 9d ago · @afeddersen — 1 commit, last 11d ago
Command Line Interface · changed @anirudhkumar-nanonets — 5 commits, last yesterday
Graph Management · affected Buseong Kim — 1 commit, last 11d ago · @tpoignonec — 1 commit, last 15d ago
Graph Engine · affected @shhdwi — 1 commit, last 15d ago
Pull Request Review · affected @anirudhkumar-nanonets — 8 commits, last yesterday

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 9 dependent symbols, grouped by area

Graph Management — 5 symbols in 5 files

  • src/graph/build.ts:L151-L410 — buildGraph (calls, depth 1)
  • src/graph/check.ts:L58-L164 — checkGraph (calls, depth 1)
  • src/graph/container.ts:L151-L208 — extractContainer (calls, depth 1)
  • src/graph/refresh.ts:L150-L227 — ensureFreshGraph (calls, depth 2)
  • src/graph/workspace.ts:L630-L655 — federateCheck (calls, depth 2)

Graph Engine — 2 symbols in 1 file

  • src/engine.ts:L91-L101 — graph (calls, depth 2)
  • src/engine.ts:L82-L84 — checkGraph (calls, depth 2)

Pull Request Review — 2 symbols in 2 files

  • src/app/brain-build.ts:L251-L358 — readRepository (calls, depth 2)
  • src/app/review.ts:L45-L99 — reviewPullRequest (calls, depth 2)
Test signal per changed area — 1 ✓ · 1 ⚠ · 1 –

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • ⚠ Source Parsing — 1 of 4 reached · 8 test files reach it, none changed here
    • not reached: grammarOf, treeSitter, parseSource
  • ✓ MCP Protocol — 1 of 12 reached · 1 test file changed here: test/mcp-server.test.ts
    • not reached: discoverResult, initializeResult, metaProtocolVersion, reply, replyError, send, send, stdoutWrite, …3 more
  • – Command Line Interface — no function, method or class changed here
38 test suites also reference this code

46 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/ask-index.test.ts
  • test/ask.test.ts
  • test/container-extract.test.ts
  • test/context-only-dir.test.ts
  • test/context.test.ts
  • test/covers.test.ts
  • test/generic-extract.test.ts
  • test/graph-bindings.test.ts
  • test/graph-extract-dedup.test.ts
  • test/graph-follow-submodules.test.ts
  • test/graph-go.test.ts
  • test/graph-incremental.test.ts
  • test/graph-invariants.test.ts
  • test/graph-java.test.ts
  • test/graph-languages.test.ts
  • test/graph-load.test.ts
  • test/graph-php.test.ts
  • test/graph-posix-paths.test.ts
  • test/graph-python.test.ts
  • test/graph-r-classes.test.ts
  • …18 more

graft blast · refs/graft/base...HEAD · depth 2 · 6 changed files

Open the interactive graph → — click an area to see the code that changed, and the line that reaches it.

github-actions Bot added a commit that referenced this pull request Aug 25, 2026
github-actions Bot added a commit that referenced this pull request Aug 25, 2026
@Frankie-Xu
Frankie-Xu marked this pull request as draft August 26, 2026 09:53
…io (trailhq#211)

Closes trailhq#211 trailhq#212 — same root cause on both spec revisions: the stdio
server loaded native grammars at boot (or returned before stdin was
armed), so official SDK clients saw Connection closed / timeout instead
of an initialize result.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Frankie-Xu
Frankie-Xu force-pushed the fix/211-mcp-initialize-handshake branch from cfaf5c5 to 2b2f590 Compare September 11, 2026 13:14
The handshake rebase changed rpc() to return {responses, exitCode, stderr}; listTools still treated the return as an array.
github-actions Bot added a commit that referenced this pull request Sep 11, 2026
@Frankie-Xu
Frankie-Xu marked this pull request as ready for review September 11, 2026 13:21
@Frankie-Xu

Copy link
Copy Markdown
Contributor Author

Rebased onto current main. Handshake is completed before native grammars load; tools/list still uses advertised() (including the parent-worktree case). CI is green — please review when you can.

Ercaner1988 added a commit to Ercaner1988/Graft that referenced this pull request Sep 12, 2026
actionlint checks context availability, expression syntax, action inputs, and
shellchecks `run:` blocks -- none of which YAML validation catches. Soup's CI
found this the hard way (a job-level `env:` referencing the `runner` context,
valid YAML, invalid Actions, dead before any job logged) and pins the release
binary by SHA-256 rather than `go install`-ing it. Ported verbatim, credited.

Also lands test/ratchet-lazy-grammar-import.test.ts: an AST-based repo-wide
ratchet banning a static top-level import of any native tree-sitter grammar
package (trailhq#323) -- the exact idiom trailhq#337 and trailhq#217 both moved away from. It runs
as an ordinary test under ci.yml's existing `npm test`, no new job needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ercaner1988 added a commit to Ercaner1988/Graft that referenced this pull request Sep 13, 2026
… depth language (trailhq#337 trailhq#217 trailhq#214 trailhq#325)

Four PRs were rewriting the same code path in extract.ts — one function now
does what they each asked for:

- Lazy loading (trailhq#217): a grammar (and the core tree-sitter binding itself) is
  required at most once, the first time entryFor()/depthExtensions() is asked
  about that language, not for all nine at module-import time. `graft mcp`
  never asks before the client's `initialize` reply, so a client can no
  longer see that handshake stall behind nine native loads.
- Per-language isolation (trailhq#337, unchanged): a grammar that will not load
  costs its own language, not the CLI.
- WASM fallback for every depth language, not just Kotlin/Java (trailhq#214): the
  breadth tier already had a "FALLBACK row" mechanism (a GENERIC_LANGS row
  reachable only when the matching depth grammar failed); tree-sitter-wasm
  ships a .wasm for all nine depth languages already, so the remaining seven
  now have one too. None has a queries/<name>.scm, so on the rare machine
  that actually reaches one it degrades to the node-kind walker (symbols
  only) instead of leaving the language unindexed.
- optionalDependencies (trailhq#325's core ask): the eight native grammar packages
  moved out of dependencies, so a platform lacking a prebuild for one no
  longer fails `npm install` for the other eight (core tree-sitter stays
  required — a missing core is a bigger question than one language, left for
  a follow-up).

New test: `graft mcp` answers `initialize` with tree-sitter-typescript broken
and never touches grammar loading at all (no warning on stderr) — the
concrete claim trailhq#217 exists for.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@shhdwi

shhdwi commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Thanks for digging into this! On current main, graft mcp already completes the stdio handshake. It answers initialize with or without protocolVersion, answers tools/list, and stays running. @hasmcp/mcp-spec-test@0.1.1 --spec-version 2025-11-25 shows a stock official-SDK client completing the handshake and listing tools. Merged onto main, this branch makes the suite's tools/call returns a schema-conformant CallToolResult check fail, which main passes. The remaining gaps (not echoing an unsupported protocol version, server/discover) are still useful, and we would welcome a small focused PR for either. Closing this one.

@shhdwi shhdwi closed this Sep 29, 2026
github-actions Bot added a commit that referenced this pull request Sep 29, 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

2 participants