Skip to content

Commit ae16c18

Browse files
dsarnoclaude
andcommitted
Update docs: handler/runtime layer, reload workflow, Codex client, test counts
CLAUDE.md: - Add streamable-http transport to architecture - Document handler/runtime abstraction pattern - Update 'adding a new tool' steps for handler layer - Add Codex to client configuration list - Document reload_plugin tool and ASGI reload path - Update test count: 81 -> 140 implementation-plan.md: - Mark CI setup tasks as done (Tier 1+2, Codecov, badges) - Add reload workflow items as completed - Add Codex client and handler/runtime layer as completed - Update test count: 125 -> 184 - Add project_handler.gd to plugin file listing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3ee0666 commit ae16c18

2 files changed

Lines changed: 36 additions & 19 deletions

File tree

CLAUDE.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ A production-grade MCP server for Godot. Python server (FastMCP v3) communicates
77
## Architecture
88

99
```
10-
AI Client → MCP (stdio/sse) → Python FastMCP server → WebSocket (port 9500) → Godot EditorPlugin
10+
AI Client → MCP (stdio/sse/streamable-http) → Python FastMCP server → WebSocket (port 9500) → Godot EditorPlugin
1111
```
1212

1313
- **Python server**: `src/godot_ai/` — FastMCP v3, async, lifespan manages WebSocket server
14-
- **GDScript plugin**: `plugin/addons/godot_ai/` — canonical source; copied into `test_project/addons/` for testing
14+
- **GDScript plugin**: `plugin/addons/godot_ai/` — canonical source; symlinked into `test_project/addons/` for testing
1515
- **Protocol**: JSON over WebSocket. Request/response with `request_id` correlation. Handshake on connect.
1616
- **Session model**: Multiple Godot editors can connect. Tools route through active session.
17+
- **Handler/Runtime layer**: Shared handlers in `src/godot_ai/handlers/` contain tool logic. They depend on a `Runtime` protocol (`runtime/interface.py`), implemented by `DirectRuntime` for the in-process server. Tools and resources are thin wrappers that create a runtime and delegate.
1718

1819
## Key conventions
1920

2021
- **GDScript plugin is the canonical copy** in `plugin/`. `test_project/addons/godot_ai` is a symlink — no copy needed.
2122
- **Error codes**: Defined in `protocol/errors.py` (Python) and `utils/error_codes.gd` (GDScript). Keep in sync.
22-
- **Tools return `dict`**: `GodotClient.send()` returns `response.data` (a dict) or raises `GodotCommandError`. Tools just `return await app.client.send(...)`.
23+
- **Tools return `dict`**: Handlers call `runtime.send_command(command, params)` which returns a dict or raises. Tools create a `DirectRuntime` and delegate to handlers.
2324
- **Plugin runs on main thread**: All GDScript executes in `_process()` with a 4ms frame budget. Never block. Use `call_deferred` for scene tree mutations.
2425
- **Scene paths are clean**: `/Main/Camera3D` format, not raw Godot internal paths. Use `ScenePath.from_node(node, scene_root)` in GDScript.
2526
- **MCP logging**: Plugin prints `MCP | [recv] command(params)` / `MCP | [send] command -> ok` to Godot console. Controlled by `mcp_logging` var.
@@ -40,21 +41,26 @@ ruff format src/ tests/ # format
4041
### Server lifecycle in dev
4142

4243
The plugin manages the server process:
43-
- **Reload Plugin** in the Godot dock kills the old server, starts a new one from `.venv/bin/python -m godot_ai`
44-
- After Reload Plugin, do `/mcp` in Claude Code to reconnect
45-
46-
The plugin prefers the local `.venv` over system-installed `godot-ai` so dev checkouts always use source code.
44+
- On startup, plugin checks if port 8000 is already in use. If yes, uses existing server. If no, spawns `.venv/bin/python -m godot_ai --transport streamable-http --port 8000`.
45+
- The plugin prefers the local `.venv` over system-installed `godot-ai` so dev checkouts always use source code.
4746

4847
For Python auto-reload during dev (no need to touch Godot):
4948
```bash
5049
python -m godot_ai --transport streamable-http --port 8000 --reload
5150
```
51+
This uses `src/godot_ai/asgi.py` to run uvicorn with its factory reload path. Uvicorn watches `src/` for changes and restarts the server process automatically. The plugin auto-reconnects.
52+
53+
### Plugin reload
54+
55+
The `reload_plugin` MCP tool triggers a live plugin reload inside Godot (`EditorInterface.set_plugin_enabled` off/on). Requires the server to be running externally (not managed by the plugin). The Python handler waits for the new session via `SessionRegistry.wait_for_session()`.
56+
57+
The Godot dock also has a **Start/Stop Dev Server** button for convenience.
5258

5359
## Testing
5460

5561
### Python tests
5662
```bash
57-
pytest -v # 81 unit + integration tests
63+
pytest -v # 140 unit + integration tests
5864
```
5965

