mindustry-mcp
by hubdustrylab
README.md
# Mindustry MCP
A Model Context Protocol interface to Mindustry.
The first working implementation runs a Rust MCP host and a Java bridge inside an owned Mindustry runtime. It supports a complete logic workflow: create a processor and memory cell, install mlog, observe the real memory value, replace the program with a digest precondition, and close the runtime.
Current supported profile: **Windows x64, Mindustry v159.7 headless, Java 17, stdio MCP**. This is the first executable slice of the architecture. Desktop input, Android, attaching to existing games, arbitrary worlds, and additional capabilities remain future work.
## Install from npm
Requires **Windows x64 and Node.js 22.14 or newer**. Provision the runtime once:
```powershell
npx --yes mindustry-mcp@0.1.0 setup
```
Setup downloads about 210 MB of the pinned official Mindustry server and Temurin JDK, verifies their SHA-256 digests, and extracts Java with Windows PowerShell. Runtime files are stored in `%LOCALAPPDATA%/mindustry-mcp/runtime-v159.7-java17/`; game evidence is retained in `%LOCALAPPDATA%/mindustry-mcp/state/`. It does not change global Java or PATH. Set `MINDUSTRY_MCP_RUNTIME_DIR` and `MINDUSTRY_MCP_STATE_DIR` to choose other directories.
Then configure your MCP client's stdio server:
```json
{
"mcpServers": {
"mindustry": {
"command": "npx",
"args": ["--yes", "mindustry-mcp@0.1.0"]
}
}
}
```
Clients that require the Windows command shim can use `npx.cmd`. Run setup in a terminal before connecting: the MCP launcher reports missing runtime files immediately and performs no downloads during the handshake. No Rust, Gradle, or separately installed Java is needed to use the npm package. It bundles the compiled Rust host, Java bridge, asset digests, and third-party dependency notices. Mindustry and Temurin are downloaded separately from their official releases with their upstream licenses.
For a global install, use `npm install -g mindustry-mcp@0.1.0`, then `mindustry-mcp setup` and the `mindustry-mcp` command. Use a separate state directory for each concurrently connected MCP server; a directory has a single host writer.
## Build from source
Prerequisites: Windows x64, PowerShell 7, `curl.exe`, and Rust installed through rustup. The repository pins Rust 1.95.0. Run from the repository root:
```powershell
./tools/bootstrap.ps1
./tools/build-bridge.ps1 -Verify
cargo build --locked
```
Bootstrap downloads the pinned official Mindustry server and Temurin JDK into `.local/`, checks their SHA-256 digests, and writes `.local/config.json`. The bridge helper provisions a checksum-pinned Gradle 8.10.2 distribution. These scripts do not change your global Java installation or PATH.
## Connect a source build
Configure a stdio server using an absolute path to the launcher:
```json
{
"mcpServers": {
"mindustry": {
"command": "pwsh",
"args": [
"-NoProfile",
"-File",
"C:/path/to/mindustry-mcp/tools/run.ps1"
]
}
}
}
```
Clients with a different configuration format can launch the same command. `run.ps1` reserves stdout for MCP. For a release binary, run `cargo build --release --locked` and pass `-Release` to the launcher.
The executable can also run directly with `--java`, `--server-jar`, `--bridge-jar`, and `--state-dir`. Each option accepts an absolute path. Their environment alternatives are `MINDUSTRY_MCP_JAVA`, `MINDUSTRY_MCP_SERVER_JAR`, `MINDUSTRY_MCP_BRIDGE_JAR`, and `MINDUSTRY_MCP_STATE_DIR`.
## First workflow
| Tool | Input and behavior |
| --- | --- |
| `runtime_open` | `{}`; launches an owned runtime and returns `instanceId` |
| `runtime_status` | `instance_id`; returns lifecycle and retained evidence paths |
| `logic_fixture_create` | `instance_id`; creates the 32 by 32 fixture once |
| `logic_processor_configure` | `instance_id`, `x`, `y`, `program`, optional `expected_program_digest` |
| `logic_processor_observe` | `instance_id`, `x`, `y`, `memory_index` |
| `runtime_close` | `instance_id`; requests exit and confirms owned process-tree cleanup |
1. Call `runtime_open`, then `logic_fixture_create` with the returned identity.
2. Use the fixture's `result.processorX`, `result.processorY`, and `result.programDigest` to configure the processor with:
```text
write 42 cell1 0
end
```
3. Observe memory index `0` until `result.memoryValue` becomes `42`. Index `1` stays `0`.
4. Replace the program with `write 7 cell1 0\nend`, supplying the previous program digest, and observe `7`.
5. Call `runtime_close` and check `instance.cleanup` is `CLEAN`.
The fixture prepares blocks and links; it does not write the expected memory value. `configure` invokes the native processor handler and `observe` reads live native state on the game thread. `COMPLETED` means a command completed; assertion results are recorded separately by the acceptance runner. This workflow does not exercise mouse or keyboard input.
## Verify
```powershell
./tools/build-bridge.ps1 -Verify
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
$env:MINDUSTRY_MCP_REAL_CONFIG = Join-Path $PWD '.local/config.json'
$env:MINDUSTRY_MCP_EVIDENCE_DIR = Join-Path $PWD 'artifacts/acceptance'
cargo test -p mindustry-mcp-host --locked -- --ignored --nocapture --test-threads=1
cargo test -p mindustry-mcp --test stdio_protocol --locked -- --ignored --nocapture
```
The explicit runtime tests cover the `42` to `7` workflow, digest/target rejection, and closing the MCP connection while a runtime is still open. Four additional native tests cover lost replies after an effect, timeout with a late reply, cancellation after an effect, and duplicate/expired/stale commands. They retain JSON reports, process logs, run manifests, command journals, and content-addressed artifacts under `artifacts/acceptance/`.
The fault tests insert a test-only TCP proxy after the ordinary native handshake. The proxy confirms real game memory is `42` before withholding the mutation result. The host must retain `UNKNOWN_OUTCOME`, refuse further mutations, ignore late completion, and confirm cleanup. Direct native protocol tests verify cached retries preserve the current program/world and rejected commands do not advance the applied sequence. No fault switches or test capabilities are added to the shipped host or bridge.
Ordinary workspace tests do not launch Mindustry; Windows process tests launch isolated test helpers. A protocol test sends a real MCP cancellation notification to a probe handler using the production cancellation helper, verifies its pending future is dropped, and confirms the service still responds. The native cancellation test separately exercises host command cancellation against the real game; these are two boundary tests, rather than a single external-client-to-game cancellation test.
[Windows acceptance](.github/workflows/windows.yml) runs on pushes, pull requests, and manual dispatch. A fresh Windows runner bootstraps the pinned dependencies, runs the Java checks and all Rust/native suites, then retains bounded evidence for 14 days. The workflow has read-only repository permissions and pins its actions by full commit SHA.
## Ownership and limits
Each runtime gets its own working directory, game configuration, input snapshots, and Windows Job Object. Cleanup targets that owned process tree. Bridge credentials remain ephemeral and are omitted from persisted handshakes. An isolated process is not a security sandbox for untrusted native code.
The host allows four active runtimes, sixteen in-flight host calls, one command at a time per runtime, 45 seconds for startup, 10 seconds per bridge command, and a five-minute runtime lifetime. Native frames are limited to 256 KiB, program text to 64 KiB, and each captured output stream to 1 MiB. Retention stops at 1,024 commands per runtime, 128 runs, or 512 MiB/20,000 artifacts per state directory. Archive completed state externally when a limit is reached; there is no automatic evidence deletion.
The state directory has one host writer. Interrupted commands become `UNKNOWN_OUTCOME`; the host freezes further work on that runtime and does not replay or adopt it after restart. Close it and open a fresh runtime. Runtime manifests record engine and bridge hashes plus the Java launcher hash; they do not claim a complete JVM/environment fingerprint.
MCP cancellation notifications stop ordinary request futures. If a mutation was already dispatched, cancellation preserves an unknown outcome instead of asserting that it had no effect. `runtime_close` continues cleanup after a cancellation notification so that an abandoned response does not interrupt process ownership reconciliation.
## Code boundaries
- `crates/core`: contracts and state transitions; no MCP, game, or IO dependencies.
- `crates/host`: lifecycle, commands, persistence, and bridge transport.
- `crates/process`: owned Windows process-tree supervision and bounded logs.
- `crates/server`: rmcp stdio adapter and tool schemas.
- `bridge`: version-specific Java access to Mindustry.
The current provider uses static capabilities and local filesystem storage. More providers can follow the architecture, but their implementations and acceptance evidence are not supplied by this slice.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues