# 109 — Stop binding `ottyl mcp` to one session for its whole lifetime ## Summary `ottyl mcp --session ` resolves the session name to a port once, at process startup, and then serves stdio JSON-RPC for the life of the process against that fixed URL. In practice this means every ottyl session an agent wants to talk to needs its own registered MCP server entry (e.g. `ottyl-hygienic-gyrfalcon`), and switching which running `ottyl` instance a client is looking at requires killing that entry and re-registering a new one — which, for MCP clients that only read server config at startup, means the whole host process needs restarting before the new tools are even callable. This got hit directly: an agent needed to switch from an old dead session (`enormous-warthog`) to a new one (`hygienic-gyrfalcon`) mid-conversation and had no way to do it without editing MCP config and waiting for a restart. ## Where - `src/cli/mod.rs:43-49` — `Command::Mcp { session: Option, port: Option }`, the CLI surface. - `src/cli/mod.rs:171-195` (`run_mcp`) — resolves `session` to a port via `sessions::resolve_session` exactly once, builds one `OttylMcp::new(base_url)`, then blocks forever on `service.waiting()`. There is no re-resolution after startup. - `src/api/mcp_server.rs:254-282` — `OttylMcp` stores `base_url: String` as a plain field set once in `new()`; every `#[tool]` method builds request URLs via `self.url(path)` (`mcp_server.rs:282-284`), so the fixed base URL is threaded through every single tool call. - `src/cli/sessions.rs` — `SessionInfo { name, port, pid }` and `resolve_session`/`list_sessions`, the registry this would need to re-query if the bound session can change after startup. ## Direction Two ways to remove the "pick a session forever at startup" behavior; pick one during implementation rather than building both: 1. **Request/response CLI, no long-running process.** Add a CLI subcommand (e.g. `ottyl call `) that resolves the session, makes the one HTTP call, prints the result, and exits — no `rmcp`/stdio JSON-RPC server at all. Simplest fix for the "MCP config never changes, session does" mismatch, but loses whatever the MCP client gives you over a bare CLI call (tool discovery/schema, a persistent connection) — check whether that matters to how this is actually invoked before going this route. 2. **Long-running MCP server, session switchable at runtime.** Keep `ottyl mcp` as a stdio server, but stop fixing `base_url` at construction: either (a) accept a `session` (or `port`) argument on every tool call, resolved against `sessions::resolve_session` per-call instead of once in `run_mcp`, with the constructor's `--session`/`--port` becoming just a default; or (b) add an explicit `switch_session` tool that mutates the stored target (`base_url` would need to move from a plain field to something mutable, e.g. `Mutex` or resolved fresh from the registry on every call from a stored session name instead of a pre-resolved URL). ## Scope - Land one of the two directions above for `ottyl mcp` / `src/api/mcp_server.rs`; out of scope to build both. - If going with direction 2, update `SKILL.md`'s "Connect an MCP client" section, since it currently documents the resolve-once-at-startup model. - Out of scope: changing anything about the underlying `/api/...` HTTP routes themselves, or the session registry format in `src/cli/sessions.rs` — this is purely about how the MCP-facing process picks and holds (or doesn't hold) its target session.