godot-mcp
by blentz
README.md
# godot-mcp
An MCP server that lets an AI agent work on a Godot 4.x project the way a developer does: read and
edit scenes, write scripts, build, test, run, look at the result, and debug what went wrong.
Two things set it apart from guessing at file formats or shelling out blindly:
- **It asks Godot itself.** Scene and resource files are parsed and re-serialized structurally
(proven byte-identical on round-trip against a real project corpus), but anything that depends on
the engine's own behavior — C# introspection, shader compilation, input bindings, editor state —
is answered by actually running Godot headless or querying a live editor, not by reimplementing
Godot's semantics from memory.
- **It fails loudly.** Every measurable failure mode in this project — a missing display, an
unbuilt C# assembly, a closed editor, a non-.NET binary — is a named, distinct error with a
remedy, not a silent empty result. See `docs/capability-matrix.md` for the measurements this is
built on; several of them exist specifically because the obvious signal (exit code, a non-null
return value) turned out to lie.
50 tools, organized into four tiers by what they need to work. See `docs/tools.md` for the full
reference and `docs/troubleshooting.md` for what to do when a tool refuses.
## Requirements
- Node.js >= 20
- A Godot 4.7+ binary (the capability matrix was measured against 4.7; other 4.x versions are
expected to behave similarly but are unverified)
- For any C# tool (`build_csharp`, `run_tests`, `csharp_script_info`, `validate_node_property`,
and C# script paths through `validate_script`): a **.NET/mono build of Godot** — the ordinary
GDScript-only distribution cannot load or introspect `.cs` scripts, and every C# tool detects
this up front and refuses with `NOT_MONO_BINARY` rather than failing partway through. A mono
build's `--version` output contains `.mono.` and it ships a sibling `GodotSharp/` directory.
- For the C# tools specifically, a `dotnet` SDK on `PATH` (`build_csharp` and `run_tests` invoke it
directly; `csharp_script_info` and `validate_node_property` need a Debug build to already exist,
which comes from `build_csharp` or `build_godot_artifacts`).
## Install and build
```sh
git clone <this-repo> godot-mcp
cd godot-mcp
npm install
npm run build
```
This produces `dist/server.js`, the entry point an MCP client runs.
## Configuring an MCP client
Point your client at `dist/server.js` with `node`, and give it a way to find a Godot binary. The
simplest setup sets `GODOT_PATH` explicitly:
```json
{
"mcpServers": {
"godot": {
"command": "node",
"args": ["/path/to/godot-mcp/dist/server.js"],
"env": {
"GODOT_PATH": "/path/to/Godot_v4.7-stable_mono_linux.x86_64"
}
}
}
}
```
### How the server finds a Godot binary
In order, the first one found wins:
1. An explicit `binary` argument passed to a tool call.
2. The `GODOT_PATH` environment variable.
3. A binary vendored under the project's own `vendor/` directory (searched up to 3 levels deep,
preferring one whose filename contains `mono`) — for projects that ship their own Godot build.
4. `godot`, `godot4`, or `godot-mono` on `PATH`.
### How the server finds a project
In order:
1. An explicit `project` argument passed to a tool call.
2. The `GODOT_PROJECT` environment variable.
3. Walking up from the server process's working directory looking for `project.godot`.
If none of these resolves to a directory containing `project.godot`, tools that need a project
fail with `PROJECT_NOT_FOUND`.
### `GODOT_MCP_DOCS_CACHE`
`godot_class_doc` and `search_classes` build a class-reference index by asking the Godot binary
itself, which is slow enough to be worth caching. The index is written under
`$TMPDIR/godot-mcp-docs-cache/<godot-version>/` by default; set `GODOT_MCP_DOCS_CACHE` to put it
somewhere persistent. The cache is keyed by Godot version, so upgrading the binary builds a fresh
index rather than serving a stale one. If your MCP client runs the server with a working directory outside
the target project, set `GODOT_PROJECT` explicitly:
```json
{
"mcpServers": {
"godot": {
"command": "node",
"args": ["/path/to/godot-mcp/dist/server.js"],
"env": {
"GODOT_PATH": "/path/to/Godot_v4.7-stable_mono_linux.x86_64",
"GODOT_PROJECT": "/path/to/your/godot-project"
}
}
}
}
```
Call `godot_status` first in any new session — it reports the resolved binary path, version,
whether it's a mono build, display availability, project root, and whether the C# assembly is
built, so an agent (or you) can see what's actually possible before trying anything.
## The four tiers
Every tool is served by the lowest tier that can answer it. If a tool fails with `TIER_UNAVAILABLE`
or `DISPLAY_REQUIRED`, this is why:
| Tier | Mechanism | Requires |
|---|---|---|
| **A** — file layer | Direct read/write of `.tscn`, `.tres`, `.cs`, `.gd`, `project.godot` | Nothing — no Godot process at all |
| **B** — headless CLI | `godot --headless …` and `dotnet …` subprocesses | A Godot binary, and/or a `dotnet` SDK |
| **C** — editor bridge | A TCP socket to a GDScript editor addon | A running Godot editor, with the addon installed and enabled (below) |
| **D** — display-dependent | A Tier B tool that additionally needs to render a frame | Everything Tier B needs, plus a real display (X11 or Wayland) |
Tier D is not a separate transport — it's a capability constraint on top of Tier B. Only
`capture_screenshot` is Tier D: there is no headless rendering path in Godot, so screenshots
require an actual display, virtual or physical. `run_project` with `windowed: true` has the same
requirement.
Tier A tools (most scene, node, script, and project-config editing) work with no Godot installed
at all — they're pure file operations, tested against a corpus of real `.tscn`/`.tres`/
`project.godot` files re-serialized byte-identically. Tier B needs a Godot binary and/or `dotnet`
on `PATH` or resolved as above. Tier C needs the editor addon (next section) — until it's installed
and an editor with it enabled is running, all five editor-bridge tools fail with
`TIER_UNAVAILABLE`; that's expected, not a bug, and the error message tells you which of two
distinct situations applies (see `docs/troubleshooting.md`).
See `docs/tools.md` for every tool's tier.
## Installing the editor addon (Tier C)
Five tools — `editor_state`, `get_selected_node`, `live_scene_tree`, `open_scene_in_editor`, and
`execute_editor_script` — talk to a live, running Godot editor over a loopback TCP socket instead
of spawning a process. That requires a small GDScript editor addon, installed and enabled in the
target project. There is no install tool: writing into a user's `addons/` directory and mutating
their `project.godot` is exactly what this project's path-jail discipline exists to avoid doing
automatically. This is a one-time manual step:
1. Copy `addon/godot_mcp/` from this repository into the target project's `addons/` directory, so
it ends up at `<project>/addons/godot_mcp/plugin.cfg`.
2. Enable the plugin — either in the editor (Project Settings > Plugins > godot_mcp > Enable), or
by adding it directly to `project.godot`:
```
[editor_plugins]
enabled=PackedStringArray("res://addons/godot_mcp/plugin.cfg")
```
3. Launch (or restart) the Godot editor for that project — headless is fine
(`--headless --editor --path <project>`), no display is required. The addon writes a handshake
file to `<project>/.godot/mcp_bridge.json` on startup; the five tools read it to find the
bridge's port and per-session token.
Note that editor selection state is always empty in a headless editor — that's expected
(`get_selected_node` reports "nothing selected" as a normal result, not an error).
## Safety and scope
- **Path jail.** Every write path — scene, script, resource, screenshot output — is resolved and
required to be inside the project root. A path that escapes it, directly or through a symlink,
is refused with `PATH_OUTSIDE_PROJECT` before anything is touched.
- **`dry_run`.** Every mutating tool accepts `dry_run` and returns a unified diff instead of
writing. Use it to preview a change before committing to it.
- **No backup store.** Version control is the undo system. This is a deliberate simplification —
there is no snapshot or backup mechanism in the server itself. If your project isn't under
version control, use `dry_run` before any mutating call, or start using version control.
- **`execute_editor_script` is not a sandbox.** It runs arbitrary GDScript inside your actual
running editor process, with the same privileges as the editor itself — it can read and mutate
live editor state, the open scene, and anything else reachable from GDScript. Treat it like you
would treat handing an agent a shell: appropriate for a trusted agent working on your own
project, not for untrusted input.
## Testing
```sh
npm test
```
Runs `tsc --noEmit` against both `tsconfig.json` and `tsconfig.test.json`, then the full Vitest
suite. Nearly every test is parser-level: it feeds canned Godot/MSBuild output through the parsers
and asserts on the structured result, so the suite runs with no Godot install and no .NET
toolchain, and stays fast and portable (CI, containers, a laptop with neither installed).
### `GODOT_TEST_BINARY`
Two blocks are different: `tests/integration/tier-b.test.ts` drives a **real** Godot binary —
building temp projects on disk and invoking `validate_script`, `check_shaders`, `run_project`, and
`godot_class_doc` against it — because a parser can be tested against canned text forever without
ever proving the tool's own process-spawning, argument-building, and stream-reading code actually
works against the real engine. `tests/integration/tier-c.test.ts` does the same for the editor
bridge tools, with a materially different requirement: it must launch a real Godot editor as a
long-lived, detached process (editors do not exit on their own) and reliably reap it afterward,
including on test failure.
- **Unset** (the default): both blocks report as `SKIPPED`. Nothing else in `npm test` is
affected.
- **Set** to the path of a Godot 4.7+ binary: both blocks actually execute end-to-end against it.
```sh
GODOT_TEST_BINARY=/path/to/Godot_v4.7-stable_linux.x86_64 npm test
```
A mono/.NET build is not required for this block. If you're also working on the C#-facing tools
(`build_csharp`, `run_tests`), you'll separately want a `dotnet` on `PATH`; there's currently no
equivalent gate variable for that since none of the `GODOT_TEST_BINARY` block's tests need it.
## Documentation
- `docs/tools.md` — every tool, grouped by area, with tier and key inputs.
- `docs/troubleshooting.md` — measured failure modes and their fixes.
- `docs/capability-matrix.md` — the empirical measurements this project's behavior is built on.