Skip to content
Conduit

mcp server

worlddriver

Exposes a running Minecraft game as a programmable control surface: observation, action and Rhino scripting over one API, reachable via MCP, WebSocket RPC or in-JVM script.

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 26, 2026

WorldDriver

A Minecraft mod that exposes the running game as one programmable API surface, reachable three ways:

  • In-process JavaScript — a bundled Rhino engine that runs alongside the game
  • WebSocket JSON-RPC — newline-delimited JSON over ws://127.0.0.1:<port>/rpc
  • Model Context Protocol — Streamable HTTP over http://127.0.0.1:<port>/mcp

All three translate their parameters and call the same router, so an external agent sees exactly what an in-game script sees.

  • Minecraft 1.21.1, Architectury (Fabric and NeoForge from one source tree)
  • Architectury API 13.0.8 must be installed alongside on either loader, the way Fabric API must be on Fabric
  • JDK 21
  • Rhino fork dev.latvian.mods:rhino:2101.2.7-build.81 (the KubeJS build)
  • Licence: LGPL-3.0-only; the GPL-3.0 text it builds on is COPYING

What it gives you

external MCP client            in-game JS script              external WS client
       │                              │                              │
       ▼                              ▼                              ▼
 HTTP /mcp (port 39800)        Driver.invoke(method,…)         WS  /rpc (port 39801)
       │                              │                              │
       └──────────────────────────────┴──────────────────────────────┘
                                      ▼
                                  DriverApi
                       (single source of truth, on the server thread)
                                      ▼
                          live ServerLevel + ClientHooks

Something over seventy methods are grouped by concern. The table below is an orientation map; the method-by-method surface, with parameters and return shapes, is in docs/guide/capabilities.md, and the schemas themselves are generated from common/src/main/java/net/magicterra/worlddriver/mcp/catalog/.

GroupWhat it is for
mc.system.*Probe the running build, fetch the test-arena origin most spatial tools centre on, and wait a fixed number of ticks.
mc.script.evalRun a JavaScript snippet in-process. Preferred whenever a task would otherwise cost three or more round trips.
mc.skillA persistent skill library: save, list, run and delete reusable scripts.
mc.eventsThe driver-to-agent event channel, including rising-edge watchers that poll a route and emit when a predicate flips.
mc.observe.*Read-only sensing: the player, hostiles and incoming projectiles, bosses, the hazard scene, an ASCII spatial map, container contents, and the event backlog.
mc.queryFiltered scans of blocks or entities in a cube, with field projection.
mc.action.*Mutate the world within one server tick: box fill, batched placement, and operator-level vanilla commands.
mc.world.*Single-cell inspection, plus snapshot and restore of a region including block-entity NBT — the undo a risky build or a destructive test wants.
mc.recipe.*Read the game's own recipe table, vanilla plus any loaded mod, and expand a request into an ordered craft plan.
mc.plan.acquireGoal-directed acquisition: route every missing ingredient to mining, farming, smelting or crafting and emit the steps in executable order.
mc.wait.*Long-poll primitives: the next event, world load, an arbitrary truthy condition, and the result of a wait started in the background.
mc.client.*Client-authoritative observation and synthetic GUI input: screen probes, the widget tree, mouse, slot, key and text input, chat, and screenshots.
mc.bot.*The autonomous layer. See below.

