{"_id":"@adhisang/minecraft-blockbench-mcp","_rev":"2-043bd5da98fd1c707be1fcb66969c54c","name":"@adhisang/minecraft-blockbench-mcp","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@adhisang/minecraft-blockbench-mcp","version":"0.1.0","keywords":["blockbench","mcp","mcp-server","minecraft","model-context-protocol"],"author":{"name":"adhi-jp"},"license":"MIT","_id":"@adhisang/minecraft-blockbench-mcp@0.1.0","maintainers":[{"name":"adhi-jp","email":"dev+npm@adhi.jp"}],"homepage":"https://github.com/adhi-jp/minecraft-blockbench-mcp#readme","bugs":{"url":"https://github.com/adhi-jp/minecraft-blockbench-mcp/issues"},"bin":{"minecraft-blockbench-mcp":"dist/adapter/cli.js"},"dist":{"shasum":"86a4c0766e213f4ff34cafe977f81761af6b13e6","tarball":"https://registry.npmjs.org/@adhisang/minecraft-blockbench-mcp/-/minecraft-blockbench-mcp-0.1.0.tgz","fileCount":19,"integrity":"sha512-rs7SdBFYXFzJvAYULmppxyE0Gg/acOiWBzyNYlWQ6JyGgBCGO9Qoe33PAcgJrRxvoGtq+iUrnAwEGCI3JajXEg==","signatures":[{"sig":"MEYCIQD4PyIkPXZxg9gzcBdUs+Rcrz2hSuhs/u8e5XduZReKNAIhALrNLNquuiMaB14maFpYoxxfaDy/nQNIPv3uiXSuchct","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":484309},"type":"module","engines":{"node":">=22"},"gitHead":"e1f619627b479ae05b7ec896b7327dc768d3aa04","scripts":{"dev":"tsx src/adapter/cli.ts","test":"node --test --import tsx tests/*.test.ts","build":"npm run clean && npm run build:adapter && npm run build:plugin","check":"tsc -p tsconfig.adapter.json --noEmit && tsc -p tsconfig.plugin.json --noEmit","clean":"node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true });\"","setup":"node scripts/setup-wrapper.mjs","start":"node dist/adapter/cli.js","prepack":"npm run build","build:plugin":"node scripts/build-plugin.mjs","test:package":"node scripts/verify-package.mjs","build:adapter":"tsc -p tsconfig.adapter.json","test:coverage":"node --test --import tsx --experimental-test-coverage --test-coverage-lines=80 --test-coverage-branches=70 --test-coverage-functions=80 tests/*.test.ts","test:coverage:lcov":"node --input-type=module -e \"import { mkdirSync } from 'node:fs'; mkdirSync('coverage', { recursive: true });\" && node --test --import tsx --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info --test-coverage-lines=80 --test-coverage-branches=70 --test-coverage-functions=80 tests/*.test.ts","smoke:geckolib-live":"node scripts/smoke-geckolib-animation-frame.mjs"},"_npmUser":{"name":"adhi-jp","email":"dev+npm@adhi.jp"},"repository":{"url":"git+https://github.com/adhi-jp/minecraft-blockbench-mcp.git","type":"git"},"_npmVersion":"11.11.0","description":"MCP integration for Blockbench: a Claude Code-launched stdio MCP adapter plus a Blockbench desktop plugin for AI-assisted Minecraft Java block/item model editing.","directories":{},"_nodeVersion":"24.13.0","dependencies":{"ws":"^8.18.0","zod":"^3.25.0","@modelcontextprotocol/sdk":"^1.29.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","esbuild":"^0.25.0","@types/ws":"^8.18.0","typescript":"^5.8.0","@types/node":"^22.14.0"},"_npmOperationalInternal":{"tmp":"tmp/minecraft-blockbench-mcp_0.1.0_1785588349681_0.49534810359506976","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@adhisang/minecraft-blockbench-mcp@0.3.0","bin":{"minecraft-blockbench-mcp":"dist/adapter/cli.js"},"bugs":{"url":"https://github.com/adhi-jp/minecraft-blockbench-mcp/issues"},"dist":{"shasum":"3a1ef990cce01791c6e80d346b6525da30b9e380","tarball":"https://registry.npmjs.org/@adhisang/minecraft-blockbench-mcp/-/minecraft-blockbench-mcp-0.3.0.tgz","fileCount":29,"integrity":"sha512-VFUy5gk0MdkQOpq+ibK0YuErgKbsEqXFzXlktyJEcpM/vspP49r9n0xMvJodO9CjK39Jf/SiN4SGRub5fV7h3Q==","signatures":[{"sig":"MEUCIQDfecf8mNKAn/jcUrxe4FgRxLVHnNIMCDEgoSPGzMSBFwIgXk6AR3ZELUQc9JLhDFYVladEwKiSKJkOP0LM6esYoGA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGlBxA5lraVuKOnt5Kyia/jX8WTazMXtP0NoRLtqqjskAiBNtqtvgcH+6TXepwEX7pYxuTDfrbZ7jgCXrMCZsJWwSw=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@adhisang%2fminecraft-blockbench-mcp@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1081740},"name":"@adhisang/minecraft-blockbench-mcp","type":"module","author":{"name":"adhi-jp"},"engines":{"node":">=22"},"gitHead":"bb84090ac659a79bff2276c57fe70d1870432b23","license":"MIT","scripts":{"dev":"tsx src/adapter/cli.ts","test":"node --test --import tsx tests/*.test.ts","build":"npm run clean && npm run build:adapter && npm run build:plugin","check":"tsc -p tsconfig.adapter.json --noEmit && tsc -p tsconfig.plugin.json --noEmit","clean":"node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true });\"","setup":"node scripts/setup-wrapper.mjs","start":"node dist/adapter/cli.js","prepack":"npm run build","build:plugin":"node scripts/build-plugin.mjs","test:package":"node scripts/verify-package.mjs","build:adapter":"tsc -p tsconfig.adapter.json","test:coverage":"node --test --import tsx --experimental-test-coverage --test-coverage-lines=80 --test-coverage-branches=70 --test-coverage-functions=80 tests/*.test.ts","test:coverage:lcov":"node --input-type=module -e \"import { mkdirSync } from 'node:fs'; mkdirSync('coverage', { recursive: true });\" && node --test --import tsx --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info --test-coverage-lines=80 --test-coverage-branches=70 --test-coverage-functions=80 tests/*.test.ts","smoke:geckolib-live":"node scripts/smoke-geckolib-animation-frame.mjs","smoke:scope-isolation-live":"node scripts/smoke-scope-isolation.mjs"},"version":"0.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0fb3972e-f9ef-4e2c-944d-6d0b204ba528"}},"homepage":"https://github.com/adhi-jp/minecraft-blockbench-mcp#readme","keywords":["blockbench","mcp","mcp-server","minecraft","model-context-protocol"],"repository":{"url":"git+https://github.com/adhi-jp/minecraft-blockbench-mcp.git","type":"git"},"_npmVersion":"11.5.2","description":"MCP integration for Blockbench: a Claude Code-launched stdio MCP adapter plus a Blockbench desktop plugin for AI-assisted Minecraft Java block/item model editing.","directories":{},"maintainers":[{"name":"adhi-jp","email":"dev+npm@adhi.jp"}],"_nodeVersion":"22.23.2","dependencies":{"ws":"^8.18.0","zod":"4.4.3","@modelcontextprotocol/client":"2.0.0","@modelcontextprotocol/server":"2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","esbuild":"^0.25.0","@types/ws":"^8.18.0","typescript":"^5.8.0","@types/node":"^22.14.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/minecraft-blockbench-mcp_0.3.0_1790603556962_0.8734918863506755"}}},"time":{"created":"2026-08-01T12:45:49.444Z","modified":"2026-09-28T13:52:37.486Z","0.1.0":"2026-08-01T12:45:49.851Z","0.3.0":"2026-09-28T13:52:37.086Z"},"bugs":{"url":"https://github.com/adhi-jp/minecraft-blockbench-mcp/issues"},"author":{"name":"adhi-jp"},"license":"MIT","homepage":"https://github.com/adhi-jp/minecraft-blockbench-mcp#readme","keywords":["blockbench","mcp","mcp-server","minecraft","model-context-protocol"],"repository":{"url":"git+https://github.com/adhi-jp/minecraft-blockbench-mcp.git","type":"git"},"description":"MCP integration for Blockbench: a Claude Code-launched stdio MCP adapter plus a Blockbench desktop plugin for AI-assisted Minecraft Java block/item model editing.","maintainers":[{"name":"adhi-jp","email":"dev+npm@adhi.jp"}],"readme":"# @adhisang/minecraft-blockbench-mcp\n\nMCP integration for [Blockbench](https://www.blockbench.net/): a Claude Code-launched\n**stdio MCP adapter** plus a **Blockbench desktop plugin**, connected over a\nloopback WebSocket, so AI clients can create and edit Minecraft Java\nblock/item models (`java_block` format) and GeckoLib animated models\n(`geckolib_model` format, via the third-party GeckoLib plugin) through\nBlockbench itself.\n\n```\nClaude Code ──(stdio MCP)── adapter process ──(ws://127.0.0.1:39731)── Blockbench plugin\n                            │ owns McpServer,                          │ executes/rejects every\n                            │ schema validation,                       │ operation through\n                            │ relay only                               │ Blockbench APIs\n```\n\nThe adapter is only a compatibility shim: it never edits model files itself.\nEvery operation is executed (or rejected) by the plugin inside Blockbench,\nwith undo entries and viewport refreshes.\n\nThe adapter speaks MCP revision `2026-07-28` and continues to support\n`2025-11-25`, `2025-06-18`, `2025-03-26`, `2024-11-05`, and `2024-10-07`. The\nrevision is settled per connection, so a client on the newest revision and a\nclient on an older one both work against the same installed adapter. There is\nno protocol setting to pick, and an existing MCP registration keeps working\nunchanged.\n\n## Package installation\n\nThe npm package contains both the stdio adapter and the compiled Blockbench\ndesktop plugin. Install it in a persistent directory so Blockbench can continue\nloading the same plugin file after npm exits:\n\n```sh\nmkdir minecraft-blockbench-mcp\ncd minecraft-blockbench-mcp\nnpm init -y\nnpm install @adhisang/minecraft-blockbench-mcp\n```\n\nThe two package entry points are then:\n\n- Adapter: `node_modules/@adhisang/minecraft-blockbench-mcp/dist/adapter/cli.js`\n- Blockbench plugin: `node_modules/@adhisang/minecraft-blockbench-mcp/dist/plugin/minecraft_blockbench_mcp.js`\n\nUse absolute paths when registering the adapter and loading the plugin. To\nupgrade both components together, run `npm update @adhisang/minecraft-blockbench-mcp`\nin the installation directory, then reload the plugin in Blockbench.\n\nFor adapter-only experiments, the package also exposes the\n`minecraft-blockbench-mcp` executable and can be started with:\n\n```sh\nnpx -y @adhisang/minecraft-blockbench-mcp\n```\n\nThe adapter does not install or launch Blockbench and does not automatically\nload the desktop plugin.\n\n## Development prerequisites\n\n- Node.js >= 22\n- Blockbench 5.1.4+ **desktop** (this repository expects its source checkout at\n  `external/blockbench` for TypeScript types)\n- One-time type generation for the plugin typecheck:\n\n```sh\ncd external/blockbench\nnpm run generate-types\n```\n\n## Development build\n\n```sh\nnpm install\nnpm run check   # typecheck adapter + plugin\nnpm run build   # emits dist/adapter/** and dist/plugin/minecraft_blockbench_mcp.js\nnpm test        # protocol, scope-safety, bridge, stdio E2E, plugin-session suites\n```\n\n## Setup\n\n### Guided setup (recommended)\n\n```sh\nnpx minecraft-blockbench-mcp setup   # inside the persistent npm installation directory\nnpm run setup                        # in a git checkout, after npm run build\n```\n\nThe `setup` command generates a shared secret, stores it in a per-user config\nfile readable only by your user (Linux/WSL:\n`${XDG_CONFIG_HOME:-~/.config}/minecraft-blockbench-mcp/config.json`, macOS:\n`~/Library/Application Support/minecraft-blockbench-mcp/config.json`, Windows:\n`%APPDATA%\\minecraft-blockbench-mcp\\config.json`), registers the `blockbench`\nMCP server in Claude Code (default `--scope project`; the registration stores\nonly the config file path, never the secret), and prints the remaining\nBlockbench steps. Under WSL the plugin path is also printed in Windows notation\nfor a Windows-native Blockbench. The secret is never shown unless you pass\n`--show-secret`, or `--clipboard` to copy it without displaying it.\n\nFlags: `--scope project|user|local`, `--port <n>`, `--replace` (replace an\nexisting `blockbench` registration — never done silently), `--rotate-secret`,\n`--show-secret`, `--clipboard`, `--wait <seconds>` (keep the adapter up and\nreport the moment the plugin connects), `--uninstall` (remove the registration\nand config file; the Blockbench-side plugin stays installed).\n\nThe plugin reads that same config file directly. When Blockbench runs on the\nsame system, load the plugin and approve the one-time file-access permission\n(\"Always allow for this plugin\") — no port or secret needs to be typed, and a\nlater `setup --rotate-secret` is picked up automatically by the plugin. A\nrunning adapter keeps the secret it started with, so the adapter (or the\nClaude Code session that launched it) must be restarted for a rotated secret\nto take effect. When Blockbench runs on Windows against a WSL adapter, set the\nplugin's \"MCP Config File Path\" setting to the Windows-notation path that\n`setup` prints (or pick the file via\n**Tools → Locate MCP Config File**). Entering \"MCP Adapter Port\" and \"MCP\nShared Secret\" manually keeps working and takes precedence over the file. The\nadapter itself also picks the config file up automatically when started with\nno `--config`/`BLOCKBENCH_MCP_CONFIG` at all.\n\nRe-running `setup` with nothing to change is a no-op. `minecraft-blockbench-mcp\ndoctor` diagnoses the current state without changing anything: when no config\nfile is resolved it reports `Not configured`; otherwise it prints the resolved\nconfig file, the Claude Code registration, and one of four adapter states\n(broken with remediation, port already held — usually your registered adapter\nrunning, waiting for Blockbench, or fully connected).\n\n`setup --full-auto` goes one step further on Linux and Windows (including a\nWindows Blockbench driven from a WSL adapter): it launches Blockbench once\nwith a loopback-only DevTools port, installs the plugin and applies the\nconnection settings through Blockbench's own APIs, and reports the connected\nstate — zero clicks inside Blockbench. The binary is auto-detected\n(`--blockbench-path <path>` overrides); a Blockbench that is already running\naborts the run with guidance instead of being touched. Note: the DevTools port\nstays open until that Blockbench instance exits — restart Blockbench after\nprovisioning to close it. On unsupported platforms (currently macOS) the\nnormal setup completes and the manual steps are printed instead.\n\nRunning `setup` from the npx cache (outside a persistent installation\ndirectory) is refused, because Blockbench keeps loading the plugin from its\noriginal path across restarts.\n\n### Manual setup\n\n#### 1. Choose a shared secret\n\nThe adapter refuses plugin connections until a secret is configured, and the\nplugin refuses to connect until the same secret is entered in its settings.\nPick any random string (for example `openssl rand -hex 16`).\n\n#### 2. Register the adapter in Claude Code\n\nFor an npm installation, replace the example adapter path below with the\nabsolute path under the persistent installation directory described in\n[Package installation](#package-installation).\n\n```sh\nclaude mcp add blockbench -e BLOCKBENCH_MCP_SECRET=<your-secret> -- node /path/to/minecraft-blockbench-mcp/dist/adapter/cli.js\n```\n\nConfiguration precedence: CLI arguments > environment variables > JSON config\nfile > defaults.\n\n| Setting | CLI | Environment | Default |\n| --- | --- | --- | --- |\n| WebSocket port | `--port` | `BLOCKBENCH_MCP_PORT` | `39731` |\n| Shared secret | `--secret` | `BLOCKBENCH_MCP_SECRET` | (unset — required) |\n| Config file path | `--config` | `BLOCKBENCH_MCP_CONFIG` | (none) |\n| Request timeout (ms) | `--request-timeout-ms` | `BLOCKBENCH_MCP_REQUEST_TIMEOUT_MS` | `30000` |\n\nThe optional config file is a JSON object with keys `port`, `secret`,\n`requestTimeoutMs`, `heartbeatIntervalMs`, `heartbeatMissLimit`,\n`handshakeTimeoutMs`, `maxMessageBytes`.\n\n### Brokered and direct adapter modes\n\nThese modes decide only how the adapter reaches Blockbench. The AI client\ntalks to the adapter over the same stdio MCP connection either way.\n\nOn Linux and WSL the adapter uses brokered mode by default, allowing multiple\nAI clients configured with the same config file to share one Blockbench\nconnection. Native Windows uses direct mode by default. Select direct mode\nexplicitly with `--direct` or `BLOCKBENCH_MCP_DIRECT=1`; select brokered mode\nwith `--broker` or `BLOCKBENCH_MCP_BROKER=1`.\n\nBy default, clients share a broker whenever they use the same config file,\nwhatever else their environments contain. The broker keeps its socket and lock\nfiles in `run/` next to that config file; if the socket path is too long for\nthe platform, set `BLOCKBENCH_MCP_RUNTIME_DIR` to a short absolute path to move\nthem, and give every client the same value.\n\nBrokered mode also recognizes the JSON config keys `brokerIdleTimeoutMs` (how\nlong the shared broker remains running without AI clients) and\n`leaseIdleTimeoutMs` (how long an inactive client retains control). The\n`health` result identifies the active `mode` and reports `broker_connected`,\n`controller_state`, `controller_owner`, and `client_count`.\n\nThe adapter and the shared broker greet each other over a private handshake,\nand this version changed it. A broker left running from an earlier version\ntherefore cannot serve an adapter from this version: the new adapter reports\n`E_BROKER_VERSION_MISMATCH` and leaves that broker alone rather than killing or\nreplacing it. The old broker shuts itself down once it has been idle for\n`brokerIdleTimeoutMs`; restart the adapter (or the Claude Code session that\nlaunched it) after that and it attaches normally. Direct mode is unaffected by\nthis broker handshake, but whenever the adapter ↔ plugin protocol changes — as\nit does in this release — the Blockbench plugin must be updated and reloaded\ntogether with the adapter; a mismatched plugin is closed with\n`protocol_mismatch` and `health` shows `plugin_connected: false`. If an older\nadapter (for example, a 0.1.0 adapter left running in another client session)\nis still holding the port, the broker cannot start and `health` reports\n`E_PORT_IN_USE`; close that old adapter and the next tool call re-attaches\nautomatically, with no need to restart the AI client.\n\nOnly one AI client controls Blockbench at a time. While one client has\ncontrol, another client's Blockbench command returns `E_CLIENT_BUSY`; health\nand tool discovery remain available. Handing control to another client revokes\nthe scoped-directory grant first, so Blockbench asks for fresh confirmation\nbefore the new client can use file tools.\n\n#### 3. Load the plugin in Blockbench\n\n1. For an npm installation, select the already-built plugin bundle at\n   `node_modules/@adhisang/minecraft-blockbench-mcp/dist/plugin/minecraft_blockbench_mcp.js`.\n   For a git checkout only, build (`npm run build`) and select\n   `dist/plugin/minecraft_blockbench_mcp.js`.\n2. In Blockbench: **File → Plugins → Load Plugin from File** and select that\n   file.\n3. In **File → Preferences → Settings → General**, set **MCP Adapter Port**\n   (default `39731`) and **MCP Shared Secret** to match the adapter.\n\nWhen the connection succeeds, Blockbench shows “MCP adapter connected”. The\n`health` tool then reports `plugin_connected: true`.\n\n## Scoped file access\n\nFile reads/writes (including `export_model` and texture loading by path) are\nrestricted to one directory per session:\n\n1. The AI client calls `propose_scoped_directory` with an absolute path.\n2. Blockbench shows a confirmation dialog with the normalized path. Nothing is\n   accessible until you click **Allow this session**.\n3. Access lasts for the current session only: it expires when the plugin\n   reloads or Blockbench restarts, and **Tools → Revoke MCP Scoped Directory**\n   revokes it immediately mid-session.\n4. Overwrites require an explicit per-file `overwrite: true` flag; multi-file\n   writes preflight every destination and write nothing if any blocker exists.\n   Symbolic links inside the scoped directory are rejected.\n\n## Security model\n\nThe adapter listens on loopback only (`127.0.0.1`) and authenticates the\nplugin with a shared secret. The config file is user-only (`0600` on\nnon-Windows systems).\n\nThis design assumes every process running on the machine under your user\naccount is trusted, because such a process can read the config file directly.\nOn a shared multi-user machine, another local user can bind the loopback port\nbefore the adapter starts and obtain the secret, so this tool is intended for\nsingle-user desktops.\n\nAI file access is additionally gated by the in-Blockbench scoped-directory\nconfirmation dialog and remains limited to the confirmed directory for the\nsession. `setup --full-auto` leaves a loopback DevTools port open until that\nBlockbench instance exits; restart Blockbench after provisioning to close it.\n\n## Tools\n\n`health` (adapter status; works with Blockbench closed) plus, relayed to the\nplugin: `get_plugin_status`, `get_project_state`, `get_elements` (cube/group\nread-back: geometry, UV state, per-face texture references, and hierarchy),\n`create_project`, `open_model`, `create_cubes` (optionally with per-cube\n`box_uv`/`uv_offset`), `update_cube`, `set_cube_uv` (box-UV offset/mirroring,\nper-face UV rectangles with rotation, UV mode switching),\n`set_texture_resolution` (project texture resolution with optional UV\nrescale), `delete_cubes`, `create_group`, `update_group`, `delete_group`,\n`assign_texture`, `set_display_transform`, `export_model`, `read_file`,\n`write_files`, `save_project`, `close_project`, `capture_screenshot`\n(optionally from a native camera preset — `initial`, `top`, `bottom`, `north`,\n`south`, `east`, `west`, and the isometric variants — rendered offscreen so\nthe visible viewport camera never moves; which side is a model's \"front\"\ndepends on the format's `forward_direction`; returns MCP image content, or\nwrites a PNG to `output_path` inside the scoped directory with optional\n`overwrite`), `validate_project`, `propose_scoped_directory`.\n\n`save_project` writes the open project of any format as a `.bbmodel` into the\nscoped directory through `Codecs.project.compile()`; other installed plugins\nlistening to the codec's compile hooks may adjust the saved output\n(format-owner behavior). A fresh project adopts the destination as its save\npath and is marked saved; saving to a path that differs from the project's\ncurrent save path deliberately leaves the user's Ctrl+S target and the\nunsaved indicator untouched.\n\n`close_project` closes the active project tab without ever showing a dialog:\na tab created or opened by `create_project`, `open_model`,\n`create_geckolib_project`, or `open_geckolib_model` in the current plugin\nsession closes without a save prompt (discarding unsaved changes); any other\ntab closes only when it is saved, and an unsaved one is refused with\n`E_INVALID_PARAMS`, closing nothing.\n\n`open_model` and `open_geckolib_model` reload file-linked textures from disk\nbefore returning, and report each texture's `path` and any load `error`;\n`open_model` also reports the resolved model `path` and any adjustment\n`warnings`, accepts sprite-object texture values and multi-hop `#texture`\nvariable chains, and opens a model that has a parent but no elements without\nBlockbench's own child-model dialog. Java project tabs opened via `open_model`\nare named after the opened file. `create_project`, `open_model`,\n`create_geckolib_project`, and `open_geckolib_model` no longer need `force`:\nother open tabs, saved or not, are left untouched (the parameter is still\naccepted but has no effect). `open_model`'s opt-in `resolve_parents` (with\n`asset_roots`) inlines the model's parent chain from files inside the\nconfirmed scoped directory before the model opens; textures still resolve\nagainst the opened model's own assets directory, not `asset_roots`.\n\nGeckoLib tools (they require the third-party **GeckoLib Models & Animations**\nplugin, see below): `create_geckolib_project`, `open_geckolib_model`,\n`export_geckolib_model`, `export_geckolib_animations`,\n`validate_geckolib_file`, `upsert_geckolib_animation`,\n`delete_geckolib_animation`, `get_geckolib_animation`,\n`capture_geckolib_animation_frame`.\n\nWhile Blockbench (or the plugin) is not running, operation tools return a\nstructured `E_PLUGIN_NOT_CONNECTED` error immediately — the adapter never\nauto-launches Blockbench, waits, or retries in the background.\n\nCancelling a tool call that has not reached Blockbench yet stops it before it\nruns; once the plugin has started an operation, cancelling does not undo it.\nOne case never cancels at all: when the AI client numbers a request with the\nJSON-RPC id `0` or `\"\"`, the MCP SDK the adapter is built on drops the\ncancellation before the adapter can see it, so that request runs to completion\nand returns its normal result as if nothing had been cancelled. Every other\nrequest id cancels normally. This is upstream behavior, not something the\nadapter can intercept.\n\n## GeckoLib models\n\nThe `geckolib_*` tools drive the third-party\n[GeckoLib](https://wiki.geckolib.com/) Blockbench plugin (\"GeckoLib Models &\nAnimations\", plugin id `geckolib`; tested with 4.2.5). Install it once inside\nBlockbench via **File → Plugins → Available**. Without it, every `geckolib_*`\ntool fails per call with a structured `E_PLUGIN_DEPENDENCY_MISSING` error that\nnames the install remediation; the plugin re-checks the format registration on\nevery call, so installing or re-enabling GeckoLib takes effect immediately.\n\n- `create_geckolib_project` needs `modid`, `model_type`\n  (`Entity|Block|Item|Armor|Object`), and `identifier`; the identifier becomes\n  `geometry.<identifier>` and the recommended export file names\n  `<identifier>.geo.json` / `<identifier>.animation.json`.\n- Geometry building reuses the format-neutral tools (`create_cubes`,\n  `create_group`, `assign_texture`, ...) on the GeckoLib project.\n- `export_geckolib_model` writes Bedrock-format geometry with\n  `format_version 1.12.0` (GeckoLib 4 strict; also loads on GeckoLib 5);\n  `export_geckolib_animations` writes the animation JSON with GeckoLib's\n  keyframe encoding and `geckolib_format_version: 2`. Item display-settings\n  JSON export is not supported.\n- `validate_geckolib_file` checks an exported `.geo.json`, an animation JSON,\n  or both against rules derived from GeckoLib runtime behavior — no official\n  schema exists, so diagnostics carry stable `geckolib_*` check ids and the\n  applied profile (`gl4`). Animation content checks (loop values, easing\n  names, easingArgs, timestamps, keyframe value shapes, effect-keyframe\n  structure) need no geometry; bone cross-checks need both paths.\n  `validate_project` additionally runs GeckoLib project checks (bone naming,\n  modid/identifier, Armor bone template, texture size) when a\n  `geckolib_model` project is open, and validates the open project's\n  animations through the same checks — including animators orphaned by a\n  group rename or delete.\n- Validation never blocks exports; export and validate are independent tools.\n\n### Animation authoring\n\n- `upsert_geckolib_animation` creates or replaces one whole animation clip,\n  keyed by `name`, in a single undo step: loop mode\n  (`once|loop|hold_on_last_frame`), clip `length`, optional `override` and\n  `anim_time_update`, plus per-bone `rotation`/`position`/`scale` keyframes\n  with GeckoLib easing (`easing`, `easingArgs`) and\n  `linear|catmullrom|step` interpolation. Replacing an existing name requires\n  `replace: true`, otherwise the call fails with `E_FILE_EXISTS`.\n  `delete_geckolib_animation` removes a clip by name;\n  `get_geckolib_animation` reads one back in exactly the upsert payload\n  shape; `get_project_state` lists an `animations` summary (name, loop,\n  length).\n- **Time units**: keyframe `time` and clip `length` are seconds (Blockbench's\n  convention, and what `.animation.json` stores).\n- **Axis convention**: payload values use the GeckoLib `.animation.json`\n  convention — exactly what `export_geckolib_animations` writes and\n  `validate_geckolib_file` reads. Relative to the Blockbench UI, rotation X/Y\n  and position X are stored inverted; the plugin applies the same mapping the\n  GeckoLib plugin's own importer uses, so read → edit → upsert round-trips.\n- **Molang**: string values (keyframes, `anim_time_update`) are passed\n  through, never evaluated. Validation only checks value shapes and warns on\n  unbalanced parentheses; a molang expression that fails to compile makes\n  GeckoLib 4 drop the whole animation at load, so test expressions in-game.\n- **Replace clobbers manual edits**: `upsert_geckolib_animation` with\n  `replace: true` overwrites the whole clip, including manual tweaks made in\n  the Blockbench UI since the clip was last read. Every upsert/delete is one\n  undo step, so Ctrl+Z in Blockbench recovers the previous state.\n- **Still-frame screenshots**: `capture_geckolib_animation_frame` poses a named\n  animation at a still timestamp and returns MCP image content, or writes a PNG\n  to `output_path` inside the scoped directory with optional `overwrite` and\n  returns `path` and `bytes`; results also carry `project`, `counts`, `width`,\n  `height`, `animation`, `time`, `rendered_time`, and `angle_preset` when one\n  was requested. It accepts the same\n  optional `width`, `height`, and native `angle_preset` values as\n  `capture_screenshot`; preset renders use the offscreen preview so the visible\n  camera does not move. The command leaves the project unchanged and writes no\n  file except the `output_path` PNG: it\n  temporarily marks only the target animation as playing, calls Blockbench's\n  still preview, renders the screenshot, then restores the previous selected\n  animation, playing flags, timeline time/playback flag, effect mute flags, and\n  default/current pose after success or failure. Timeline playback must already\n  be stopped. If the requested time is beyond the clip length, `loop` wraps,\n  `hold_on_last_frame` clamps to the last frame, and `once` fails with\n  `E_INVALID_PARAMS` before moving the timeline. Sound, particle, and timeline\n  effect channels are muted during the still preview.\n- **Importing existing `.animation.json` files**: there is no dedicated import\n  tool. An agent can read a file (`read_file`), translate each\n  animation into an upsert payload, and call `upsert_geckolib_animation` —\n  but that workaround drops constructs outside the authoring scope: effect\n  keyframes (sounds, particles, timeline instructions) and bezier\n  interpolation are not representable in the payload (their file validation\n  still works).\n\n\n## GeckoLib live smoke\n\n`npm run smoke:geckolib-live` is a developer-run smoke helper for the\n`capture_geckolib_animation_frame` path. It drives a real Blockbench + GeckoLib\nruntime through the stdio MCP adapter, writes PNG/report/checklist artifacts,\nand stops at a human visual review handoff. A successful script run means the\nautomated sanity checks passed and the artifacts are ready to inspect; it does\nnot prove visual correctness by itself.\n\nPrerequisites:\n\n1. Run `npm run build` so `dist/adapter/cli.js` and\n   `dist/plugin/minecraft_blockbench_mcp.js` match the current checkout.\n2. Load or reload `dist/plugin/minecraft_blockbench_mcp.js` in Blockbench.\n3. Install and enable the **GeckoLib Models & Animations** Blockbench plugin.\n4. Configure the Blockbench MCP plugin with the same port and shared secret the\n   helper will use, then reconnect it.\n5. Ensure no other adapter process is already using the selected port.\n\nRecommended run command:\n\n```sh\nBLOCKBENCH_MCP_SECRET=<your-secret> npm run smoke:geckolib-live\n```\n\nUseful options after `--`:\n\n```sh\nBLOCKBENCH_MCP_SECRET=<your-secret> npm run smoke:geckolib-live -- --port 39731 --out ./smoke-output\n```\n\n- `--out <dir>` chooses a parent directory; the helper always creates a unique\n  `geckolib-live-smoke-*` run subdirectory and refuses to overwrite an existing\n  run directory. Without `--out`, the parent is the system temporary directory.\n- `--port <port>` changes the adapter listener. If you change it, set the\n  Blockbench MCP plugin to the same port and reconnect before running the\n  helper.\n- `--secret <secret>` exists only as a less-safe convenience. Prefer\n  `BLOCKBENCH_MCP_SECRET` because command-line secrets can leak through shell\n  history, process listings, copied commands, or npm logs. The helper must not\n  write the shared secret, raw argv, or raw environment dumps into reports.\n\nBefore fixture commands run, the helper prints a notice that it will create and\nleave a new unsaved GeckoLib project tab for manual inspection. It does not\nopen, save, or overwrite existing project files, and it does not use scoped\ndirectory writes. After the run, inspect `frame-0.png` and `frame-1.png`, record\nnotes in `review-checklist.md`, then close or discard the smoke project tab\nmanually.\n\nGenerated artifacts:\n\n- `frame-0.png` / `frame-1.png` — still-frame screenshots captured at distinct\n  animation timestamps.\n- `smoke-report.json` — sanitized runtime metadata, command outcomes, frame\n  hashes, automated sanity checks, and `human_review_required: true`.\n- `review-checklist.md` — human review steps for visible pose differences,\n  playback/effect side effects, Blockbench usability, and reviewer notes.\n\nCommon failures:\n\n| Symptom | Remediation |\n| --- | --- |\n| Missing `dist/adapter/cli.js` | Run `npm run build` before the smoke helper. |\n| Missing secret | Set `BLOCKBENCH_MCP_SECRET` to the same secret configured in the Blockbench MCP plugin. |\n| Port conflict / `E_PORT_IN_USE` | Do not kill unknown processes from the helper. Close the adapter you started, or use `--port <free-port>` and configure the Blockbench plugin to the same port. |\n| Plugin disconnected / stale protocol | Rebuild, reload or reinstall `dist/plugin/minecraft_blockbench_mcp.js`, verify matching port/secret settings, reconnect Blockbench, and rerun. Some stale-plugin protocol failures are only observable as a disconnected-plugin precondition. |\n| GeckoLib unavailable | Install or enable the GeckoLib Models & Animations plugin in Blockbench and rerun. |\n| Required tool absent | Rebuild and reload the current Blockbench MCP plugin bundle. |\n| Automated frame hashes match | Treat the smoke artifacts as not ready for visual review; inspect the generated report and adjust/fix the runtime path before claiming a live-smoke pass. |\n\n## Troubleshooting\n\n| Symptom | Check |\n| --- | --- |\n| `health` reports `E_SECRET_MISSING` | Run `minecraft-blockbench-mcp setup`, or configure `--secret` / `BLOCKBENCH_MCP_SECRET` for the adapter. |\n| `health` reports `E_PORT_IN_USE` | Another process (possibly an orphaned adapter) holds the port; change `--port` on both sides or free it. |\n| `health` reports `E_BROKER_VERSION_MISMATCH` | A shared broker left running from an earlier version still holds the connection. Let it idle out (`brokerIdleTimeoutMs`), then restart the adapter — or start the adapter with `--direct`. |\n| `health` reports `E_LISTENER_FAILED` | The operating system or runtime could not create the loopback listener; check local network permissions and platform policy, then restart the adapter. |\n| `health` reports `E_BROKER_UNAVAILABLE` | The broker failed to start for a reason other than a missing secret, a port conflict, or a listener failure. Once the underlying cause is resolved, the next tool call re-attaches automatically; no client restart is needed. |\n| Plugin shows “rejected the connection” | Port or secret mismatch between adapter and plugin settings. |\n| Rotated the secret but the plugin still reports “rejected the connection” | Restart the adapter, or the Claude Code session that launched it, so it loads the new secret. |\n| Plugin loads but nothing happens | Open the Blockbench devtools console (`Ctrl+Shift+I`); Blockbench logs plugin load errors there without any UI notice. |\n| Plugin never connects and no permission prompt appears | Check **Tools → MCP Connection Status** for the config source. A denied file-access permission prompts again after changing \"MCP Config File Path\" (or use **Tools → Locate MCP Config File**); entering the port and secret manually always works. |\n| File tools fail with `E_SCOPE_*` codes | The scoped directory is unconfirmed, expired (reload), or revoked — run `propose_scoped_directory` again. |\n\n## Development notes\n\n- `npm run dev` (in `external/blockbench`) launches Blockbench with a DevTools\n  remote-debugging port, which is handy for driving smoke tests.\n- The adapter ↔ plugin protocol (versioned, capability-flagged, partitioned\n  into format-neutral and Java-format commands) lives in `src/shared/protocol.ts`;\n  the path-containment rules live in `src/shared/scope.ts`.\n- GeckoLib and other formats are future adapters: add a capability flag and a\n  new command group instead of extending the Java group.\n","readmeFilename":"README.md"}