Skip to main content
The in-app API is what every other interface is built on: HTTP, MCP, RPC, SSH and the CLI all end in the calls on this page. Use it directly when your agent runs in the same Python process as the workspace.

Workspace and Session

A Session is one session’s handle: shell runs a line as it, glob matches as it, and vfs is the file API run as it, so every call answers under the session’s profile, with its working directory, environment, mount modes and hides.
ws.session(id) creates the session when the id is new and adopts it when it exists. Pass profile= to create it under one of the workspace’s profiles, or mounts= to narrow its mount modes. A profile, mounts or permissions for an existing session raise ValueError, since a profile is set once, when the session is created. ws.shell(line, session_id=...) and ws.glob(pattern, session_id=...) are the same calls without the handle; without a session id they act as the workspace’s default session.

session.shell

It answers an IOResult: exit_code, and await stdout_str() / await stderr_str() for the text (stdout and stderr are the lazy byte streams). A non-zero exit is a result, not an exception.

Cancel a line

Set the event, or cancel the task awaiting the line. The line stops at whatever await it is in, and $? goes back to what it was before the line ran. Setting the event raises MirageAbortError from the call:
A command that runs past its limit is not a cancel: it exits 124.

session.vfs

The file API, run as the session. Paths are absolute workspace paths, and bytes are bytes. A missing path raises FileNotFoundError, and a refused one PermissionError, as the os module does.

Beside your own event loop

An app that already runs its own loop, such as a FastAPI or aiohttp server, can pin a workspace to a thread and loop of its own, so a slow call inside the workspace never stalls the app. runner.call runs a workspace coroutine there from any loop; the Mirage server hosts every workspace this way.

The agent tool table

MirageToolOperations is the seven agent tools (shell, read, write, edit, ls, grep, glob) as a class, answered for one session. MCP, RPC’s tools/call, the HTTP tool routes and the CLI’s tool verbs all call it, so an agent loop of your own gets the same answers:
Each tool is also a method: await tools.read("/data/report.csv", offset=0, limit=20), await tools.grep("todo", "/src", ignore_case=True). call takes the arguments as the schema reads them, and is what every interface uses. mirage.agents.tool_descriptions holds each tool’s description and JSON schema (READ_DESCRIPTION, READ_INPUT, …) to hand your model, and the agent adapters wrap the table for each framework.

Embed the MCP server

The MCP server the Mirage server runs is a class you can run yourself, over any MCP transport, without the Mirage server:
server is the SDK’s low-level Server, so it also mounts behind StreamableHTTPSessionManager in your own ASGI app. mirage.server.mcp.server.TOOLS is the tool list it advertises.

Embed the RPC server

MirageRpcServer serves one session over JSON-RPC, with the same methods the Mirage server answers. handle answers one message, for a route of your own; serve answers a newline-delimited stream until it ends, running each request as its own task so $/cancelRequest reaches a running one:
In-app, shell runs the line directly on the session; the Mirage server runs it as a job instead.

Reach a server from code

When the workspace lives in a server, any HTTP client reaches it. The token is in ~/.mirage/auth_token in the default local mode:
POST /rpc carries the whole Session API in one route; the HTTP page lists the rest, and the RPC page shows a mirage rpc client over stdio.