3.6 KiB
109 — Stop binding ottyl mcp to one session for its whole lifetime
Summary
ottyl mcp --session <name> 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<String>, port: Option<u16> }, the CLI surface.src/cli/mod.rs:171-195(run_mcp) — resolvessessionto a port viasessions::resolve_sessionexactly once, builds oneOttylMcp::new(base_url), then blocks forever onservice.waiting(). There is no re-resolution after startup.src/api/mcp_server.rs:254-282—OttylMcpstoresbase_url: Stringas a plain field set once innew(); every#[tool]method builds request URLs viaself.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 }andresolve_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:
- Request/response CLI, no long-running process. Add a CLI
subcommand (e.g.
ottyl call <session> <tool> <json-args>) that resolves the session, makes the one HTTP call, prints the result, and exits — normcp/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. - Long-running MCP server, session switchable at runtime. Keep
ottyl mcpas a stdio server, but stop fixingbase_urlat construction: either (a) accept asession(orport) argument on every tool call, resolved againstsessions::resolve_sessionper-call instead of once inrun_mcp, with the constructor's--session/--portbecoming just a default; or (b) add an explicitswitch_sessiontool that mutates the stored target (base_urlwould need to move from a plain field to something mutable, e.g.Mutex<String>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 insrc/cli/sessions.rs— this is purely about how the MCP-facing process picks and holds (or doesn't hold) its target session.