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

KeyDefaultWhat it does
mcp.enabledfalseServes the MCP endpoint at /mcp.
mcp.host127.0.0.1The 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.port25585The 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

ToolArgumentsWhat it returns
run_commandcommandRuns the command as the console (as RCON does: permission level 4, a leading / optional) and returns each line it replied with.
server_statusnoneTick 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_playersnoneEvery player online: name, uuid, entity id, dimension, position, rotation, whether on the ground, game mode, health, food and view distance.
entities_nearx, 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_atx, 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_infrom, 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_infocx, 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_loglines? (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 /mcp with one JSON-RPC 2.0 message, or a batch. A request is answered 200 with application/json. A notification (notifications/initialized) or a response is answered 202 Accepted with no body.
  • Methods: initialize, ping, tools/list and tools/call. Anything else is -32601.
  • There is no server-sent event stream and no sessions are kept: any method but POST is 405. Any path but /mcp is 404.
  • 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 is 411.
  • blocks_in reads at most 4096 blocks, entities_near looks at most 256 blocks away and lists at most 500 entities, and recent_log keeps the last 2000 lines.

Security and privacy

  • run_command runs any command at the console's permission level, so the token is as powerful as the console. Anyone who can read rustmc.yml can read the token.
  • Every request must carry Authorization: Bearer <token>, or it is answered 401. 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 Origin header that is not localhost, 127.0.0.1 or [::1] is 403, which keeps a web page from reaching the endpoint through a browser. An Origin of null is accepted, so the token remains the real guard.
  • The endpoint speaks plain HTTP. With host set 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.