DraxGUI MCP adapter

Updated 28 October 2026

DraxGUI exposes its project editor to AI clients (Claude Code, Codex, MCP Inspector or any Model Context Protocol client) through an MCP endpoint built into the editor web server. AI clients edit projects through the same services as the browser editor: the same project store and edit ops, the same validation, asset checks, build jobs and pending-edit preparation. DraxGUI does not contain an AI model, does not call any paid API and never receives your AI account credentials.

What an AI client can do: list and read projects, learn the format, apply batched edit transactions, upload images, validate, start and cancel builds, and prepare a pending edit.

What it cannot do: apply or roll back a live deployment (a person runs the returned /draxgui applyedit <id> command in game or on the console), run shell or console commands, read or write arbitrary files, fetch URLs, or use the browser login link.

  • Endpoint: POST <editor base URL>/mcp, for example http://127.0.0.1:8765/mcp
  • Stdio bridge for stdio-only clients: draxgui-mcp-bridge-<version>.jar, under Downloads in the licence panel

1. Create a token

MCP clients use their own bearer tokens. A token has a name, an expiry, a project scope and a set of capabilities. Only a SHA-256 verifier is stored (plugins/DraxGUI/mcp-tokens.json, owner-only file permissions where the file system supports them); the secret is shown once.

In game (with the DraxGUI admin permission) or on the console:

/draxgui mcp token create <name> <projects|all> [--caps <c1,c2,...>] [--ttl <duration>]
/draxgui mcp token list
/draxgui mcp token revoke <name>

projects is required (comma-separated ids, or all); without --caps the token is read-only. The command calls the McpTokens Java API described in §7.

Option Meaning Default
projects Comma-separated project ids, or all for every project (including future ones) required
--caps Subset of project.read, project.write, asset.write, preview, build, deploy.prepare, script.write read-only (project.read); project.read is always added
--ttl Lifetime, 5 minutes to 365 days 30 days

Pick the smallest scope that does the job:

Use Capabilities
Read-only review project.read
Authoring screens/HUDs/popups/graphs project.read,project.write
... plus images add asset.write
... plus scripts (Java/JavaScript/Python source text) add script.write
Build and prepare deployments add build,deploy.prepare

A pending edit packages the whole server pack (every project), so prepare_deploy only works for a token scoped to *, or when the token's projects are the only projects on the server.

Token handling

  • Put the token in an environment variable (DRAXGUI_MCP_TOKEN) or a file readable only by you. Never put it in project files, .mcp.json literals, screenshots or chat.
  • Console issuance is visible in the server console log (DraxGUI does not log it again, but the host may keep console history). Prefer issuing tokens in game when the console log is shared.
  • Revoke immediately if a token leaks (/draxgui mcp token revoke <name>); it stops working on the next request. Expired and revoked tokens are kept in the list for 30 days for audit.
  • Every write is recorded in audit.jsonl as mcp:<token name>.

2. Connect a client over HTTP

Use HTTP when the client supports remote/HTTP MCP servers with custom headers. Set the token in your environment first (PowerShell: $env:DRAXGUI_MCP_TOKEN = Get-Content <token file>; bash: export DRAXGUI_MCP_TOKEN="$(cat <token file>)").

Claude Code — project file .mcp.json (the file only contains the variable name; Claude Code expands ${DRAXGUI_MCP_TOKEN} when it loads the server):

{
  "mcpServers": {
    "draxgui": {
      "type": "http",
      "url": "http://127.0.0.1:8765/mcp",
      "headers": { "Authorization": "Bearer ${DRAXGUI_MCP_TOKEN}" }
    }
  }
}

Or for your user only: claude mcp add --transport http draxgui http://127.0.0.1:8765/mcp --header "Authorization: Bearer <TOKEN>" (this stores the literal token in ~/.claude.json; the .mcp.json form above keeps it out of files).

Codex — ~/.codex/config.toml:

[mcp_servers.draxgui]
url = "http://127.0.0.1:8765/mcp"
bearer_token_env_var = "DRAXGUI_MCP_TOKEN"

MCP Inspector (CLI, tested with @modelcontextprotocol/inspector@2.11.0) — config file draxgui-inspector.json kept outside the repository:

{ "mcpServers": { "draxgui": { "type": "http", "url": "http://127.0.0.1:8765/mcp",
  "headers": { "Authorization": "Bearer <TOKEN>" } } } }
npx -y @modelcontextprotocol/inspector@2.11.0 --cli --config draxgui-inspector.json --server draxgui --method tools/list
npx -y @modelcontextprotocol/inspector@2.11.0 --cli --config draxgui-inspector.json --server draxgui \
    --method tools/call --tool-name list_projects --tool-args-json "{}"

Python SDK (tested with mcp==2.3.0):

import os, httpx2
from mcp import Client
from mcp.client.streamable_http import streamable_http_client

