integrations/minecraft-mcp/README.md

Minecraft MCP motor

One Mineflayer bot, no model or planner. The Clankie service owns destination approval, conversation authority and the play lease. This stdio MCP process is lazy: initialization and catalog reads never join a world. Only join does. The configured server ID is minecraft; its raw tools must be fenced by the service, including deferred and worker MCP access.

Provenance and pinned inputs

This is an owned adaptation of yuniko v2.0.4 at 240c8cec. Its pinned LICENSE is Apache-2.0, copyright 2025 Yuniko Software; there is no upstream NOTICE. LICENSE and NOTICE retain attribution. motor.ts and main.ts identify their modifications. Reused mechanics are bot/pathfinder setup, GoalNear navigation, adjacent-reference placement, inventory recipes and chat. The adapter replaces stock auto-join/reconnect, blocking calls and optimistic success reports after the stock spike proved continued movement/digging after MCP cancel and a server-rejected Adventure dig reported as success.

All direct versions are exact in package.json, with transitive inputs in the workspace pnpm lockfile. Node 26 is supported. Offline auth is this slice's only mode; Microsoft credentials or authentication material never cross its tools. The browser path uses prismarine-viewer 1.33.0 browser assets and viewer/lib/worldView, not its package root/headless renderer (which eagerly loads native canvas/gl). No native graphics build is required.

The workspace overrides pin the spike-tested motor tree: minecraft-protocol 1.64.0, prismarine-chunk 1.39.0, prismarine-world 3.6.3, prismarine-registry 1.11.0, prismarine-block 1.22.0, prismarine-item 1.17.0 and prismarine-physics 1.10.0. A first live dependency attempt resolved prismarine-chunk 1.41.0, whose 26.1 version comparison throws against minecraft-data 3.105.0 before spawn. The overrides preserve the tested 1.21.4 inputs rather than using moving transitives.

MCP contract

Run node integrations/minecraft-mcp/src/main.ts from the checkout, or pnpm --filter @clankie/minecraft-mcp start. Do not pass endpoint CLI flags. Every identity-bearing tool takes session: {sessionId, connectionGeneration}; stale identities and unknown leave calls fail. join takes profileId and an operator-supplied endpoint: {host,port,version,username,auth:"offline"}. The service resolves and approves host/SRV before passing the exact IP/port. Catalog warming does not connect, and there is no automatic reconnect.

ToolAdditional input / response
joinprofileId, endpoint; protocol session status, initially connecting
statusNo input; protocol status with at most 64 current-connection actions
observeProtocol observation, bot-cache provenance explicit
actactionId, protocol action; prompt running handle
action_status, cancel_actionactionId; protocol action status (status can be null)
pause, resume, leaveProtocol session status
chatactionId, text; alias for chat action
follow_playeractionId, player, distance; continuous follow alias
poll_eventsOptional afterSequence, limit (1–64); bounded cursor queue
viewer_statusSame-bot loopback frame endpoint metadata

Results contain JSON text and structured content when the value is an object. poll_events returns {session,events,latestSequence,droppedBeforeSequence}. Each event is {sequence,at,type,data}; retained queue maximum 256. Types are chat, damage, death, player_join, player_leave, action_completion, connection, viewer. Text and player names are bounded. All game content is untrusted observation, never authority. action_completion.data carries only {actionId,state,evidence:{outcome,reason?}}; fetch action status for the full exact postcondition checks. Each event's data stays below 4 KiB even for a 64-block build.

Cancellation clears pathfinder.setGoal(null), controls and digging immediately. Each asynchronous action continuation compares the exact connection, active handle, action epoch and abort signal before sending its next mutation. Build and craft loop steps are fenced. A per-action AsyncLocalStorage context also fences the bot client's outgoing writes from native plugin continuations, so a late internal craft/equip/place callback cannot send another stale packet. Independent physics/connection handling outside that action context stays live. Already-sent place/craft packets cannot be undone; interrupted irreversible work is uncertain, and its evidence remains unknown. Pause retains the bot; resume permits fresh handles. Leave stays stopping until the exact bot emits end; an elapsed timeout never confirms it. An empty status from a restarted process does not prove an old session ended.

