MCP server
@irtio/mcp is an MCP server over the irtio control plane. It gives a coding agent the same
surface irtio gives you at a terminal: create a project, allow an origin, deploy a room, then
read its rooms, logs, metrics and saves.
It is a thin client and nothing more. Every tool is one call to the control plane, narrated. There is no game logic in it, and it holds no state of its own.
Setup
Sign in once, at a terminal:
npx irtio login That opens a browser, and writes a credential to your machine. Then point your MCP client at the server. The snippet is a command, and it carries no secret:
{
"mcpServers": {
"irtio": {
"command": "npx",
"args": ["-y", "@irtio/mcp"]
}
}
} If you would rather install it, npm i -g @irtio/cli @irtio/mcp and use "command": "irtio-mcp" with no arguments.
How it signs in
The server looks for a credential in two places, in this order:
- The credential file
irtio loginwrites. This is the path to take. IRTIO_TOKENin the environment, for headless cases like CI where no browser login can happen.
When it finds neither, the first tool call answers with the instruction to run irtio login rather
than an authentication error, and you can run it without restarting your editor. The next tool call
picks the new credential up.
Never put a token in the config snippet above. The server does not read one from there, and a secret in an editor config file is a secret in your shell history, your backups and quite possibly your repository.
To talk to a control plane other than the default, set IRT_CONTROL_URL in the server’s
environment. That is a URL, not a credential.
The loop it closes
project_create -> origin_add -> deploy -> scenario_run -> logs / metrics Every result ends with a next action, so an agent working through that sequence is told what to
call next rather than having to guess. deploy reports what the schema change was, whether it
needed a migration, what drained, and where the room is playable.
Tools
Read-only, and cheap:
| Tool | What it does |
|---|---|
whoami | The signed-in account and the control plane it is signed in to |
project_list | Every project in your organisation, with ids and regions |
project_get | One project: name, region, project key, connection limit, origins |
origin_list | The browser origins allowed to connect |
rooms | The rooms the control plane knows about, and their status |
logs | Log lines, oldest first, with a forward cursor and a room filter |
metrics | Metric rollups, including per-room tick health |
usage | Measured usage this billing period: room hours, data out, voice minutes, storage |
deployment_list | Deployments, newest first, and which one is serving |
save_list | The save generations of one room |
These write:
| Tool | What it changes |
|---|---|
project_create | Registers a project and returns its id, which is also the client join key |
project_update | The name, or the per-IP connection limit |
origin_add | Allows one browser origin |
origin_remove | Withdraws one browser origin |
jwt_secret_mint | Mints a signing secret, returned in plaintext once |
jwt_secret_retire | Retires the oldest signing secret for an issuer |
save_restore | Puts one room back to a save, stopping the tenant |
rollback | Re-promotes an older deployment, stopping the tenant |
deploy | Bundles the room in your working directory and ships it |
This one runs locally:
| Tool | What it does |
|---|---|
scenario_run | Runs a scenario against a local irtio dev room and reports every assertion against the server’s recorded timeline |
scenario_run is the only tool that never reaches the control plane, so it works without a login.
It also runs against a dev server on your own machine rather than a deployed project, because the
authoritative timeline it asserts on is served by irtio dev alone. It opens real sockets and
plays the room for a few seconds, so it is not a cheap read.
Three of those are disruptive, and their descriptions say so before an agent calls them. save_restore and rollback stop the project’s tenant, so every room in the project drops its
connections, not only the one named. deploy drains or migrates live rooms.
What is not here
There is no tool that reads a stored secret. jwt_secret_mint returns a plaintext secret at the
moment it creates one, because that is the only moment it can be returned; no route reads one back,
and none will be added.
Fleet, organisation and billing surfaces are out of the tool surface entirely. An agent working in your repository should be able to ship a room, not change who is billed for it.
There is no tool that runs a scenario against a deployed project, because there is no authoritative timeline off your own machine to run it against.
Reading a result
Every tool answers with prose, the structured data, and a next action:
Deployed v2 of p_0123456789abcdef. The running tenant took the new version. 1 room moved.
Playable at wss://eu.irt.io.
{
"project": "p_0123456789abcdef",
"version": 2,
"url": "wss://eu.irt.io",
"applied": "live"
}
next: Verify it: run `irtio simulate --url wss://eu.irt.io --key p_0123456789abcdef`, then call
`logs` and `metrics` for this project. The prose is what an agent reads to decide; the JSON is what it reads to act. Both are always present.
Errors
A refused call is narrated too, with the same shape. A breaking schema change relays the control plane’s own verdict and its hint, because the control plane is what classifies the change:
The deploy was refused because the schema change is breaking. deployment introduces 1 breaking
change. A breaking change needs a migration, because rooms holding live state cannot be
reinterpreted under the new schema without one.
next: run: irtio migrate create <name> A project belonging to another organisation reads as not found rather than forbidden. The control plane answers the same way for a project that does not exist and one you cannot see, and this server does not restore the distinction.
See Error catalogue for the codes themselves, and CLI for the commands these tools mirror.