On a dedicated server the mc.client.* methods return an error rather than a value, and the two client-only sensing methods — mc.observe.threats and mc.observe.boss — return an empty reading, because the state they read (the client entity render set, creeper swell, projectile velocity, the dragon's phase manager) only exists on a client.

mc.client.screenshot returns a real MCP image content block alongside the metadata text block, so a multimodal model receives the framebuffer as vision input rather than as a large base64 string inside text.

The autonomous layer. mc.bot.* is a client-side agent with its own A* pathfinder, aligned with Baritone's goal surface. It can travel to a position, a block type, an entity, a saved waypoint or a bearing; mine, farm, build, clear an area, tower, bridge and sleep; craft a full sub-recipe tree from what is in the inventory, drive a furnace, and fit the best armour and weapon it owns; follow, explore, flee, fight, fly with an elytra, dig itself an emergency shelter, and cut a staircase out of a pit. Long-running verbs are asynchronous: poll mc.bot.status or pass awaitMs. mc.bot.setting tunes the whole subsystem — a few hundred keys, generated from the settings registry rather than hand-listed, so the inputSchema of that one method is the authoritative list. Two of those keys decide whether the pathfinder may modify the world: allowBreak lets it mine through an obstruction and allowPlace lets it bridge a one-block gap, and both are on by default, which means goto and follow will change terrain unless you turn them off. The scene suite pins both off, so a route that works there is not evidence about a default client. The layering behind all of this is described in docs/dev/bot-layering.md.


Quick start

1. Build, and run the scene suite

StageWright, the in-game test framework the suite runs on, lives in its own repository and is used here as published Maven artifacts: the exact version set as stagewright_version in gradle.properties, which StageWright's CI published to https://nexus.gardel.top/repository/maven-releases. A fresh clone builds:

./gradlew build

WorldDriver's shipped jars contain no StageWright class; only its test sources and development runs use it.

For every pull request and every push to master, CI builds the project, runs the unit tests and runs the scene suite on a NeoForge dedicated server, and a push to master that passes is published to the same Nexus as 0.1.0-build.<build number>+1.21.1. A master run still waiting for an earlier one when a newer push arrives is cancelled, and its push is not published under a number of its own.

One command runs the scene suite against a headless dedicated server:

./gradlew stagewrightDedicatedServerNeoforge

The task provisions a clean run directory, launches the game, runs the scenes and the JavaScript validation suite, and exits zero only if every required scene passed. There are six such tasks, one per combination of process topology and loader, described in docs/dev/testing.md.

2. Run a client and connect an MCP client

./gradlew :fabric:runClient

The development run pins the MCP endpoint to port 39800 and the RPC endpoint to 39801, so the configuration below keeps working across restarts. Override them with -PagentMcpPort= and -PagentRpcPort= when two clients have to coexist. In an ordinary installation the ports are chosen by the operating system and written to worlddriver-mcp.port and worlddriver-rpc.port in the game directory.

Both endpoints open at client initialisation, so you can connect at the title screen before any world is loaded. Methods that need a world return an error until a save is open; mc.client.* and mc.script.eval work immediately.

Both bind to 127.0.0.1. Set -Dworlddriver.rpcHost= or -Dworlddriver.mcpHost= to a wildcard (0.0.0.0, or :: for IPv6) or to a specific interface address to accept connections from other hosts. Read the note on scripting under "Design" before you do: an endpoint that can run scripts can run arbitrary Java, so moving either bind address off loopback puts the whole JVM on the network. A wildcard bind still logs a loopback URL, because 0.0.0.0 and :: are not connectable targets.

Drop this .mcp.json into the directory you launch your MCP client from, and any spec-conformant client discovers the server automatically:

{
  "$schema": "https://modelcontextprotocol.io/schemas/mcp.json",
  "mcpServers": {
    "worlddriver": {
      "type": "http",
      "url": "http://127.0.0.1:39800/mcp"
    }
  }
}

Clients that speak only stdio need a bridge; see docs/guide/mcp-clients.md.

3. Check it from the shell

PORT=$(cat fabric/run/worlddriver-mcp.port)
curl -s http://127.0.0.1:$PORT/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"mc.system.version","arguments":{}}}' | jq

In-game commands

Brigadier subcommands of /worlddriver. The root is the full mod id so that it cannot collide with another mod's command in a large pack. Every subcommand requires permission level 2: an operator, or on a single-player world, one with cheats allowed. Without it, the endpoints are still in worlddriver-mcp.port and worlddriver-rpc.port.

CommandEffect
/worlddriver portPrint the RPC endpoint
/worlddriver mcpPrint the MCP endpoint
/worlddriver reloadReload user scripts from config/worlddriver/scripts/
/worlddriver server spawn|goto|mine|status|clearSpawn and steer a server-side player

The validation suite's /worlddriver test ships with the testmod and exists only in development runs; see docs/dev/testing.md.


Project layout

worlddriver/
├── common/              Architectury shared sources: the router, the transports, the Rhino glue
│   ├── src/main/java/net/magicterra/worlddriver/
│   │   ├── api/            DriverApi — the router and its handlers
│   │   ├── bot/            The client-side autonomous layer: pathfinder, walker, processes
│   │   ├── mcp/            The MCP HTTP server and the tool catalog
│   │   ├── rpc/            The Netty WebSocket server and the JSON codec
│   │   ├── script/         Rhino integration, the evaluator, the optional class filter
│   │   ├── model/          Wire types shared by the transports
│   │   └── client/         The broker for client-only calls; implementations live per loader
│   ├── src/testmod/     The scenes the StageWright tasks run, and the JavaScript validation suite
│   └── src/test/        Plain JVM tests that need no game
├── fabric/              Fabric entry point and client-side implementation
├── neoforge/            NeoForge entry point and client-side implementation
├── stagewright-scenes/  Scene scripts installed into a run's config directory
├── path-replay/         Offline analysis of recorded pathfinding runs
├── docs/                Documentation; start at docs/README.md
└── scripts/             The scene manifests, the source checks, and the MCP bridge

Design

  • One router. DriverApi.route(method, params) is the only function that runs game logic. The MCP server, the WebSocket server and the script bridge each translate parameters and call it; none of them may hold behaviour of its own. The validation suite compares the three transports on a sample of methods, so the guarantee comes from the single router rather than from an exhaustive comparison.
  • Server-thread discipline. Writes, and reads that touch the level, are dispatched onto the server thread. Scripts run off it, so they may block on a result without deadlocking.
  • Spec-conformant MCP. Protocol-version negotiation in initialize, Origin header validation against a loopback allowlist as a defence against DNS rebinding, and image content blocks for screenshots. McpServer.java carries the spec citations inline.
  • Scripting is a first-party capability, and it is not sandboxed by default. A class filter exists — ScriptClassFilter denies process spawning, reflection, raw file and socket access and the JDK internals — but it is disabled unless the JVM is started with -Dworlddriver.sandbox=on, and nothing in the build passes that. This is deliberate: restricting what a script may call restricts the driver's own capability, and anything that can reach the RPC or MCP endpoint already owns the process, so the trust boundary is the endpoint and not the interpreter. Treat both endpoints as you would a shell on the machine. mc.script.eval additionally enforces a wall-clock deadline through Rhino's instruction-count observer, which is a liveness guard, not a security one.
  • One source tree, two loaders. The same common/ sources ship on Fabric and NeoForge through Architectury. The loader-specific modules carry only the entry point and the implementation of the client-only calls.

The architecture in full, including the seams and the threading rules, is in docs/dev/architecture.md.


Status

The mod is usable and under active development; the version number is pre-1.0 and the method surface still moves.

The scene suite covers three process topologies on both loaders: a headless dedicated server, a client hosting its own integrated server, and a dedicated server with a real client joined to it over a socket. The third of those is the shape a production install has, and it is the only one in which an assertion can be made about the process boundary, which is why its client half writes its own results file that the task also judges. A separate reconciliation task compares all six runs against each other, because a scene that skips everywhere records a pass over a subject nothing tested.

The number of scenes is in the per-loader manifests under scripts/stagewright/, which are also what a run is judged against — a scene that registers without being listed there fails the run. Do not take a count from prose, here or anywhere else.

A handful of scenes are declared as optional failures: they record a known gap without failing the run. CHANGELOG.md records what each release changed and ROADMAP.md what is still ahead.


Pointers

中文版本请见 README-zh_CN.md。

↑↓ navigate openesc close