A devcontainer setup that runs Claude Code and OpenCode with two Godot MCP servers for AI-assisted Godot game development.
Ideal for indie or solo game developers, which simply would like solid tooling without wanting to run an entire game studio using AI.
- Claude Code CLI running with
--dangerously-skip-permissionsinside a sandboxed container - OpenCode — Alternative AI coding agent with multi-provider support (OpenAI, Anthropic, Google, local models, etc.)
- godot-mcp — Full Godot editor integration (11 tools): scene manipulation, node management, script editing, documentation lookup, game testing
- minimal-godot-mcp — LSP-based diagnostics (4 tools): GDScript error checking, workspace scanning, console output
- Godot headless CLI — Run scenes, export projects, execute GDScript, and validate projects from the command line (
godot --headless) - Asset generation tools — ImageMagick, FFmpeg, Python/Pillow, trimesh, gltf-transform, obj2gltf, fbx2gltf
- Godot specific skills - This is very subjective and each developer may have different preferences when it comes to skills. The setup will source your skills and global Claude setup based on an environment variable (see 2. Configure environment in the Setup guide). Personal recommendation for a good comprehensive Godot skill: Godot skill for Claude Code
- Blender CLI or MCP: Turned out to be too big for the container and can be covered with some of the light-weight tooling installed with the devcontainer instead
- Docker (20.10+) installed and running
- macOS / Windows: Docker Desktop (recommended)
- Windows (WSL2): Docker Desktop with WSL2 backend, or Docker Engine inside WSL2
- Linux: Docker Engine or Docker Desktop
- Node.js (18+) on the host (for npm scripts and devcontainer CLI)
- socat (Linux only) — bridges Godot's localhost ports to the Docker network. Not needed on macOS or Windows where Docker Desktop handles this natively. Install with
sudo apt-get install socat - Godot 4.5+ editor installed on the host
git clone <this-repo>
cd godot-agents-devcontainer
npm installcp .env.example .envEdit .env and set the required variables:
# Required: absolute path to your Godot project
GODOT_PROJECT_PATH=/home/you/projects/my-godot-game
# Your Claude Code user config directory (skills, CLAUDE.md, etc.)
# Default: $HOME/.claude (standard Claude Code setup)
CLAUDE_USER_CONFIG_DIR=$HOME/.claude
# Optional: Godot headless CLI version (default: 4.7.1)
# GODOT_VERSION=4.7.1
# Optional: long-lived Claude Code token so the container stays logged in
# (only needed if you also use the same Anthropic account elsewhere).
# See "Staying logged in across days" below.
# CLAUDE_CODE_OAUTH_TOKEN=CLAUDE_USER_CONFIG_DIR should point to a directory containing any of:
skills/— custom skills (shared by Claude Code and OpenCode)CLAUDE.md— global instructions for Claude Code (also read by OpenCode as fallback)AGENTS.md— global instructions for OpenCode (takes precedence over CLAUDE.md)
If you manage your Claude config in a separate repo (e.g., with CLAUDE.md as a symlink to another file), point this to that directory. Symlinks within the directory resolve correctly inside the container.
npm run build
npm run upOn first use, log in inside the container:
npm run claudeClaude Code will prompt you to authenticate. Credentials are stored in a Docker volume and persist across container restarts.
Interactive claude login is all you need if this container is the only place you
use that Anthropic account. But if you also run Claude Code on your host (or another
machine) under the same account, the container tends to get logged out — usually on
the first session of the day. This is not a bug in this setup: Anthropic uses rotating,
single-use refresh tokens, so when your host refreshes the account's token it invalidates
the copy stored in the container's volume.
The fix is a long-lived token that is independent of that rotation. It's optional — leave
CLAUDE_CODE_OAUTH_TOKEN unset and everything works as before.
- Requirements: an active Claude subscription (Pro, Max, or Team). This token is created
from your Claude subscription account via the CLI below — it is not a
console.anthropic.comAPI key (those bill against the pay-as-you-go API, not your subscription). - On a machine that has a browser and Claude Code installed (e.g. your host, not the
headless container), run:
Complete the browser sign-in. It prints a token and the line
claude setup-token
export CLAUDE_CODE_OAUTH_TOKEN=<token>. Copy the<token>value. - Add it to
.env(which is git-ignored — never commit it):CLAUDE_CODE_OAUTH_TOKEN=<token>
- Recreate the container so the token is injected:
From now on
npm run up
npm run claudeauthenticates with the token automatically — noclaude loginneeded, and it survives shutdowns and multi-day gaps.
Notes:
- Inference-only. Long-lived tokens are scoped to inference, which covers normal coding.
- Expiry. The token is long-lived (about a year), not infinite — regenerate with
claude setup-tokenwhen it eventually expires. - If the token is set, it overrides any interactive login. To go back to
claude login, blank outCLAUDE_CODE_OAUTH_TOKENin.envand runnpm run upagain. - Still see a login screen after setting the token? A leftover
~/.claude/.credentials.jsonfrom a previous interactive login can make the TUI prompt even though the token authenticates fine.npm run upclears it automatically when the token is set; to fix it immediately, delete that file once (npm run shell→rm ~/.claude/.credentials.json).
npm run install-godot-addonThis copies the godot-mcp addon into your Godot project's addons/ directory.
- Open your Godot project in the editor
- Go to Project > Project Settings > Plugins
- Enable the godot-mcp plugin
- In Godot, go to Editor > Editor Settings > Network > Language Server
- Ensure the language server is enabled
- Note the port (default: 6005)
With Godot running on the host, start the container and launch your preferred AI coding agent:
npm run up
npm run claude # or: npm run opencodeOn Linux, a port bridge starts automatically with the container, relaying Godot's localhost-bound ports to the Docker network. On macOS and Windows, Docker Desktop handles this natively — no bridge needed. Both Claude Code and OpenCode have access to all MCP tools on all platforms. You can verify with the /mcp command inside Claude Code.
When done:
npm run down| Command | Description |
|---|---|
npm run build |
Build the container image |
npm run up |
Start the container (auto-starts port bridge on Linux; skipped on macOS/Windows) |
npm run down |
Stop and remove the container (auto-stops port bridge on Linux) |
npm run shell |
Open a shell inside the container |
npm run bridge:start |
Manually start host-side port bridge (Linux only; no-op on macOS/Windows) |
npm run bridge:stop |
Manually stop the port bridge (Linux only) |
npm run bridge:status |
Show whether host-side bridge relays are listening |
npm run bridge:doctor |
End-to-end health check: Godot ports, host bridge, container-side relay |
npm run claude |
Launch Claude Code with --dangerously-skip-permissions |
npm run claude:resume |
Resume a previous Claude Code session |
npm run claude:prompt -- "prompt" |
Run a one-shot prompt |
npm run opencode |
Launch OpenCode TUI |
npm run opencode:prompt -- "prompt" |
Run a one-shot prompt with OpenCode |
npm run install-godot-addon |
Install godot-mcp addon into the Godot project |
Note:
npm upis a built-in npm alias fornpm update. Always usenpm run up(withrun) to start the container.
Host Machine Container
+------------------+ +--------------------+
| Godot 4.5+ | host.docker. | Claude Code CLI |
| 127.0.0.1:6550 | internal | godot-mcp |
| 127.0.0.1:6005 | <--------------> | minimal-godot- |
| | (native) | mcp |
+------------------+ +--------------------+
| /workspace (bind) |
| = Godot project |
+--------------------+
| ~/.claude (volume) |
| + skills/ (link) |
| + CLAUDE.md (link) |
+--------------------+
Docker Desktop resolves host.docker.internal to the host and can reach localhost-bound ports natively. No bridge needed.
Host Machine Container
+------------------+ +--------------------+
| Godot 4.5+ | | Claude Code CLI |
| 127.0.0.1:6550 | bridge.sh | godot-mcp |
| 127.0.0.1:6005 | -------------> | minimal-godot- |
| | (host socat) | mcp |
+------------------+ binds on +--------------------+
docker bridge | /workspace (bind) |
172.17.0.1 | = Godot project |
| +--------------------+
+-- socat -> | ~/.claude (volume) |
(container) | + skills/ (link) |
| + CLAUDE.md (link) |
+--------------------+
Godot binds to 127.0.0.1, but the container reaches the host via the Docker bridge gateway (172.17.0.1). The host-side bridge (bridge.sh / socat) relays between these interfaces. Container-side socat forwards localhost to host.docker.internal.
- Your Godot project is bind-mounted into the container at
/workspace - Your Claude user config (
CLAUDE_USER_CONFIG_DIR) is mounted read-only; skills and CLAUDE.md are symlinked into the persisted~/.claudevolume on startup - The container has unrestricted network access (Docker provides filesystem and process isolation)
The container includes tools that Claude Code can use to generate and manipulate game assets:
| Tool | Type | What it does |
|---|---|---|
| ImageMagick | 2D | Image manipulation, format conversion, compositing (convert CLI) |
| Pillow (Python) | 2D | Programmatic texture/sprite generation, pixel art, normal maps |
| numpy (Python) | 2D/3D | Numerical operations for procedural generation, used by Pillow and trimesh |
| FFmpeg | Audio | Audio format conversion, simple sound effect generation (ffmpeg CLI) |
| trimesh (Python) | 3D | Procedural mesh generation (primitives, extrusions, booleans), export to glTF/OBJ/STL |
| gltf-transform | 3D | Optimize, compress (Draco/meshopt), merge, convert glTF files |
| obj2gltf | 3D | Convert OBJ models to glTF |
| fbx2gltf | 3D | Convert FBX models to glTF (Node.js API, use via node -e "require('fbx2gltf')(input, output)") |
The container includes the Godot engine binary (v4.7.1 by default), usable via godot --headless for:
- Running scenes:
godot --headless --path /workspace -s res://script.gd - Automated testing: Run test frameworks like GUT or GdUnit4 from the command line
- Exporting projects:
godot --headless --path /workspace --export-release "Linux" build/game - Project validation:
godot --headless --path /workspace --check-only
The version can be changed by setting GODOT_VERSION in your .env file before building the container.
Note: There is no display server in the container — always use
--headless. The Godot editor runs on your host machine.
OpenCode is included as an alternative AI coding agent with support for 75+ model providers (OpenAI, Anthropic, Google, local models via Ollama, and more).
npm run opencodeOn first launch, use the /connect command inside OpenCode to add your API credentials (e.g., OpenAI, Anthropic). Credentials are stored in a persisted volume.
Both Claude Code and OpenCode share the same Godot MCP servers. Claude Code's MCP registration runs at container start; OpenCode's is generated lazily by npm run opencode so it doesn't hold the godot-mcp connection when idle. Your skills and instructions (CLAUDE.md, AGENTS.md, skills/) are also shared:
| Feature | Claude Code | OpenCode |
|---|---|---|
| MCP servers | Configured via claude mcp add |
Configured via opencode.json |
| Global rules | CLAUDE.md |
AGENTS.md (falls back to CLAUDE.md) |
| Skills | ~/.claude/skills/ |
Reads from ~/.claude/skills/ |
| Permissions | --dangerously-skip-permissions |
"permission": { "*": "allow" } in config |
To change the default model, create or edit opencode.json in your Godot project root:
{
"model": "openai/gpt-4o",
"small_model": "openai/gpt-4o-mini"
}See the OpenCode documentation for the full list of supported providers and models.
You don't need the devcontainer to use the Godot MCP servers — both godot-mcp and minimal-godot-mcp are plain npm packages, and running your agent natively on the same machine as Godot is actually simpler than the container setup: Godot binds to 127.0.0.1, and a native agent process is already on 127.0.0.1 with it, so none of the bridging machinery (socat, host.docker.internal) is needed.
What you give up: the container's sandboxing/isolation, and the auto-installed asset-generation tools (ImageMagick, Pillow, trimesh, gltf-transform, obj2gltf, fbx2gltf) — install those yourself if you want them.
-
Install the godot-mcp addon and enable Godot's LSP server as described in step 5 above ("Set up Godot for MCP integration") — identical whether or not you use the container.
-
Install the MCP server packages globally (or let
npxfetch them on demand at connect time):npm install -g @satelliteoflove/godot-mcp @ryanmazzolini/minimal-godot-mcp
-
Register the MCP servers with your agent, pointing at
127.0.0.1instead ofhost.docker.internal:Claude Code:
claude mcp add godot-mcp -s user \ -e GODOT_HOST=127.0.0.1 \ -e GODOT_PORT=6550 \ -- npx -y @satelliteoflove/godot-mcp claude mcp add minimal-godot-mcp -s user \ -e GODOT_LSP_HOST=127.0.0.1 \ -e GODOT_LSP_PORT=6005 \ -e GODOT_WORKSPACE_PATH=/absolute/path/to/your/godot-project \ -- npx -y @ryanmazzolini/minimal-godot-mcp
OpenCode: add the equivalent block to
opencode.jsonin your Godot project root:{ "mcp": { "godot-mcp": { "type": "local", "command": ["npx", "-y", "@satelliteoflove/godot-mcp"], "environment": { "GODOT_HOST": "127.0.0.1", "GODOT_PORT": "6550" } }, "minimal-godot-mcp": { "type": "local", "command": ["npx", "-y", "@ryanmazzolini/minimal-godot-mcp"], "environment": { "GODOT_LSP_HOST": "127.0.0.1", "GODOT_LSP_PORT": "6005", "GODOT_WORKSPACE_PATH": "/absolute/path/to/your/godot-project" } } } } -
Start Godot, then launch your agent as usual (
claude/opencode) from your project directory.
- No port bridge is needed on any platform —
127.0.0.1reaches Godot directly. - Only one MCP client can hold the godot-mcp WebSocket connection at a time — the same "Another MCP server connected and replaced this one" behavior applies if you run Claude Code and OpenCode against Godot simultaneously (see Troubleshooting below).
.devcontainer/poststart.shpatchesminimal-godot-mcp'sdiagnostics-manager.jsso Godot'sworkspaceChangeLSP notification doesn't overwriteGODOT_WORKSPACE_PATHwith an unreachable container path. Running natively this mismatch shouldn't occur, since your workspace path already matches what Godot reports — but if diagnostics start resolving to the wrong file paths, that patch is the place to look.- Skills and
CLAUDE.md/AGENTS.mdare read directly from wherever your agent normally looks (e.g.~/.claude) — noCLAUDE_USER_CONFIG_DIRsymlink step needed.
Run npm run bridge:doctor first — it checks every link in the chain (Godot listener, host-side bridge, container-side relay) and prints which one is broken.
- Ensure Godot is running on the host before launching Claude Code
- Linux: Verify the port bridge is running (
npm run bridge:start) — this starts automatically withnpm run upbut may need restarting if Godot was restarted - macOS / Windows: No bridge needed, but ensure Docker Desktop is running
- Verify the godot-mcp addon is enabled in Project Settings > Plugins
- Check that the LSP server is enabled in Editor Settings > Network > Language Server
The godot-mcp addon allows only one WebSocket client at a time — when a second client connects (e.g. OpenCode), it disconnects the first (e.g. Claude Code). Run only one AI tool against Godot at a time. OpenCode's MCP servers are now registered lazily by npm run opencode (not at container startup), so OpenCode does not squat the connection slot when idle. To switch tools mid-session, exit one before launching the other.
- macOS / Windows: Docker Desktop provides this automatically. Ensure Docker Desktop is up to date.
- Linux (Docker Engine): Requires Docker 20.10+. The
--add-host=host.docker.internal:host-gatewayflag is set indevcontainer.json. Verify with:
devcontainer exec --workspace-folder . ping -c 1 host.docker.internalCredentials are stored in the Docker volume godot-agents-config-<id>. If you destroy the volume (e.g., docker volume prune), you'll need to log in again.
If you get logged out repeatedly — typically on the first session of the day — and you
also run Claude Code elsewhere under the same Anthropic account, that's the rotating
refresh token being invalidated across clients, not lost credentials. Set a long-lived
CLAUDE_CODE_OAUTH_TOKEN as described in Staying logged in across days.
Godot binds to 127.0.0.1 only.
- macOS / Windows: Docker Desktop can reach host localhost ports natively. Ensure Docker Desktop is running and up to date.
- Linux: The host-side bridge relays from the Docker bridge IP to localhost. The bridge starts automatically with
npm run up. If it still fails:- Verify socat is installed (
sudo apt-get install socat) - Manually restart the bridge:
npm run bridge:stop && npm run bridge:start
- Verify socat is installed (
If running Godot natively on Windows with Docker Desktop using the WSL2 backend, host.docker.internal resolves to the Windows host. This should work without additional configuration, but networking through the WSL2 VM can occasionally cause connectivity issues. If MCP tools fail to connect, verify that Godot's ports (6550, 6005) are not blocked by the Windows firewall.