http = httpx2.AsyncClient(headers={"Authorization": "Bearer " + os.environ["DRAXGUI_MCP_TOKEN"]})
async with Client(streamable_http_client("http://127.0.0.1:8765/mcp", http_client=http)) as client:
    print(await client.call_tool("list_projects", {}))

3. Connect a stdio-only client through the bridge

The bridge is a small Java 21 program the AI client launches. It speaks MCP on stdin/stdout and forwards every message to the DraxGUI endpoint with your token. Download it (draxgui-mcp-bridge-<version>.jar) from the licence panel: sign in, and it is under Downloads while your licence is active. MCP Bridge Setup.txt in the BuiltByBit download walks you through the setup.

java -jar draxgui-mcp-bridge.jar --url <http(s)://host:port/mcp> [--token-file <path>]
     [--timeout-seconds N] [--allow-insecure-http] [--verbose]
  • Token: environment variable DRAXGUI_MCP_TOKEN, or --token-file. A --token argument is refused (command lines are visible to other local users). The token is never printed.
  • --url may be the base URL; /mcp is appended when the path is empty. DRAXGUI_MCP_URL works too.
  • Plain http:// is only accepted for loopback hosts unless you pass --allow-insecure-http; use HTTPS (a reverse proxy) for remote servers.
  • stdout carries only protocol messages; diagnostics go to stderr (--verbose adds method/status lines).
  • If the server restarts during an initialize-era session, the bridge re-initializes and retries once.

Claude Code .mcp.json:

{
  "mcpServers": {
    "draxgui": {
      "type": "stdio",
      "command": "java",
      "args": ["-jar", "/opt/draxgui/draxgui-mcp-bridge.jar", "--url", "https://editor.example.com/mcp"],
      "env": { "DRAXGUI_MCP_TOKEN": "${DRAXGUI_MCP_TOKEN}" }
    }
  }
}

Codex ~/.codex/config.toml:

[mcp_servers.draxgui]
command = "java"
args = ["-jar", "/opt/draxgui/draxgui-mcp-bridge.jar", "--url", "https://editor.example.com/mcp"]
env_vars = ["DRAXGUI_MCP_TOKEN"]

On Windows use the full path to java.exe if java is not on PATH, and forward slashes or escaped backslashes in JSON/TOML paths.

4. Remote access

The editor (and so /mcp) listens on editor.bind-address (loopback by default). For an AI client on another machine, put the editor behind an HTTPS reverse proxy and set editor.public-url (see the editor documentation). The MCP endpoint accepts only requests whose Host header names this server (loopback names with the configured port, the bind address, or the public URL host), which blocks DNS-rebinding; it rejects any browser Origin other than the configured editor origin. Do not expose the editor port directly to the internet over plain HTTP: bearer tokens would travel in clear text.

5. Tools

All tools take a JSON object; schemas are published in tools/list (closed objects with patterns, ranges and enums). Results are JSON (structuredContent, and the same JSON as text). Lists are paginated with cursor/limit and return nextCursor when more items exist. Tools you lack the capability for are hidden from tools/list and refused if called.

