concinnity_dev/mcp/mod.rs
1//! The Model Context Protocol: the one transport the runtime debug surface
2//! speaks.
3//!
4//! A running app serves MCP itself on its debug port (`cn debug`, or
5//! `cn editor --debug-port N`) over the Streamable HTTP transport in its
6//! stateless form, so any MCP client can post straight to
7//! `http://127.0.0.1:8777/mcp`. `cn mcp` is the stdio entry a client that
8//! spawns servers as child processes uses instead: it answers `initialize`,
9//! `tools/list` and `ping` from the verb catalog, so a client connects with no
10//! app running, and forwards each `tools/call` to the app.
11//!
12//! The tool surface is the debug protocol's own verb catalog, so this module
13//! declares no commands of its own. The split below is what keeps the protocol
14//! testable without a socket:
15//! jsonrpc message parsing and response building, transport-agnostic
16//! tools the catalog rendered as MCP tools, and the body one call carries
17//! server the methods answered, over an injected call executor
18//! http the app's transport: one request, one response, one connection
19//! stdio newline-delimited framing over any byte streams
20//! app the executor that runs a call against the live world snapshot
21//! bridge the executor that forwards a call to a running app
22//! remote the client that posts one JSON-RPC message to an app
23
24mod app;
25mod bridge;
26mod http;
27mod jsonrpc;
28mod remote;
29mod server;
30mod stdio;
31mod tools;
32
33pub(crate) use app::AppServer;
34
35/// Serve MCP over stdin and stdout until the client closes stdin, forwarding
36/// tool calls to the app on `port`.
37pub fn run(port: u16) -> std::io::Result<()> {
38 eprintln!("[mcp] forwarding tool calls to {}", remote::endpoint(port));
39 let server = server::Server::new(bridge::Forward::new(port));
40 let stdin = std::io::stdin();
41 let mut stdout = std::io::stdout();
42 stdio::serve(&server, stdin.lock(), &mut stdout)
43}