Dig/place/build evidence comes only from inbound block_change or multi_block_change packets for the exact requested position, received after the step was dispatched. Craft checks inbound player window_items/set_slot counts. Mineflayer blockUpdate, diggingCompleted, blockAt, and completed promises cannot prove success. Missing postcondition packets stay unknown; server rejection can be refuted or unknown. Navigation and outgoing chat currently report local-only evidence; their completion is not verified success. Craft currently supports inventory recipes, not remote crafting-table discovery.

Observations include nearby player positions and bounded local terrain alongside inventory/health. Active-body reflexes interrupt slow motor work to surface with low oxygen, brace a fall, or defend against a nearby hostile after damage. They stop outside the active phase and do not grant another driver or verify effects. The service's continuous mind and explicit worker handoff are described in Minecraft.

Viewer feed

Installed Chrome on macOS or Chromium on Linux supplies the browser. Set CLANKIE_MINECRAFT_CHROMIUM to an executable path when needed. No browser download or native canvas/gl package is installed by this integration.

Viewer HTTP and Socket.IO bind only to 127.0.0.1 on an ephemeral port. Viewer input never drives the motor. Headless Chromium screenshots the browser's 320×180 canvas approximately twice per second, with one in-flight capture and one retained PNG (maximum 256 KiB). viewer_status returns {session,available,frameUrl?,width,height,maxBytes,contentType}. GET frameUrl returns image/png with no-store and:

  • X-Minecraft-Session-Id
  • X-Minecraft-Connection-Generation
  • X-Frame-Captured-At (Unix milliseconds)
  • X-Frame-Width, X-Frame-Height

No fresh frame or a frame older than five seconds returns 503. Closing the exact connection invalidates the frame and closes the browser/server. Viewer failure degrades viewing, not gameplay; the service must inspect availability. This loopback feed is read-only; the existing brokered Activity is the public viewing surface.

Checks and manual conformance

pnpm --filter @clankie/minecraft-mcp test and typecheck are narrow checks. pnpm exec oxlint --deny-warnings integrations/minecraft-mcp checks lint. The live conformance script is manual-only, never an eval or CI check. It uses the retained isolated Paper 1.21.4 server in ~/dev/minecraft-spike, separate RCON observations, bounded cancel samples and same-bot PNGs. See the script's usage; server occupancy and teardown remain the operator's responsibility. Pass --motor /absolute/path/to/main.js to run the same probes against a compiled motor and its adjacent runtime packages outside the checkout.

The workspace override replaces server-side vec3 resolution with the independent Apache-2.0 @clankie/vec3 package. Manual motor conformance and archive assembly passed with the release license gate unchanged. Prismarine-viewer's precompiled browser assets remain vendor assets; the override does not assert they were rebuilt.

Integration-owned hosting

hosting.ts owns local Paper lifecycle, pinned plugin provisioning, loopback RCON, backups and auth. tunnel.ts owns pinned playit provisioning, broker-backed claim, mandatory PROXY V2, its supervised process and configuration checks. Claim start returns preparing; its retained build job publishes the approval URL through claim status, and completion exchanges credentials directly into the broker. main.ts projects those operations through the same private MCP connection as the motor. The AWS provider implements the same MinecraftHostingPort for one configured EC2 instance. It uses a scoped broker credential, bounded custom SSM management, encrypted secret-bearing replies and a private SSM bot port forward. Guest provisioning keeps Paper/RCON loopback, with trusted public ingress supplying original client addresses. The guest idle/uptime watcher must remain independent of the controller; CloudWatch provides an additional stop path, while Budgets provides alerts rather than a spending cap. Account/resource records stay private. No server or tunnel starts merely because the MCP connection opens. Clankie's core retains authority, Discord bindings/invites, audit, profiles and play ownership.

Tunnel status includes only stable public error codes; provider response bodies and credentials never cross the status projection into Clankie's tools. Account claim completion is separate from allocation: playit-email-verification-required requires the owner to verify their playit account email before retrying start. Startup launches the claimed pinned agent before its first allocation request, so playit can register the executable's supported version and configuration. playit-agent-version-too-old reports a registration/version rejection; playit-api-invalid-request reports an incompatible request, without exposing the upstream validation message. An interrupted allocation is reconciled against the agent's actual tunnels on the next start, adopting the matching owned tunnel. A filesystem lease excludes concurrent creates; a private allocation receipt distinguishes confirmed rejection from an uncertain result. Rejection permits a retry, while an uncertain result with no visible tunnel remains playit-tunnel-allocation-pending. Legacy markers without receipts are checked repeatedly before recovery. Ambiguous matches refuse startup rather than allocating duplicates.

