jevstrudel.git / tools / mcp / README.md
1# tools/mcp
2
3`strudel-mcp.mjs` is the MCP server Claude Code starts from `.mcp.json`
4(`nix run .#mcp`). It speaks MCP over stdio and forwards each message to
5one of two jevstrudel environments:
6
7- **dev** (the default): the jevstrudel Worker's MCP endpoint
8  (`worker/src/mcp.ts`) that `buck2 run //:dev` runs, playing into this
9  machine's tabs with no sign-in. It keeps no connection, so the Worker can
10  restart or reload under a running Claude Code session; while it is down,
11  tool calls say how to start it, and the tool list is the Worker's own
12  files (`worker/src/tools.json`, then `shared-tools.json`, as the Worker
13  lists them). It polls the Worker's tool list and sends
14  `tools/list_changed` when it changes, so an edited tool reaches Claude
15  Code without a restart.
16- **prod**: the public site's hosted MCP (`/jev/mcp`,
17  `worker/README.md`, The hosted MCP), as the user's own account.
18
19The proxy adds one tool of its own to either list, `use_environment`:
20`{ "environment": "prod" }` or `"dev"` switches, and with no argument it
21says which is in use. A switch sends `tools/list_changed`, since the lists
22differ (prod has `list_songs`, `publish_song`, `comment`, …; both have the
23shared tools, `list_sounds`, `ask_jev_sound`, `api_reference`, the settings,
24`play_song`, `ask_jev_song`, `react` and `vote`).
25
26The first switch to prod signs in. The proxy is an OAuth client of the
27site: the MCP SDK's `auth()` does discovery, dynamic registration, PKCE and
28the token exchange; the proxy listens for the redirect on `127.0.0.1` and
29a random port and opens the authorize page in the Windows browser
30(`rundll32.exe url.dll,FileProtocolHandler` through WSL interop). The user
31signs in with a passkey and allows the app; the call waits up to two
32minutes for that, and if it takes longer, answers with the URL and switches
33by itself when the sign-in completes. A token prod refuses later (expired,
34or the app disconnected in the account panel) is refreshed silently, or
35else the call answers with a new sign-in's URL. Rate limits and missing
36scopes come back as tool errors saying so.
37
38The tokens and the registration are in the proxy's memory only: a new
39Claude Code session signs in once, and the proxy revokes its grant when it
40exits.
41
42| File | What it is |
43|---|---|
44| `strudel-mcp.mjs` | The stdio server: reads the ports, wires the rest together. |
45| `proxy.mjs` | Forwarding and the environment switch, apart from stdio. |
46| `oauth.mjs` | The OAuth client: the SDK's `auth()` plus the loopback redirect, `state` and `iss` checks, and revocation. |
47| `browser.mjs` | Opening the authorize page in the user's browser. |
48| `proxy.test.mjs` | `node --test`: the switch and forwarding, against fakes (in `nix flake check`). |
49| `package.json` | The MCP SDK, pinned; `package-lock.json` is what nix fetches. |
50
51`tools/mcp-e2e/proxy.mjs` runs the prod path end to end against a local
52production Worker.