1# Chapter 8: mcp, a window that answers questions 2 3The window is worked on by AI agents, and an agent cannot look at a 4screen. It can call tools, though. The [Model Context Protocol](https://modelcontextprotocol.io) 5(MCP) is a standard way for a program to offer tools to an agent client such 6as Claude Code: a list of named functions with JSON-schema arguments, called 7over JSON-RPC. So the window carries an MCP server, and an agent can ask the 8running game what it is doing instead of photographing it: `state`, `frame` 9(the picture as a PNG), `read_wram`, `bot`, `jev_history`, `budget`. 10 11**Who plays is not who watches.** The bot plays; the client watches. So the 12tools come in two sets. The ones that only look (`watching`) are always 13there. The ones that change anything (`driving`: `press`, `save_state`, 14`load_state`, `power_cycle`, `restart`, `hot_patch`) exist only while *dev 15mode* is on, and it is off whenever the window opens. Turning it either way 16tells every client its tool list changed. 17 18**The console stays on its own thread.** The emulator belongs to the window's 19UI thread. A tool call becomes a `Request` sent down a channel; the UI thread 20answers it at the next frame boundary and the reply comes back on a oneshot. 21Nothing in this crate touches the emulator, which is why it can be a library 22the window merely uses. 23 24```mermaid 25sequenceDiagram 26 participant A as Agent (Claude Code) 27 participant P as native mcp (stdio proxy) 28 participant S as Server in the window (127.0.0.1:7637) 29 participant U as UI thread (owns the console) 30 A->>P: tools/call state 31 P->>S: the same call, over streamable HTTP 32 S->>U: Request::State 33 U-->>S: at the next frame boundary 34 S-->>P: result 35 P-->>A: result 36``` 37 38> **Aside: why a proxy in front?** A client talking HTTP straight to the 39> window loses its connection every time the window restarts, which during 40> development is constantly. So a client starts `native mcp` instead, the 41> same binary with no window, which speaks MCP on stdio and forwards to 42> whichever window is running. It outlives any number of windows, and tells 43> the client `tools/list_changed` when one appears, goes or is replaced. It 44> knows a window went away because it holds a request to the window's 45> `/alive` open, which is never answered and ends when that process does. 46 47MCP has two lifecycles in use, and the server speaks both: sessions (the 48protocol before 2026-07-28), kept in a file so a client connected before a 49restart is still connected after it; and sessionless clients that ask to be 50told of changes with `subscriptions/listen`. 51 52> **Try it.** With a window running (chapter 12), from the repository root: 53> 54> tools/watch/budget.sh 55> 56> opens a session on `http://127.0.0.1:7637/mcp`, calls `state`, `budget` 57> and `bot`, and prints one line. Chapter 26 explains it. 58 59## For the people who maintain it 60 61### In this folder 62 63| Path | What | 64| --- | --- | 65| [src/lib.rs](src/lib.rs) | The server: `ADDRESS` (`127.0.0.1:7637`), the `Request` channel to the UI thread, the `watching` and `driving` tool routers, dev mode, `tools/list_changed` for both lifecycles, the `/alive` endpoint. | 66| [src/proxy.rs](src/proxy.rs) | `native mcp`: the stdio process a client starts, which outlives any number of windows. | 67| [src/sessions.rs](src/sessions.rs) | The file-backed `SessionStore` a restarted window restores clients from (`$XDG_STATE_HOME/jev/mcp-sessions.json`). | 68| [src/states.rs](src/states.rs) | Snapshot names (`Name`) and the snapshot directory (`States`) kept beside the ROM, as `<rom>.states/<name>.state`. | 69| [BUCK](BUCK) | The `rust_library` and `//packages/mcp:test`. Deliberately without the hot-patch flags. | 70 71The always-there tools are `state`, `frame`, `window`, `read_wram`, `states`, 72`bot`, `jev_history`, `budget`, `patch_info` and `dev_mode`. Each one except 73`budget` (which reads the shared spend ledger itself, through `jev-http`), 74`patch_info` and `dev_mode` is a `Request` answered by the UI thread, which 75owns the console and the bot: `bot` gets the zbanks bot's own report, 76already JSON (built in the app, so this crate does not link the C), and 77`jev_history` gets `decisions::goal_choice::Chooser`'s bounded history. 78Adding a watching tool is therefore two edits here (the `Request` variant 79and the `#[tool]` method) and one in `apps/native` (an arm in `App::answer`). 80 81This crate was pulled out of `apps/native` on 2026-09-20 so that editing the 82window's own code (`apps/native/src/app.rs`, `packages/panels`) never makes 83rmcp's tool-router macros and schemars' derive expand again; CLAUDE.md has 84the reasoning. It is the one crate here that serves a socket. 85 86← Previous: [Chapter 7, panels/](../panels/) · Up: [packages](../) · Next: [Chapter 9, apps/](../../apps/) →