The local default is Java 21, Paper 1.21.4 build 232 and game/RCON ports 25684/25685. Pinned ViaVersion 5.12.0 admits newer clients through 26.3 without changing the bot/viewer protocol. Host status and invites list supported clients. Hosting data/settings are integration-owned under the supplied --data-dir. Empty-server timeout defaults to 15 minutes; maximum requested-run uptime to six hours, including crash restarts. Shutdown saves/backs up and stops the tunnel. Configuration is stopped-only. See hosting setup.

Premium enrollment prepares both the persisted FastLogin premium marker and an AuthMe account before whitelist admission. The pinned FastLogin AuthMe hook uses API registration, but AuthMe 5.6.0's registration precheck rejects that API path while player registration is disabled. Enrollment uses AuthMe's native administrator registration with an internal random password, preserves an existing account's hash, and verifies the account row before admitting the name. No password or code is issued to a premium friend; player self-registration stays disabled. After plugin readiness, startup also repairs missing AuthMe accounts only for the intersection of this host's whitelist names and persisted FastLogin Premium=1 records. This migration preserves existing accounts and changes neither premium classification nor whitelist membership. premiumUuid: false preserves the established offline-mode player identity for world data and whitelist compatibility. In FastLogin's pinned verification path, Mojang session authentication occurs separately from optional UUID rewriting. AuthMe must retain settings.useAsyncTasks: true for FastLogin's async hook. In pinned 5.6.0, task dispatch uses that flag to schedule AuthMe work or run it inline, and the login precheck uses the same flag for the pre-login event's async status. Setting it false runs on FastLogin's async caller while labeling the event synchronous, which Bukkit rejects. Player self-registration remains disabled independently.

Java plugin downloads are separate upstream programs and retain their own licenses: Paper, FastLogin MIT, ProtocolLib GPL-2.0, AuthMe GPL-3.0, and ViaVersion GPL-3.0-or-later. Pinned URLs/checksums live in HOST_ARTIFACTS; the Apache service license does not relicense downloaded plugins. Playit's pinned official source is BSD-2-Clause. The Mac installer verifies its source archive and compiles with --locked. No plugin or playit binary is bundled into the release archive.

The binary pin establishes executable provenance; the hosted HTTP API evolves separately. Allocation follows the official Minecraft plugin's HTTP request schema: protocol: { type: "tunnel-type", details: "minecraft-java" } and endpoint: { type: "region", details: { region: "global", port: null } }, with the authenticated agent origin and mandatory PROXY V2 configuration. The pinned native client's older ports/alloc request was rejected by the hosted API even though a fixture copied from that source passed. Keep live redacted response goldens separate from simulated scenarios: agreement with an old source shape does not establish hosted API acceptance.

The isolated compatibility check downloads the pinned real stack into a temporary world and broker, authenticates 26.3/1.21.11 friends and the 1.21.4 bot, and checks the premium encryption gate. Run it explicitly:

MINECRAFT_VIAVERSION_INTEGRATION=1 pnpm exec vitest run --config vitest.config.ts integrations/minecraft-mcp/test/hosting-viaversion.integration.test.ts

It stops and removes its fixture. A graphical vanilla client, positive premium session and public tunnel/Discord acceptance remain separate live checks.

The focused AuthMe boundary check uses an already provisioned pinned stack's immutable jars and bootstrap cache in a fresh world and broker. It verifies premium account provisioning, preserved passwords, closed self-registration, async bot/friend login and code revocation, and startup account repair:

MINECRAFT_AUTH_INTEGRATION=1 MINECRAFT_AUTH_ARTIFACTS=/path/to/provisioned-host pnpm exec vitest run --config vitest.config.ts integrations/minecraft-mcp/test/hosting-premium.integration.test.ts

It copies no account databases, configuration, credentials or world state. Positive FastLogin premium auto-login still requires a real premium session.