jevsnes.git / packages / mcp / CLAUDE.md
1@README.md
2
3- **This is the one crate under `packages/` that is native-only and does
4  NOT build for `wasm32-unknown-unknown`.** `../CLAUDE.md`'s "no
5  `std::fs`/`std::net`/threads/windowing" rule is about every OTHER crate
6  here staying portable to the browser build; this one is sockets
7  (`tokio::net`, `axum`), threads (its own MCP-server thread) and files
8  (`sessions.rs`, `states.rs`) on purpose, because that is what serving MCP
9  and remembering sessions/snapshots IS. It lives under `packages/` rather
10  than a new top-level directory because it is a plain buck2 `rust_library`
11  like every other package, just not a portable one - `apps/native` is its
12  only consumer and always will be, since a browser build has no local
13  process for an agent to talk to.
14- **Why this crate exists at all, measured 2026-09-20:** before the split,
15  `mcp.rs`/`proxy.rs`/`sessions.rs`/`states.rs` were part of `apps/native`'s
16  own tip crate. Every edit to `apps/native` or `packages/panels` forced
17  buck2 to re-run that crate's ONE rustc invocation, which re-expanded
18  rmcp's `#[tool_router]`/`#[tool]` proc-macros and schemars' `JsonSchema`
19  derive on every single hot-patch cycle, whether or not anything in this
20  code had changed. As its own buck2 target, this crate is recompiled only
21  when ITS OWN source changes.
22- **Deliberately not hot-patch-flagged.** No `-Csave-temps`/`-Clink-dead-code`
23  in `BUCK`. An edit here is not meant to be part of the fast iteration loop
24  `tools/hotpatch` serves - it needs a `restart`. If that ever stops being
25  true (e.g. wanting to hot-patch a tool's description text), add the two
26  flags the same way `packages/panels/BUCK` does, and `patch.sh`'s existing
27  first-party-dependency scan will pick this crate up with no other change.
28- **A new `Request` variant breaks `apps/native` until it is answered, on
29  purpose.** `App::answer` matches every variant with no wildcard arm, so the
30  compiler names the one place the console is actually read. Add the variant
31  and the tool here, then the arm there — never a `_ => {}` to make the build
32  go quiet, which would leave the tool hanging on a reply that never comes
33  (the caller waits on the oneshot until the window closes).
34- **A reader that can be absent returns `Option<T>`, and the tool says so in
35  words**, not with a JSON `null`: null reads to a client as this tool
36  failing, where the truth is about where the game is.
37- `Request`'s `SaveState`/`LoadState`/`HotPatch` variants and the tool
38  methods that build them are the actual channel contract with the app; the
39  UI-thread handling of every `Request` variant stays in `apps/native`
40  (`app.rs`'s `App::answer`), not here - this crate only ASKS, it never
41  touches the console.