Tool Capability Writes Purpose
list_projects project.read Projects in the token's scope with head revision and counts
get_schema_info project.read Document format, id patterns, every op with fields and capability, expression syntax, limits, templates, a worked example batch
list_element_types project.read Element catalogue (compact, or one full entry with type)
list_node_types project.read Logic node catalogue (filter by category, full entry with type)
get_capabilities project.read Token scope, Minecraft client version registry, shader families, compiler/script runtimes, capability registry entries
get_project project.read Document at head or revision; sections for parts; large documents return a summary
get_container project.read One screen/HUD/popup with elements and a parent/child tree
preview_container preview PNG of one screen/HUD/popup composed like the Shader-Safe runtime from a fresh compile (the build's own shapes, images, icons; authored state, variable defaults, player "Steve"), returned as an image content item plus {width, height, scale, notes}. Text uses a substitute font stretched to Minecraft's widths, heads/models show placeholders, HUDs use a 480x270 GUI px reference screen, Standard screens show their 2D canvas. One preview at a time, 30 s compile limit, at most 2048 px per side. Not proof of in-game rendering; the standalone dev server answers preview_unavailable.
list_revisions project.read Recent revisions with actor and summary
validate_project project.read Diagnostics with JSON pointer and container/element/node/script-line ref
apply_ops project.write (+ script.write / asset.write for those ops) yes One transaction of edit ops against baseRevision with requestId
create_project project.write yes New project from a template
restore_revision project.write yes Copy an older revision into a new revision
list_snapshots project.read Named snapshots (newest first): name, source revision, time, actor, note
create_snapshot project.write yes Save a revision (default head) under a name (a-z 0-9 - _, up to 48); at most 50 per project; a taken name is refused. Snapshots survive revision pruning; deleting one is an editor action
restore_snapshot project.write yes Copy a snapshot into a new revision against baseRevision
upload_asset asset.write yes Base64 image/animation bytes (up to the editor's upload limit, 32 MiB by default) or a TTF/OTF font (<= 8 MiB), same limits and validation as the editor upload; optional license (record it for fonts: DraxGUI ships none and rasterises them into the pack)
list_assets project.read Asset entries with size, frames, media type, hash
search_icons project.read Bundled icon library search (use as props.icon)
start_build build yes Build an immutable revision (Java versions, optional Bedrock)
get_build_status project.read State, steps, diagnostics, artifact names/sizes/hashes (never bytes)
cancel_build build yes Cancel a queued/running build
prepare_deploy deploy.prepare yes Pending edit for a succeeded build; returns the /draxgui applyedit <id> command for a person to run
list_deployments project.read Pending/applied edits in scope and the active generation

Authoring loop

  1. get_schema_info once, list_element_types / list_node_types as needed.
  2. get_project (or get_container) and note revision.
  3. apply_ops with baseRevision = that revision and a new requestId; batch related edits (up to 500 ops) — the batch is all-or-nothing.
  4. Read the returned revision and diagnostics; fix errors with another apply_ops.
  5. start_build → poll get_build_status → prepare_deploy → give the command to a person.

Retrying with the same requestId returns the original result ("replayed": true) instead of applying twice. Request ids are private to each token.

Wiring logic graphs. A handler (events.click, a container's events.open, ...) is either a list of actions or {"graph": "<graph id>", "entry": "<event node id>"}. Event nodes inside a graph (event.click, event.open, ...) are only entry markers: an event.click node naming an element runs when that element's events.click routes to it. validate_project reports event nodes nothing routes to as unbound_event warnings (with the exact events.* value to set) and a handler naming a missing node as a missing_entry error.

Worked example. The "Daily Rewards" demo was authored through these tools with the official Python SDK: create, icon search, variables, popup, screen (60 elements), graph, validate → fix the reported unbound_event bindings → validate clean → build (Java + Bedrock) → prepare_deploy. A person then ran /draxgui applyedit <id> and the screen was tested in a 1.21.4 client (claim once, duplicate clicks refused, next-day branch).

Errors are tool results with isError: true and {"error": {"code", "message", "details"}}. Common codes:

Code Meaning / next step
invalid_arguments Argument failed the schema; details.errors names the field (ops[2].op: must be one of ...)
missing_capability Token lacks details.requiredCapability
project_forbidden Project outside the token's scope
not_found Project, container, element, build or revision missing; message lists what exists
revision_conflict baseRevision is stale; details.currentRevision and details.changesSince show what changed; re-read and retry
op codes (duplicate_id, bad_parent, bad_patch, ...) details.opIndex, details.op, ids and a hint; nothing was changed
unsupported_media, too_large, bad_base64 Upload refused, with the reason
build_not_finished, deploy_refused, deploy_scope Deployment preparation refused, with the reason

Protocol-level problems (unknown tool, malformed request) are JSON-RPC errors.

6. Protocol notes

  • Supported revisions: 2026-07-28 (stateless per-request _meta, server/discover, MCP-Protocol-Version / Mcp-Method / Mcp-Name header validation) and the initialize-based 2025-11-25, 2025-06-18, 2025-03-26 (session via Mcp-Session-Id, ended with DELETE /mcp). The server picks the era from each request; both work on the same endpoint.
  • Responses are single JSON objects (an SSE frame only when a client does not accept JSON). There is no GET stream (405), no subscriptions/listen, no resumability, no batching.
  • Only the tools capability is offered (no resources, prompts, logging, sampling or elicitation).
  • Authentication is a pre-provisioned bearer token. OAuth discovery (Protected Resource Metadata) is not implemented: an OAuth-capable client that receives 401 may try to start an OAuth login. Fix the configured token instead.
  • Limits: 120-request burst and 2 requests/second sustained per token (429 + Retry-After); JSON bodies up to the editor's JSON limit; asset uploads up to the editor's upload limit (one large upload at a time); 30 failed authentications per minute per address.
  • Legacy sessions are kept in memory (24 h idle); a restart ends them and clients re-initialize.
HTTP When
400 Malformed JSON-RPC, header/body mismatch (-32020), unsupported version (-32022 with data.supported), missing _meta fields (-32602), no legacy session
401 Missing, invalid, expired or revoked token (WWW-Authenticate: Bearer)
403 Browser Origin not allowed, or wrong Host
404 Unknown method in the 2026-07-28 era (-32601), unknown/expired legacy session
413 / 415 / 429 Body too large / not application/json / rate limited

7. Java API (plugin integration)

McpTokens tokens = McpTokens.open(dataDir.resolve("mcp-tokens.json"), logger);
McpServer.install(webServer, tokens);              // before webServer.start(), after Api.install
McpTokens.Created c = tokens.create(name, senderName, List.of("*"), caps, Duration.ofDays(30));
c.secret();                                        // show once to the issuer only
tokens.list();                                     // TokenInfo: id, name, actor, projects, capabilities, times, status
tokens.revoke(name);

All methods do blocking file I/O; call them off the Minecraft server thread. create throws IllegalArgumentException with an operator-facing message for invalid input.