Skip to main content
Glama
vjh0107
by vjh0107
README.md
# mc-mcp-server

An MCP server that drives Mineflayer bots, so an AI agent can exercise a Minecraft server directly: join a bot, run commands, open the menus a plugin draws, click their buttons, and read what the server puts on screen.

It runs as a long-lived container and speaks Streamable HTTP, so one deployment serves several agents at once. Bot lifetime is separate from MCP session lifetime, so a bot outlives the session that created it.

> **Local development only.** The bots authenticate offline, so the target server has to run offline-mode.

## Install

```bash
helm upgrade -i mc-mcp-server oci://junhyung.cloud/library/charts/mc-mcp-server \
  --version <tag> \
  --namespace mcp --create-namespace
```

The chart version and the image tag are the same value, so leaving `image.tag` unset runs the
matching image. Tags read `<version>-<UTC stamp>.g<commit>`; [CHANGELOG.md](CHANGELOG.md) says what
each version carried. Every value is documented in the [chart README](charts/mc-mcp-server/README.md).

The chart makes an auth token on first install and keeps it across upgrades. Read it back and point a client at the endpoint:

```bash
TOKEN=$(kubectl -n mcp get secret mc-mcp-server-auth -o jsonpath='{.data.token}' | base64 -d)
claude mcp add --transport http mc-mcp-server http://mc-mcp-server.mcp.svc/mcp \
  -H "Authorization: Bearer $TOKEN"
```

Keeping the token depends on Helm reading the Secret back from a live cluster. ArgoCD and anything else that renders first and applies later gets an empty read and a new token every sync, so pass `auth.existingSecret` there.

## Configuration

Environment variables are the defaults; the matching flag overrides one.

| Environment | Flag | Default | Meaning |
| --- | --- | --- | --- |
| `MCP_TRANSPORT` | `--transport` | `http` | `http` or `stdio` |
| `MCP_BIND_HOST` | `--bind-host` | `0.0.0.0` | Address the HTTP server binds to |
| `MCP_PORT` | `--port` | `3000` | HTTP port |
| `MCP_PATH` | `--mcp-path` | `/mcp` | Path the MCP endpoint is served at |
| `MCP_AUTH_TOKEN` | `--auth-token` | none | Shared secret checked as `Authorization: Bearer`. Unset leaves the endpoint open. |
| `SA_AUTH_ENABLED` | `--sa-auth` | `false` | Also accept Kubernetes service account tokens, checked with a TokenReview |
| `SA_AUTH_ALLOWED` | `--allowed-service-accounts` | none | Comma separated `namespace:name`. Empty admits any authenticated one. |
| `MCP_LOG_LEVEL` | `--log-level` | `info` | `debug` / `info` / `warn` / `error` |
| `POD_NAME` | `--instance` | hostname | Name reported in logs and metrics |
| `SHUTDOWN_READINESS_GRACE_SECONDS` | `--readiness-grace` | `5` | Keep serving after SIGTERM so endpoints drop this pod |
| `SHUTDOWN_DRAIN_TIMEOUT_SECONDS` | `--drain-timeout` | `30` | How long to wait for in-flight tool calls |
| `MC_VERSION` | `--mc-version` | none | Pin the protocol version instead of auto-detecting |
| `MC_USERNAME_PREFIX` | `--username-prefix` | `mcp` | Prefix for generated bot usernames |
| `BOT_MAX` | `--max-bots` | `8` | How many bots may be connected at once |
| `BOT_IDLE_TIMEOUT_SECONDS` | `--idle-timeout` | `1800` | Idle seconds before a bot leaves; `0` disables it |

`POST <mcp-path>` is MCP. `GET /healthz` and `/readyz` are the probes, `/metrics` serves Prometheus, and `/debug/bots` and `/whoami` sit behind the same authentication as MCP.

## Tools

<!-- BEGIN GENERATED TOOLS -->

60 tools in total.

**Sessions**: `join-server`, `leave-server`, `list-bots`, `get-bot-status`

**Server checks**: `ping-server`

**Server interaction**: `run-command`, `switch-server`, `wait-for-chat`, `wait-ticks`, `detect-gamemode`, `complete-command`, `get-world-state`

**Movement**: `get-position`, `move-to-position`, `look-at`, `jump`, `move-in-direction`, `fly-to`, `set-stance`

**World interaction**: `activate-block`, `interact-entity`, `attack-entity`, `use-held-item`, `fish`

**GUI windows**: `wait-for-window`, `read-window`, `close-window`

**Slots and containers**: `click-slot`, `open-container`, `drop-held-item`

**Inventory**: `list-inventory`, `find-item`, `equip-item`, `give-item`

**Blocks**: `get-block-info`, `find-blocks`, `dig-block`, `place-block`, `read-block-entity`

**Entities**: `read-displays`, `find-entity`

**Chat**: `send-chat`, `read-chat`

**HUD**: `read-scoreboard`, `read-boss-bars`, `read-player-list`, `read-action-bar`, `wait-for-action-bar`, `read-title`, `wait-for-title`, `read-dialog`, `wait-for-dialog`, `get-player-state`

**Sounds and particles**: `read-effects`, `wait-for-effect`

**Crafting**: `list-recipes`, `get-recipe`, `can-craft`, `craft-item`

**Smelting**: `smelt-item`

<!-- END GENERATED TOOLS -->

Every argument is listed in [docs/tools.md](docs/tools.md). Both are generated by `pnpm docs:tools`; do not edit them by hand.

`join-server` takes the server address every time, so a call record says which server it touched.

The GUI window tools are what exercise menu-driven features: wait for the menu, read its slots
including custom names and lore, click a button. The reading tools cover the rest of what a player
sees -- the scoreboard, boss bars, the action bar, titles, dialogs, the text floating in the world,
and the sounds and particles that never become words. A HUD drawn in custom fonts arrives as
segments joined with ` | `, each tagged with its font, rather than as one run-together string.

## Development

```bash
docker compose -f dev/compose.yml up -d   # offline-mode server on 25577
pnpm install
pnpm dev
```

The server carries no default Minecraft address; `join-server` names one on every call, so point a
bot at `127.0.0.1:25577` once it is up. See [dev/README.md](dev/README.md) for the test server.

`make check` runs typecheck, lint, tests, the version check, and fails if the generated tool docs
drifted.

## Notes

[docs/protocol-notes.md](docs/protocol-notes.md) records where this server works around mineflayer or the 26.1 protocol, and the known limits, with the measurements behind each.

Images and charts are built and signed by GitHub Actions with cosign, keyless:

```bash
cosign verify junhyung.cloud/library/mc-mcp-server:<tag> \
  --certificate-identity-regexp '^https://github.com/vjh0107/mc-mcp-server/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
```

## License

MIT