Running a server
The MCP endpoint
On this page
rustmc can serve the Model Context Protocol so an AI assistant such as Claude Code can look into a running server and act on it: run console commands, and read the tick rate, the players, the entities near a point, the blocks in a box, a chunk's state and the console's recent output.
It is off by default, binds localhost by default, and every request must carry a bearer token. Anyone holding the token has the server console: treat it as you would the RCON password.
Turning it on
In rustmc.yml (beside server.properties, or wherever --rustmc-yml points):
mcp:
enabled: true
host: 127.0.0.1
port: 25585
token: ""
The settings are read at startup. Start the server, and with the token left empty it prints
Generated an MCP token and wrote it to rustmc.yml
MCP endpoint listening on http://127.0.0.1:25585/mcp
rustmc.yml now holds token: "<40 letters and digits>", and every other line of the file is left
as it was. You can set your own token instead; any string works. If the endpoint cannot start (the
port is taken, say), the server prints MCP endpoint: and the reason, and carries on without it.
Settings
| Key | Default | What it does |
|---|---|---|
mcp.enabled | false | Serves the MCP endpoint at /mcp. |
mcp.host | 127.0.0.1 | The address the endpoint binds. The default takes connections from this machine only; anything else exposes console access to the network, guarded by the token alone. |
mcp.port | 25585 | The TCP port. 0 binds whatever port is free and prints it at startup. |
mcp.token | "" | The bearer token every request must present. Empty, a random 40-character token is generated on the first start with the endpoint on and written back to rustmc.yml. |
Adding it to Claude Code
claude mcp add --transport http rustmc http://127.0.0.1:25585/mcp --header "Authorization: Bearer <token>"
with <token> the value of mcp.token. Add --scope project to share it through the project's
.mcp.json. Keep the token out of version control: Claude Code expands ${VAR} in that file, so
"Authorization": "Bearer ${RUSTMC_MCP_TOKEN}" works. /mcp inside Claude Code shows whether it
connected and lists the tools.
Tools
| Tool | Arguments | What it returns |
|---|---|---|
run_command | command | Runs the command as the console (as RCON does: permission level 4, a leading / optional) and returns each line it replied with. |
server_status | none | Tick state, target and achieved TPS, mean, median, 95th-percentile and longest milliseconds per tick over the last hundred ticks, and ticks since start; players online, the maximum and their names; for each world served, chunks built, prepared chunks served, force-loaded chunks, cache sizes and height range; creature and item counts; chunk builder threads, whether missing chunks are generated, and the chunk send queue; pending fluid and block ticks; uptime; the last status: line printed. |
list_players | none | Every player online: name, uuid, entity id, dimension, position, rotation, whether on the ground, game mode, health, food and view distance. |
entities_near | x, y, z, dimension?, radius? (default 16, at most 256), type? | Creatures, players and item entities within the radius, nearest first: id, uuid, type, position, distance and a few fields of each (health, name, item and count). At most 500 are listed; total counts them all. |
block_at | x, y, z, dimension? | The block state (minecraft:oak_stairs[facing=north,half=bottom]) and its numeric id, and whether the chunk is generated and loaded. |
blocks_in | from, to ([x, y, z] corners, inclusive), dimension?, include_air? | At most 4096 blocks: a palette of state counts, every non-air block with its position (air too with include_air), and each chunk the box touches with whether it is generated and loaded. |
chunk_info | cx, cz (chunk coordinates: block coordinate divided by 16, rounded down), dimension? | status (loaded, saved on disk only, or not_generated), whether it is force-loaded, inhabited time, the creatures in it, and the players whose view holds it, each sent or still queued. |
recent_log | lines? (default 100, at most 2000), contains? | The last lines the server printed to its console (standard output and standard error), oldest first, optionally only those containing some text. |
Arguments marked ? are optional.
How it behaves
dimension is minecraft:overworld (the default), minecraft:the_nether or minecraft:the_end.
The minecraft: prefix may be left off, and nether and end are accepted. A dimension the server
does not serve is an error.
The reading tools never generate a chunk. A position in a chunk that has not been generated reads
as generated: false with no state. A chunk that is on disk is read from disk. The block tools
report the real blocks, even in a world with anti-xray on.
Each result is given twice, as MCP allows: as text (pretty-printed JSON, or the command's lines for
run_command) and as structuredContent. A tool that cannot do what was asked (a bad argument, a
box too large) answers with isError: true and says why. An unknown tool or a malformed call is a
JSON-RPC error.
Protocol
MCP revision 2025-06-18 over Streamable HTTP, plain JSON only:
POST /mcpwith one JSON-RPC 2.0 message, or a batch. A request is answered200withapplication/json. A notification (notifications/initialized) or a response is answered202 Acceptedwith no body.- Methods:
initialize,ping,tools/listandtools/call. Anything else is-32601. - There is no server-sent event stream and no sessions are kept: any method but
POSTis405. Any path but/mcpis404. - Connections are kept alive between requests and closed after 120 seconds idle.
A quick check from a shell:
curl -s http://127.0.0.1:25585/mcp \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"run_command","arguments":{"command":"list"}}}'
Limits
- A request head is at most 16 KiB and a body at most 1 MiB (larger is
413). - A body must be sent with
Content-Length; a chunked body is411. blocks_inreads at most 4096 blocks,entities_nearlooks at most 256 blocks away and lists at most 500 entities, andrecent_logkeeps the last 2000 lines.
Security and privacy
run_commandruns any command at the console's permission level, so the token is as powerful as the console. Anyone who can readrustmc.ymlcan read the token.- Every request must carry
Authorization: Bearer <token>, or it is answered401. The token is compared in constant time. There is no limit on wrong attempts, so use a long token (the generated one is 40 characters) and keep the endpoint off networks you do not trust. - A request with an
Originheader that is notlocalhost,127.0.0.1or[::1]is403, which keeps a web page from reaching the endpoint through a browser. AnOriginofnullis accepted, so the token remains the real guard. - The endpoint speaks plain HTTP. With
hostset to anything but localhost, the token and every reply cross the network unencrypted; put it behind a tunnel or a TLS proxy instead. - What the tools return includes player names, UUIDs, positions and the console's recent output.
From docs/mcp.md in the RustMC repository at bbd81209ad.