6066
### Godot-side tests
@@ -78,17 +84,19 @@ Test suites extend `McpTestSuite` (assertion methods: `assert_true`, `assert_eq`
7884

7985
The plugin can configure MCP clients via `client_configurator.gd`:
8086
- **Claude Code**: uses `claude mcp add` CLI to register the server
87+
- **Codex**: writes TOML config to `~/.codex/config.toml`
8188
- **Antigravity**: writes directly to `~/.gemini/antigravity/mcp_config.json`
8289

8390
MCP tools `client_configure` and `client_status` expose this to AI clients.
8491

8592
## Adding a new tool
8693

87-
1. Add a handler method in the appropriate `handlers/*.gd` file
94+
1. Add a handler method in the appropriate GDScript `handlers/*.gd` file
8895
2. Register it in `plugin.gd`: `_dispatcher.register("command_name", handler.method)`
89-
3. Add a Python tool in `tools/<domain>.py` that calls `app.client.send("command_name", params)`
90-
4. Register the tool module in `server.py` if it's a new file
91-
5. Add tests: Python integration test in `tests/` AND GDScript test in `test_project/tests/`
96+
3. Add a shared Python handler in `handlers/<domain>.py` that calls `runtime.send_command("command_name", params)`
97+
4. Add a Python tool in `tools/<domain>.py` that creates `DirectRuntime` and delegates to the handler
98+
5. Register the tool module in `server.py` if it's a new file
99+
6. Add tests: handler unit test, Python integration test, AND GDScript test in `test_project/tests/`
92100

93101
## Write tools must be undoable
94102

docs/implementation-plan.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,10 @@ addons/godot_ai/
207207
├── dispatcher.gd # Command queue, frame-budget dispatch, handler routing
208208
├── mcp_dock.gd # Editor dock panel
209209
├── handlers/
210-
│ ├── editor_handler.gd # editor_state, selection, logs
210+
│ ├── editor_handler.gd # editor_state, selection, logs, reload_plugin
211211
│ ├── scene_handler.gd # scene tree reading
212212
│ ├── node_handler.gd # node create (with undo)
213+
│ ├── project_handler.gd # project settings, filesystem search
213214
│ └── client_handler.gd # client configure/status
214215
└── utils/
215216
├── scene_path.gd # from_node(), resolve()
@@ -425,7 +426,7 @@ The product should be useful for inspection and navigation before any write tool
425426
| Reconnect button | `Button` | Calls `_attempt_reconnect()` |
426427
| Reload Plugin button | `Button` | Toggles plugin off/on |
427428
| Setup status | Dev mode / uv version | Auto-detected |
428-
| Client config | Configure buttons per client | Claude Code, Antigravity |
429+
| Client config | Configure buttons per client | Claude Code, Codex, Antigravity |
429430

430431
### Pagination design
431432

@@ -450,8 +451,14 @@ Large results (scene trees with 1000+ nodes, long log buffers) need pagination:
450451
- [x] Batch 3: Project reads (project_settings.get, filesystem.search)
451452
- [x] Batch 4: MCP Resources (7 resources: sessions, scene/current, scene/hierarchy, selection/current, project/info, project/settings, logs/recent)
452453
- [x] Batch 5: Editor dock panel with setup status
453-
- [x] Batch 6: Test harness (44 Godot-side + 81 Python = 125 total tests)
454+
- [x] Batch 6: Test harness (44 Godot-side + 140 Python = 184 total tests)
454455
- [x] Pagination for large results (offset/limit on scene_get_hierarchy, logs_read, node_find, filesystem_search)
456+
- [x] Handler/runtime abstraction layer (shared handlers depend on Runtime protocol, not FastMCP context)
457+
- [x] Codex client configurator (TOML config at `~/.codex/config.toml`)
458+
- [x] `reload_plugin` tool — triggers live plugin reload, waits for new session via Future-based waiter
459+
- [x] ASGI reloadable entrypoint (`--reload` uses uvicorn factory path for Python auto-reload)
460+
- [x] Dev server start/stop controls in Godot dock panel
461+
- [x] Reload smoke test in CI (creates node, reloads plugin, verifies log buffer fresh + scene tree survived)
455462
- [ ] Manual test: Claude describes the open scene
456463

457464
---
@@ -500,10 +507,12 @@ Run on every push and PR. Three-tier matrix:
500507
- Runs on release tags only (not every push)
501508

502509
**Setup tasks:**
503-
- [ ] Create `.github/workflows/ci.yml` with Tier 1 (Python tests)
504-
- [ ] Add Tier 2 with Godot headless (investigate `chickensoft-games/setup-godot` action)
505-
- [ ] Add Tier 3 for release smoke tests
506-
- [ ] Add status badges to README
510+
- [x] Create `.github/workflows/ci.yml` with Tier 1 (Python tests) — 6 jobs: 3 OS x 2 Python versions
511+
- [x] Add Tier 2 with Godot headless — 3 jobs: Linux (Docker), macOS, Windows using `chickensoft-games/setup-godot`
512+
- [x] Add reload smoke test to Tier 2 (reload_plugin e2e on all 3 OSes)
513+
- [x] Add Codecov integration with patch coverage check
514+
- [x] Add status badges to README
515+
- [ ] Add Tier 3 for release smoke tests (uvx install path)
507516

508517
---
509518

0 commit comments

Comments
 (0)