Skip to main content
Glama
scotteratigan

minecraft-rcon-mcp

README.md
# minecraft-rcon-mcp

An [MCP](https://modelcontextprotocol.io) server that exposes a Minecraft Java
Edition server over [RCON](https://minecraft.wiki/w/RCON) as a tool, plus an
optional in-game **AI chat listener**: players type `ai <question>` in chat and
Claude answers in-game, using RCON to read live world state.

It is configured entirely through environment variables, so it is independent of
any particular server, world, or Minecraft version.

## Features

- **`run_command` tool** — run any Minecraft server command via RCON and return
  the response. Use it from any MCP client (Claude Code, VS Code / Copilot, etc.)
  to inspect or modify the live world.
- **In-game AI chat** — a background thread tails the server log; chat messages
  prefixed with a configurable trigger (`ai ` by default) are answered by Claude
  via the Anthropic API, with an agentic RCON tool-use loop and a rolling context
  window. Responses are posted back with `/tellraw`.
- **Persistent memory** — the in-game agent reads two git-tracked memory files
  into its context each request and can append to them with a `remember` tool,
  so it accumulates knowledge across sessions instead of relying only on the
  rolling chat window. See [Persistent memory](#persistent-memory).
- **`get_ai_chat_status` tool** — report listener health, history size, model,
  log path, and memory configuration.

## Requirements

- Python 3.14+
- A Minecraft server with RCON enabled (`enable-rcon=true` in `server.properties`)
- An Anthropic API key (only if you use the in-game AI chat feature)

## Install

```bash
pip install git+https://github.com/scotteratigan/minecraft-rcon-mcp
```

Or, for local development / use from a sibling repo:

```bash
pip install -e path/to/minecraft-rcon-mcp
```

## Run

```bash
minecraft-rcon-mcp          # console entry point
python -m minecraft_rcon_mcp   # equivalent
```

The server speaks MCP over stdio, so it is normally launched by an MCP client
rather than by hand. Example client config (`.vscode/mcp.json`):

```json
{
  "servers": {
    "minecraft-rcon": {
      "type": "stdio",
      "command": "/path/to/.venv/Scripts/python.exe",
      "args": ["-m", "minecraft_rcon_mcp"],
      "env": {
        "RCON_HOST": "localhost",
        "RCON_PORT": "25575",
        "RCON_PASSWORD": "your-rcon-password",
        "LOG_PATH": "/path/to/server/logs/latest.log"
      }
    }
  }
}
```

Provide `ANTHROPIC_API_KEY` in the launching environment (not in committed files)
if you want the in-game AI chat.

## Configuration

| Variable | Default | Purpose |
|---|---|---|
| `RCON_HOST` | `localhost` | RCON host |
| `RCON_PORT` | `25575` | RCON port |
| `RCON_PASSWORD` | `changeme` | RCON password (match `server.properties`) |
| `LOG_PATH` | `./logs/latest.log` | Path to the server log to tail. **Set this explicitly** — the default is relative to the working directory. |
| `AI_CHAT_ENABLED` | `1` | Set `0`/`false` to run a pure RCON tool server (no log tailing, no Anthropic usage). |
| `AI_PREFIX` | `ai ` | Chat trigger prefix (case-insensitive). |
| `AI_MODEL` | `claude-sonnet-4-5` | Anthropic model for in-game chat. |
| `AI_MAX_CONTEXT` | `10` | Exchanges retained in the rolling history. |
| `AI_MAX_TOKENS` | `1024` | Max tokens per chat response. |
| `AI_SYSTEM_PROMPT` | *(generic built-in)* | Replace the entire system prompt. |
| `AI_SYSTEM_PROMPT_EXTRA` | *(none)* | Append server-specific context (Minecraft version, house rules) to the default prompt. |
| `AI_MEMORY_ENABLED` | `1` | Set `0`/`false` to disable persistent memory (no files read or written). |
| `SERVER_MEMORY_PATH` | `./agent_memory.md` | Path to the **server-specific** memory file (world/player facts). **Set this explicitly** to a file in your server repo so it is git-tracked there, just like `LOG_PATH`. |
| `CAPABILITY_MEMORY_PATH` | *(packaged `agent_memory/capabilities.md`)* | Path to the **server-agnostic** capability/script memory. Defaults to a file shipped inside this package; override only if you want it elsewhere. |
| `WORLD_DIR` | — | World save folder (the one with `level.dat`). Required by the file-reading and structure-building scripts run via `run_script` (it can't be derived over RCON). |
| `STRUCTURES_DIR` | — | Folder of captured `.nbt` templates. Lets `run_script` builders/capture refer to structures by bare name (e.g. `generate_monument` → `ocean_monument`). |
| `MINECRAFT_JAR` | *(auto: `versions/*/server-*.jar`)* | Server jar read for the vanilla structure catalog/sizing (`generate_structure`/`generate_piece`). Set explicitly when the working dir isn't near the jar. |
| `ANTHROPIC_API_KEY` | — | Required for in-game AI chat. |

The `run_command` + `remember` tools are always available. Two more — `list_scripts`
and `run_script` — dispatch the Python toolbox (structure builders, world queries)
without one MCP tool per script; see [Scripts & recipes](#scripts--recipes). They're
also exposed to the in-game chat agent, so players can ask it to build things.

## Persistent memory

The in-game AI agent keeps **two separate memory stores**, both intended to be
version-controlled, so it accumulates knowledge across sessions. The split is
deliberate — each store lives in a different repo:

| Scope | What goes in it | Where it lives | Written via |
|---|---|---|---|
| `server` | Facts specific to **this** server, world, or its players — base/build coordinates, player preferences, house rules, world boundaries, ongoing projects. | The **consumer (server) repo**, at `SERVER_MEMORY_PATH`. Committed there. | `remember(scope="server")` |
| `capability` | **Server-agnostic** knowledge about this MCP — useful command patterns, what a script does, gotchas, techniques that help on any server. | **This package**, at `CAPABILITY_MEMORY_PATH` (defaults to [`agent_memory/capabilities.md`](src/minecraft_rcon_mcp/agent_memory/capabilities.md)). Committed here. | `remember(scope="capability")` |

**How it works:**

- **Read** — on every chat request, both files are read fresh and appended to the
  system prompt under a `# Persistent memory` heading. Editing a file (by hand or
  by the agent) takes effect on the next message; no restart needed.
- **Write** — the agent has a `remember` tool that takes `content` and a
  `scope` (`server` | `capability`) and appends a one-line bullet to the matching
  file, creating it with a header if absent.

Because the capability store defaults to a path **inside this package**, the
agent writes into it in place. That is committable when the package is installed
**editable** (`pip install -e`), which is the intended development setup; under a
plain wheel install it would write into `site-packages` instead, so set
`CAPABILITY_MEMORY_PATH` to a writable, tracked location if you deploy that way.

Disable the whole feature with `AI_MEMORY_ENABLED=0`.

## Scripts & recipes

The package bundles server-agnostic tooling split into two layers. They live here
(not in any one server repo) because they are reusable *capabilities*. The aim is
a toolbox of small, orthogonal **primitives** that compose efficiently, plus a
few **recipes** that show how to combine them for common tasks.

> **Writing one?** See [`CLAUDE.md`](CLAUDE.md) for the authoring rules
> (primitive vs. recipe, server-agnostic requirements, shared helpers, quality
> gates).

### Primitives — [`minecraft_rcon_mcp.scripts`](src/minecraft_rcon_mcp/scripts/)

Low-level building blocks, each doing one general thing:

| Primitive | Purpose | Needs world files? |
|---|---|---|
| `rcon` | Shared RCON client + CLI for running any command. | No (RCON only) |
| `count_entities` | Run an entity selector and return the parsed count. | No (RCON only) |
| `find_block_entities` | Scan region files for block entities (spawners, chests…). | Yes |
| `check_chunk_generated` | Whether given coords are in generated chunks. | Yes |
| `find_unexplored_edges` | Nearest ungenerated region frontier to a position. | Yes |

```bash
python -m minecraft_rcon_mcp.scripts.rcon "list"
python -m minecraft_rcon_mcp.scripts.count_entities "type=minecraft:cat,distance=..48" --at Steve
python -m minecraft_rcon_mcp.scripts.find_block_entities --x 0 --z 0 --block-id minecraft:chest
python -m minecraft_rcon_mcp.scripts.check_chunk_generated 1216 237 --dimension the_nether
python -m minecraft_rcon_mcp.scripts.find_unexplored_edges 120 45
```

### Recipes — [`minecraft_rcon_mcp.recipes`](src/minecraft_rcon_mcp/recipes/)

Higher-level, task-specific tools composed from the primitives (and sometimes
coupled to a particular Minecraft version's mechanics). Each is also a worked
example of composition:

| Recipe | Composed from | Notes |
|---|---|---|
| `cat_spawn_check <player>` | `count_entities` | Village cat-spawn gates; thresholds are version-coupled (26.x). |
| `reset_trial_spawners [player]` | `find_block_entities` + `rcon` | Finds trial spawners near a player, resets cooldowns. |

```bash
python -m minecraft_rcon_mcp.recipes.cat_spawn_check Steve
python -m minecraft_rcon_mcp.recipes.reset_trial_spawners
```

### Configuration

- **RCON** — read from `RCON_HOST` / `RCON_PORT` / `RCON_PASSWORD`. As a fallback,
  `rcon.py` reads `rcon.port` / `rcon.password` from a `server.properties` in the
  working directory (or `SERVER_PROPERTIES`).
- **`WORLD_DIR`** — tools that read save files need the world folder (the one with
  `level.dat` / `dimensions/`). This **cannot be derived over RCON** (no vanilla
  command exposes the save path), so set `WORLD_DIR`, or pass `--world-dir`.
- **`--dimension`** — region-reading tools default to the overworld; pass
  `the_nether` / `the_end` / a namespaced id (`mymod:custom`) for others. Both
  vanilla (`DIM-1`/`DIM1`) and data-driven (`dimensions/<ns>/<name>/`) layouts
  resolve automatically.

> The world data itself lives in the server deployment, not in this repo — these
> tools only carry the *logic*, and reach the world through `WORLD_DIR`.

## Development

This project uses [uv](https://docs.astral.sh/uv/) for environment and
dependency management, [Ruff](https://docs.astral.sh/ruff/) for linting and
formatting, and [ty](https://github.com/astral-sh/ty) for static type checking.
The pinned Python version lives in `.python-version`; uv installs it for you.

```bash
uv sync                 # create .venv and install all deps (incl. dev tools)

uv run pytest           # run the test suite
uv run ruff format      # auto-format
uv run ruff check       # lint (add --fix to auto-fix)
uv run ty check         # type-check
```

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.6/5.0

Scored across 2 tools

Disambiguation5/5

The two tools, get_ai_chat_status and run_command, have clear and distinct purposes. One retrieves chat status, the other executes arbitrary commands, leaving no ambiguity.

Naming Consistency5/5

Both tool names follow a consistent verb_noun pattern (get_ai_chat_status, run_command), making them predictable and easy to differentiate.

Tool Count3/5

With only 2 tools, the server is minimally functional. While run_command covers core RCON operations, the additional AI chat tool is specific; the count is low but acceptable for a narrow purpose.

Completeness3/5

The server provides basic RCON command execution and AI chat status, but lacks tools for managing AI chat (e.g., enable/disable) or more granular server interactions, leaving notable gaps.

Maintenance

ActivityStale
ResponsivenessNo issues