Skip to main content
Glama
mikeumus
by mikeumus
README.md
# Godot MCP RTS

Extended MCP (Model Context Protocol) server for the Godot game engine, with
**RTS agentic testing**, **scene/script introspection & mutation**, and
**live game screenshot capture**.

Forked from [Coding-Solo/godot-mcp](https://github.com/Coding-Solo/godot-mcp).

[![](https://badge.mcpx.dev?type=server 'MCP Server')](https://modelcontextprotocol.io/introduction)
[![Made with Godot](https://img.shields.io/badge/Made%20with-Godot-478CBF?style=flat&logo=godot%20engine&logoColor=white)](https://godotengine.org)
[![](https://img.shields.io/badge/TypeScript-3178C6?style=flat&logo=typescript&logoColor=white 'TypeScript')](https://www.typescriptlang.org/)
[![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.x-blue 'MCP SDK 1.x')](https://www.npmjs.com/package/@modelcontextprotocol/sdk)

## What this fork adds

On top of the original 14 godot-mcp tools, this fork ships **15 additional
tools** in three groups:

- **Test harness** — agentic game testing (`test_get_info`,
  `test_get_game_state`, `test_validate_scenario`, `test_check_entity`,
  `test_validate_paths`, `test_run_scenario`, `test_check_mlx`)
- **Scene & script introspection / mutation** — `get_scene_tree`,
  `remove_node`, `set_node_property`, `create_script`, `attach_script`,
  `list_scripts`
- **Visual capture** — `take_screenshot` (script-mode, standalone scenes)
  and `test_take_screenshot` (live game via TestController IPC, real
  scenes with autoloads)

Plus infrastructure improvements:

- Upgraded to **MCP SDK 1.x** (from 0.6.0)
- `preflightProject()` helper retrofit on the scene-op handlers (−150 LoC of
  validation boilerplate)
- Dropped the unused `axios` dependency (`npm audit` clean)
- End-to-end test suite (`npm test`) covering protocol handshake, all
  mutation tools, and live screenshot capture

## Tool reference

All 29 tools at a glance:

### Original Godot MCP tools (14)

| Tool | Purpose |
|---|---|
| `launch_editor` | Open the Godot editor for a project |
| `run_project` | Run a project in debug mode and capture output |
| `get_debug_output` | Read stdout/stderr from the running project |
| `stop_project` | Stop the running project |
| `get_godot_version` | Print the installed Godot version |
| `list_projects` | Find Godot projects in a directory |
| `get_project_info` | Project metadata + counts of scenes/scripts/assets |
| `create_scene` | Create a new `.tscn` with a chosen root node type |
| `add_node` | Add a node to a scene |
| `load_sprite` | Load a texture into a Sprite2D/Sprite3D/TextureRect |
| `export_mesh_library` | Export a scene as a `.res` MeshLibrary |
| `save_scene` | Save a scene (optionally to a new path) |
| `get_uid` | Get the UID of a file (Godot 4.4+) |
| `update_project_uids` | Resave resources to refresh UID references |

### Test harness tools (7)

| Tool | Purpose |
|---|---|
| `test_get_info` | Test harness capabilities + project info |
| `test_get_game_state` | Snapshot of autoloads, constants, config |
| `test_validate_scenario` | Validate a scenario config before running it |
| `test_check_entity` | Verify a unit/building/resource scene exists |
| `test_validate_paths` | Validate all expected paths for a race |
| `test_run_scenario` | Launch the game with `--test-harness` |
| `test_check_mlx` | MLX local LLM health + Apple Silicon platform info |

### Scene & script tools (6, new in v0.3.0)

| Tool | Purpose |
|---|---|
| `get_scene_tree` | Hierarchical JSON dump of a scene's nodes |
| `remove_node` | Delete a node from a scene and save |
| `set_node_property` | Bulk-set properties on an existing node |
| `create_script` | Create a `.gd` file (custom content or `extends` template) |
| `attach_script` | Attach an existing script to a node in a scene |
| `list_scripts` | Recursively list `.gd` files in a project or subdir |

### Visual capture tools (2, new in v0.3.0)

| Tool | Purpose |
|---|---|
| `take_screenshot` | Render a scene via `--script` and save PNG. **Standalone scenes only** — does not load autoloads. |
| `test_take_screenshot` | Capture the currently running game via file-based IPC with TestController. **Real game scenes** with autoloads loaded. |

## Installation

```bash
git clone https://github.com/mikeumus/godot-mcp-rts.git
cd godot-mcp-rts
npm install      # also runs npm run build via the prepare hook
```

## Configuration

### Claude Code

Add to your MCP settings (`.claude/settings.json` or global settings):

```json
{
  "mcpServers": {
    "godot": {
      "command": "node",
      "args": ["/absolute/path/to/godot-mcp-rts/build/index.js"],
      "env": {
        "GODOT_PATH": "/Applications/Godot.app/Contents/MacOS/Godot"
      }
    }
  }
}
```

### Cline / Cursor / other MCP clients

Same configuration format. The server speaks MCP over stdio.

### Environment variables

- `GODOT_PATH` — Path to the Godot executable. If unset, the server
  auto-detects common install locations on macOS / Windows / Linux.
- `DEBUG` — Set to `true` for verbose stderr logging.

## Usage examples

### Inspect a scene

```
get_scene_tree(
  projectPath: "/path/to/game",
  scenePath: "maps/test_map.tscn",
  includeProperties: true
)
```

Returns nested JSON with `name`, `type`, `path`, `script`, `children`, and
optionally `properties.position` / `properties.visible` for each node.

### Create a script and attach it

```
create_script(
  projectPath: "/path/to/game",
  scriptPath: "scripts/my_unit.gd",
  extendsClass: "CharacterBody3D"
)

attach_script(
  projectPath: "/path/to/game",
  scenePath: "units/my_unit.tscn",
  nodePath: "MyUnit",
  scriptPath: "scripts/my_unit.gd"
)
```

### Set node properties

```
set_node_property(
  projectPath: "/path/to/game",
  scenePath: "units/my_unit.tscn",
  nodePath: "MyUnit",
  properties: { visible: false, modulate.r: 0.5 }
)
```

> **Note**: Property names are converted to snake_case before reaching
> Godot. Built-in Godot properties (`position`, `rotation_degrees`, etc.)
> are already snake_case so this is a no-op for them. Vector/Color values
> can't currently round-trip through JSON; pass scalar properties only.

### Run a test scenario

```
test_run_scenario(
  projectPath: "/path/to/game",
  timeout: 30000
)
```

Launches the game with the `--test-harness` flag, capturing stdout for
later inspection via `get_debug_output`.

### Capture a live game screenshot

```
# 1. Start the game (must be running for IPC to work)
run_project(projectPath: "/path/to/game")

# 2. Wait a few seconds for it to render the first frame, then
test_take_screenshot(projectPath: "/path/to/game")
# → Screenshot saved to: /path/to/game/.mcp_screenshots/capture_<id>.png

# 3. Stop when done
stop_project()
```

## TestController integration

For the **test harness tools** and **`test_take_screenshot`** to work, your
Godot project needs to include the `TestController` autoload from
`tests/harness/test_controller.gd` (or its parent-repo equivalent).

### Setup

1. Copy `tests/harness/test_controller.gd` into your project
2. Register it as an autoload (Project Settings → AutoLoad)
3. The controller activates when the game runs with `--test-harness`,
   `--test-mode`, or when the `GODOT_MCP_TEST` env var is set
4. The screenshot polling code is independent of activation — it runs in
   any **debug build** so `test_take_screenshot` works even from a plain
   `run_project` invocation

### Screenshot IPC architecture

`test_take_screenshot` uses a file-based IPC handshake instead of trying
to capture from a headless `--script` subprocess (which can't load
autoloads or the main scene):

```
MCP server                                       Running game
─────────────                                    ────────────
1. Write request.json     ──→   .mcp_screenshots/    ─→  TestController._process()
   { request_id, output_path? }                            polls every 6 frames

                                                        2. Detect request
                                                        3. get_viewport()
                                                            .get_texture()
                                                            .get_image()
                                                            .save_png(...)

4. Poll for response.json ←──   .mcp_screenshots/    ←─  Write response.json
   { request_id, status, absolute_path }                    Print TestController log
```

The polling is gated on `OS.is_debug_build()` so exported builds are
unaffected. The polling interval is 6 frames (~10 Hz at 60 fps), which is
fast enough to feel synchronous from the MCP side and slow enough to be
free.

The IPC workspace lives at `<projectPath>/.mcp_screenshots/`. Add it to
your `.gitignore`:

```
.mcp_screenshots/
```

## Testing

This project has an end-to-end test suite that exercises a real Godot
binary. There are no unit tests — the value of testing this server lives
almost entirely in verifying that real `godot --headless --script`
invocations round-trip correctly, so e2e is the right shape.

```bash
npm run build         # always build first

npm test              # smoke + mutations (~35s, no game window)
npm run test:smoke    # ~3s   protocol handshake + tool registration
npm run test:mutations # ~30s every scene/script tool against a sandbox project
npm run test:screenshot # ~15s INTRUSIVE: opens a game window, opt-in
```

The screenshot test is opt-in and gated behind the `MCP_TEST_PROJECT_PATH`
env var:

```bash
MCP_TEST_PROJECT_PATH=/path/to/your/game npm run test:screenshot
```

It validates the full `test_take_screenshot` pipeline against a live game,
including the TestController IPC handshake. See
[`tests/e2e/README.md`](tests/e2e/README.md) for the full breakdown.

## Architecture

```
Claude Code / Cursor / etc.
       │
       │ MCP over stdio (SDK 1.x)
       ▼
godot-mcp-rts (Node/TypeScript)
       │
       ├──→ child_process → godot --headless --script godot_operations.gd <op> <json>
       │                    (most tools: scene mutation, script create, etc.)
       │
       ├──→ child_process → godot --headless --script test_operations.gd <op> <json>
       │                    (test harness tools: state snapshots, validation)
       │
       ├──→ child_process → godot <project> [--debug | --test-harness]
       │                    (run_project, test_run_scenario; long-running)
       │
       └──→ file-based IPC at <project>/.mcp_screenshots/request.json
                            (test_take_screenshot, polled by TestController)
```

## Development

```bash
npm run build        # compile TypeScript, copy .gd scripts to build/
npm run watch        # tsc --watch
npm run inspector    # launch the MCP inspector UI against the built server
npm test             # run the e2e suite (see Testing above)
```

When adding a new tool, see [CONTRIBUTING.md](CONTRIBUTING.md) for the
full checklist.

## Project layout

```
godot-mcp-rts/
├── src/
│   ├── index.ts                      # MCP server, tool definitions, handlers
│   └── scripts/
│       ├── godot_operations.gd       # Headless GDScript dispatcher (file ops)
│       └── test_operations.gd        # Headless GDScript dispatcher (test harness)
├── tests/
│   └── e2e/
│       ├── _client.mjs               # JSON-RPC stdio client + tiny test runner
│       ├── smoke.mjs                 # Protocol + tool registration check
│       ├── mutations.mjs             # All scene/script tools, 20 assertions
│       ├── screenshot.mjs            # Live game capture via TestController IPC
│       └── README.md                 # How to run / extend the suite
├── scripts/
│   └── build.js                      # Post-tsc copy of .gd files into build/
├── build/                            # Compiled JS + copied scripts (gitignored)
├── README.md
├── CONTRIBUTING.md
├── LICENSE                           # MIT
├── package.json
└── tsconfig.json
```

## License

MIT — see [LICENSE](LICENSE).

## Credits

- Original [godot-mcp](https://github.com/Coding-Solo/godot-mcp) by Coding-Solo
- RTS testing extension and v0.3.0 tool additions by the As Above, So Below team

TDQS

B3.3/5.0

Scored across 29 tools

Disambiguation4/5

Most tools have clearly distinct purposes, though test_check_entity and test_validate_paths overlap somewhat in validating scene paths. The two screenshot tools are adequately differentiated by their descriptions, but the large number of test_* tools could cause brief confusion.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern, using lowercase with underscores. Prefixes like 'get_', 'list_', 'create_', 'test_' are applied uniformly, making the naming predictable and readable.

Tool Count2/5

At 29 tools, the count exceeds the 25 threshold for 'too many' per the calibration. While the server covers multiple sub-domains (project management, scene editing, testing, screenshots), the sheer number makes the toolset feel heavy and harder to navigate.

Completeness4/5

The toolset covers the core lifecycle of Godot project management, scene editing, scripting, testing, and screenshots. Missing operations like scene deletion or project settings editing are minor gaps that agents can work around.

Maintenance

ActivityInactive
ResponsivenessNo issues