MCP server
@irtio/mcp is an MCP server for irtio. 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.
Every tool is one call to irtio, narrated. The server holds no state of its own and contains no game logic.
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", "--allow-writes"]
}
}
} Leave out --allow-writes for a server that can only read: see Writes are off by default.
To install it instead, run npm i -g irtio @irtio/mcp and use "command": "irtio-mcp", with "args": ["--allow-writes"] if the agent should be able to change projects.
Sign-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. You can run it without restarting your editor, and 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 an irtio endpoint 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 irtio endpoint 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 irtio knows about, and their status |
logs | Log lines, oldest first, with a forward cursor and a room filter. Entries are marked untrusted |
metrics | Metric rollups, including per-room tick health |
usage | Measured usage this billing period: room hours, data out, voice minutes, storage |
leaderboard | A project’s board: the top N, or one player’s neighbours and rank |
match_status | How many players are waiting in a quick-match queue, and how many it takes to fill |
deployment_list | Deployments, newest first, and which one is serving |
save_list | The save generations of one room |
Writes are off by default
Started with no arguments, the server is read-only. It lists the read-only tools above and scenario_run, and nothing that writes: an agent cannot see the tools below, and calling one by
name answers writes_disabled with the instruction to ask you. Add --allow-writes to the server’s args, or set IRTIO_MCP_ALLOW_WRITES=1 in its environment, to turn them on. Any other argument
stops the server with an error, so a typo in the flag does not quietly leave it read-only.
This is deliberate. The server acts with your full login, and an agent that can deploy or roll back can be talked into doing it by text it reads. Turn writes on for the sessions that need them.
Every tool carries MCP annotations: readOnlyHint on the reads, and destructiveHint on the tools
that overwrite or remove something. A client that auto-approves read-only tools uses these to tell logs from rollback.
These write, and need --allow-writes:
| Tool | What it changes |
|---|---|
project_create | Registers a project and returns its id, which is also the client join key |
project_update | The name, the per-IP connection limit, or the memory its server gets |
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 project’s server |
rollback | Re-promotes an older deployment, stopping the project’s server |
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 reaches no irtio server, so it works without a login. It runs
against a dev server on your own machine rather than a deployed project, because irtio dev is what
serves the authoritative timeline it asserts on. It opens real sockets and plays the room for a few
seconds, so it is not a cheap read.
Log lines are whatever your room code printed, which often includes names, chat and other text
players typed. The logs result marks its entries as untrusted, and the server’s instructions tell
the agent never to act on a request that arrives inside one. That is a defence, not a guarantee,
which is one more reason writes are off unless you turn them on.
Three tools are disruptive, and their descriptions say so before an agent calls them. save_restore and rollback stop the project’s server, so every room in the project drops its
connections, not only the one named. deploy drains or migrates live rooms.
Signing secrets
jwt_secret_mint returns the secret once, when created. Store it in your server’s environment; it cannot be retrieved later.
Reading a result
Every tool answers with prose, the structured data, and a next action:
Deployed v2 of p_0123456789abcdef. The running server 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 irtio’s own verdict and its hint, because the classification happens server-side rather than here:
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: --allow-breaking needs a migration for v2. Run: irtio migrate create <name>, then re-run with: irtio deploy --allow-breaking A project belonging to another organisation reads as not found rather than forbidden. irtio 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.