What It Does
Mirage serves seven tools to any MCP client:shell, read, write, edit, ls, grep and glob, the set coding-agent harnesses expect. Each parameter carries a description, and read, ls, grep and glob are marked read-only, which lets Claude run them in parallel. Every call runs in a mirage session, so commands go through the shell, file calls go through the workspace’s mounts, and the session’s profile and policies apply. edit refuses to overwrite a file that changed since the agent last read it.
There are three ways to reach them, and all three end at the daemon’s HTTP endpoint: stdio and SSH only carry the messages, so auth, sessions and history are decided in one place. Every shell call is a daemon job, listed by GET /v1/jobs as a POST /shell is.
stdio
mirage mcp relays stdin and stdout to the daemon’s endpoint, starting the daemon when it is not running. Given a config, it creates a workspace from it. With no workspace_id in the config, that workspace is deleted when the client closes stdin, as a stdio server’s state ends with its process. A config that names a workspace_id keeps its workspace: the first run creates it, and a later run of the same config reaches it again, because the daemon answers a create with the live workspace made from an identical config. A different config naming the same id is refused rather than attached. It looks for workspace.yaml walking up from the working directory; MIRAGE_MCP_CONFIG or a path argument picks another.
-w <id> serves a workspace the daemon already holds, and leaves it as it was when the client goes:
-s <session> serves the tools as a session the workspace already holds, under that session’s profile, as mirage shell -s runs a line. A session the workspace does not hold is refused before anything is relayed:
${VAR} in the config is read from the environment mirage mcp runs in, so a host’s env block for the server reaches it. A {from: env, key: VAR} secret is fetched by the daemon from its own environment, as it is for mirage workspace create, so a credential stays where the workspace runs.
HTTP
The daemon serves every workspace at/v1/workspaces/{id}/mcp, behind the same host check and auth as the rest of its API. The endpoint is stateless: each request runs in the workspace’s default session, so a cd in one call holds for the next, and a session created with POST /v1/workspaces/{id}/sessions is named in the URL to give an agent its own cwd and environment:
?session_id=agent, the TypeScript daemon with ?sessionId=agent, the casing each daemon’s API uses throughout. An unknown workspace or session answers 404.
SSH
With the daemon’s SSH door on, themcp subsystem serves the workspace the login names over MCP’s stdio framing, so any client that launches a command can use it:
ssh login gets, and it closes when the channel ends. A key whose profile seals a path gets the same refusal from read as from cat.
The same tools over HTTP and the CLI
The tools are not only MCP’s. The daemon serves each one butshell at
POST /v1/workspaces/{id}/{read,write,edit,ls,grep,glob}, with the tool’s
input as the JSON body and the session named in the query as on the MCP
endpoint; shell is POST /v1/workspaces/{id}/shell. The CLI has one
verb per tool (mirage read -w ID PATH, mirage grep -w ID -i PATTERN PATH,
and so on). Every door calls the same per-session tool table, so a read
through one stamps the file for a write or edit through another.
Errors
A tool the server does not serve is a protocol error (-32602 Tool <name> not found). Arguments outside a tool’s input schema, and a failing call, come back as the tool’s answer with isError set, so the agent reads them and can retry.