Skip to content

Environment variables ​

All knobs are optional; the defaults serve a local single-user setup.

Server ​

VariableDefaultMeaning
GODOT_MCP_TRANSPORTstdiostdio or http
GODOT_MCP_HTTP_HOST127.0.0.1HTTP bind host
GODOT_MCP_HTTP_PORT9090HTTP bind port (/mcp is FastMCP's mount path)
GODOT_MCP_AUTH_TOKENunsetBearer token; required for non-loopback HTTP binds (fail-fast, #226)
GODOT_MCP_HTTP_HOST_ORIGIN_PROTECTIONautoHTTP Host/Origin (DNS-rebinding) guard (#636): auto validates the Host header + browser Origin for a loopback-bound server (foreign Host → 421, foreign Origin → 403); false opts out; true/strict validates every request
GODOT_MCP_BRIDGE_URLws://127.0.0.1:9080bridge listen (server) / connect (addon) URL
GODOT_MCP_BRIDGE_TOKENunsetopt-in shared secret for the bridge handshake (#538) — when set, the addon must send the matching token as its first message or the peer is refused at the handshake; never logged
GODOT_MCP_PROJECT_DIRunsetexplicit project dir; else the connected editor's project is used
GODOT_MCP_DEFAULT_TOOLSETSunset (= inspection)seed the initial enabled toolsets: all or a comma-separated list (e.g. scene_edit,runtime); unknown names are logged and ignored
GODOT_MCP_GODOT_BINgodot on PATHthe Godot binary the server shells out to for headless runs and parse/compile checks (distinct from the addon's editor; also settable as GODOT_BIN on CI)
GODOT_MCP_LOG_LEVELINFOstructured stderr logging level (JSON per line)
GODOT_MCP_APPROVAL_WEBHOOKunsethuman-in-the-loop approval endpoint for destructive tools (#153); unset → auto-approve
GODOT_MCP_APPROVAL_TIMEOUT30.0per-approval-request timeout, seconds
GODOT_MCP_APPROVAL_FAIL_OPENfalseon an unreachable/slow webhook: false denies (fail closed, #638), true approves
GODOT_MCP_CHECK_UPDATESunset (= off)opt-in update check (#629): when set (1/true/yes) the server may query the release feed so godot_check_for_updates can report a newer release; unset = no network

Addon (read in the editor's environment) ​

VariableDefaultMeaning
GODOT_MCP_BRIDGE_URLws://127.0.0.1:9080where the addon dials the server (same var as the server)
GODOT_MCP_BRIDGE_TOKENunsetthe same opt-in secret as the server — the addon authenticates with it as its first message after connecting; never logged
GODOT_MCP_AUTO_REFRESHunset (= off)opt-in timer-based filesystem auto-refresh (#561): 1/true/yes enables a periodic EditorFileSystem.scan() so external edits (agents, git, other tools) show up without editor focus; the dock checkbox is the runtime override
GODOT_MCP_AUTO_REFRESH_INTERVAL10seconds between auto-refresh scans (floor 2s; only read when GODOT_MCP_AUTO_REFRESH is on)
GODOT_BINgodot on PATHwhere a CI workflow points the server's binary lookup (e2e.yml Actions var) — the server itself reads GODOT_MCP_GODOT_BIN, then godot/Godot on PATH, then the macOS app bundle

Security model ​

ModeBindAuthWhy
stdio (default)N/A (local subprocess)noneonly the spawning client can talk to it
http on loopback127.0.0.1noneonly local processes reach it
http non-loopback0.0.0.0 / external IPGODOT_MCP_AUTH_TOKEN requiredthe server refuses to start otherwise (#226) — without a token, anyone on the network could write scripts, run the game, or export builds through your editor
bash
# Generate + set a token
TOKEN=$(openssl rand -hex 32)
GODOT_MCP_TRANSPORT=http GODOT_MCP_HTTP_HOST=0.0.0.0 GODOT_MCP_AUTH_TOKEN=$TOKEN \
  uv run godot-editor-mcp

Clients pass the same token (HTTP Authorization: Bearer … header).

Bridge handshake auth (#538) ​

The editor↔server WebSocket bridge is localhost-only; the bridge token adds a shared secret for shared/self-hosted runners and remote-editor setups. It is opt-in on each side — unset means no auth (zero-config local dev, unchanged path). Both sides read GODOT_MCP_BRIDGE_TOKEN.

Server tokenAddon tokenBehavior
unsetunsettoday's path — no auth exchange (byte-identical)
setset, matchingpeer authenticated, everything serves
setset, mismatchedstructured refusal at the handshake; the addon drops into its reconnect/backoff loop and the dock log shows the refusal reason each attempt until the tokens agree
setunsetrefused at the handshake (the addon never authenticates) — same visible reconnect loop
unsetsetthe server consumes the auth envelope as a control message and serves anyway (forward-compatible: a newer addon against an older server)

A refusal happens at the handshake — never as per-command errors, never as a silent drop. Tokens are compared in constant time and are never logged on either side.

bash
# Shared/self-hosted runner: pin the same token on both sides
TOKEN=$(openssl rand -hex 32)
GODOT_MCP_BRIDGE_TOKEN=$TOKEN uv run godot-editor-mcp          # server
GODOT_MCP_BRIDGE_TOKEN=$TOKEN godot --editor --path ./game     # the editor's env

Listener hardening (#636) ​

Browsers do not apply the same-origin policy to WebSockets, and DNS rebinding can make a page the user visits resolve to 127.0.0.1. Both listeners are hardened by default:

  • Bridge WebSocket: the listener accepts only Origin-less handshakes (origins=[None]) — the Godot addon's WebSocketPeer sends no Origin, so a browser page is rejected at the handshake (403) before it can reach the bridge. A bad-or-missing-token peer is also refused before it can displace the authenticated editor (#635).
  • HTTP transport: FastMCP's HostOriginGuardMiddleware is enabled by default (GODOT_MCP_HTTP_HOST_ORIGIN_PROTECTION=auto), so a non-loopback Host header is refused (421) and a foreign browser Origin is refused (403) before any code-executing tool (write_script, run_and_capture, export_project) is reachable. Opt out with GODOT_MCP_HTTP_HOST_ORIGIN_PROTECTION=false.

Neither guard replaces the auth token — a non-loopback HTTP bind still requires GODOT_MCP_AUTH_TOKEN.

Logging ​

Logs are one JSON object per line on stderr (stdout is the stdio protocol channel). Every log line carries the command id where applicable; errors include exception info. No secrets or full file contents are logged.

194 tools · 29 toolsets · contract version 1