jevsnes.git / tools / mcp / README.md
1# Chapter 25: mcp, how a client reaches the window in milliseconds
2
3An MCP client such as Claude Code starts its servers when it starts, and
4gives each one 30 seconds to answer `initialize`. This repository's
5`../../.mcp.json` names one server, `jev`, and its command is
6`tools/mcp/launch.sh`. Everything in this folder exists to make that
7command answer fast, every time.
8
9It used to be `nix develop -c buck2 run //apps/native:native -- mcp`. On a
10cold cache that is a build of minutes; the client gave up after 30 seconds,
11and the connection failed at startup twice (2026-09-20 and 2026-09-21).
12
13So now there are two steps, and only the fast one is on the client's clock:
14
15```mermaid
16sequenceDiagram
17  participant You
18  participant I as install.sh
19  participant C as MCP client
20  participant L as launch.sh
21  participant P as run/bin/native mcp
22  You->>I: after a build you want the proxy to be
23  I->>I: build //apps/native, copy it to run/bin/native, root its store paths
24  C->>L: start the "jev" server
25  L->>P: exec (bash builtins only, no build, no devshell)
26  P-->>C: initialize answered in milliseconds
27```
28
29If there is no copy yet, `launch.sh` answers `initialize` with a JSON-RPC
30error saying to run `install.sh`, which the client shows, and exits.
31
32> **Aside: why a copy that may be old is fine.** The proxy (chapter 8) is a
33> pass-through. It asks the window for its tool list and forwards calls by
34> name, so the tools a client sees are the running *window's*, whatever
35> build the proxy came from, and it speaks to the window over MCP's own
36> streamable HTTP, which does not change with our code. A proxy several
37> commits older than the window is normal and correct. Re-run `install.sh`
38> only when `packages/mcp/src/proxy.rs` itself changes, then reconnect the
39> client (`/mcp` in Claude Code), since the running proxy keeps its own
40> copy.
41
42> **Try it.**
43>
44>     tools/mcp/install.sh
45>     tools/mcp/test.sh
46>
47> The second times `initialize` and `tools/list` through `launch.sh` in a
48> bare environment, with and without a binary.
49
50## For the people who maintain it
51
52### In this folder
53
54| Path | What |
55| --- | --- |
56| [install.sh](install.sh) | Builds `//apps/native:native` and copies it to `run/bin/native`, with the devshell's library path in `run/bin/native.env` and every store path it links rooted under `run/bin/roots/`. Re-execs itself inside `nix develop` if it is not already. |
57| [launch.sh](launch.sh) | What `.mcp.json` runs. Execs the copy; if there is none, answers `initialize` with a JSON-RPC error saying to run `install.sh`, and exits. Bash builtins only, so it runs in a bare environment. |
58| [test.sh](test.sh) | Times `initialize` and `tools/list` through `launch.sh` in a bare environment, with and without a binary, against a scratch copy of the layout. |
59
60← Previous: [Chapter 24, tools/](../) · Up: [tools](../) · Next: [Chapter 26, watch/](../watch/) →