|
| 1 | +# Codebase Summary: Godot MCP Server |
| 2 | + |
| 3 | +## Key Components and Their Interactions |
| 4 | + |
| 5 | +### 1. MCP Server (`src/index.ts`) |
| 6 | +- **Purpose:** Acts as the bridge between the MCP client (e.g., AI assistant) and the Godot engine. |
| 7 | +- **Functionality:** |
| 8 | + - Initializes an MCP server using `@modelcontextprotocol/sdk`. |
| 9 | + - Defines available tools (`create_scene`, `add_node`, etc.) with their input/output schemas using `setRequestHandler`. |
| 10 | + - Handles incoming `CallToolRequest` messages. |
| 11 | + - Normalizes incoming parameters (camelCase to snake_case). |
| 12 | + - Detects the Godot executable path. |
| 13 | + - Constructs command-line arguments, including the operation name and a JSON string of parameters. |
| 14 | + - Invokes the `godot_operations.gd` script using `child_process.spawn` (configured with `shell: false`). |
| 15 | + - Captures stdout/stderr from the Godot process. |
| 16 | + - Formats and returns the result or error to the MCP client. |
| 17 | +- **Key Methods:** `constructor`, `setupToolHandlers`, `executeOperation`, individual tool handlers (`handleCreateScene`, etc.). |
| 18 | + |
| 19 | +### 2. Godot Operations Script (`src/scripts/godot_operations.gd`) |
| 20 | +- **Purpose:** Executes specific Godot engine actions based on commands received from the MCP server. |
| 21 | +- **Functionality:** |
| 22 | + - Runs as a headless Godot script (`extends SceneTree`). |
| 23 | + - Parses command-line arguments to get the operation name and JSON parameter string. |
| 24 | + - Uses `JSON.parse()` to convert the JSON string into a Godot Dictionary (`params`). |
| 25 | + - Uses a `match` statement to call the appropriate function based on the operation name. |
| 26 | + - Contains functions for each tool operation (e.g., `create_scene`, `add_node`). |
| 27 | + - Interacts with Godot APIs (`ResourceSaver`, `PackedScene`, `Node`, `ClassDB`, `DirAccess`, `FileAccess`, etc.) to perform actions. |
| 28 | + - Includes specific cleanup logic (e.g., freeing instantiated nodes (`scene_root.free()`), adding delays) in functions like `add_node`, `create_scene`, `load_sprite`, `export_mesh_library`, and `save_scene` to prevent resource leaks or issues in headless mode. |
| 29 | + - Prints success messages to stdout or error messages to stderr. |
| 30 | + - Exits using `quit()`. |
| 31 | +- **Key Methods:** `_init`, `create_scene`, `add_node`, `load_sprite`, `export_mesh_library`, `save_scene`, etc., `log_*`, `instantiate_class`. |
| 32 | + |
| 33 | +### Interaction Flow (`create_scene` example) |
| 34 | +1. MCP client sends `CallToolRequest` for `create_scene` with arguments (projectPath, scenePath, rootNodeType). |
| 35 | +2. `src/index.ts` receives the request via `setRequestHandler(CallToolRequestSchema, ...)`. |
| 36 | +3. `handleCreateScene` is called. |
| 37 | +4. Parameters are normalized (camelCase to snake_case). |
| 38 | +5. `executeOperation` is called with operation "create_scene" and snake_case params. |
| 39 | +6. `executeOperation` converts params to a JSON string. |
| 40 | +7. `executeOperation` calls `spawn` with the Godot executable path, `--headless`, `--path`, `--script`, the operation name ("create_scene"), and the JSON string as arguments (`shell: false`). |
| 41 | +8. `godot_operations.gd` starts execution. |
| 42 | +9. `_init` parses command-line args, extracts "create_scene" and the JSON string. |
| 43 | +10. `_init` calls `JSON.parse()` on the JSON string. |
| 44 | +11. `_init` calls the `create_scene(params)` function with the parsed Dictionary. |
| 45 | +12. `create_scene` function uses Godot APIs to create the root node, pack the scene, ensure the directory exists, and save the `.tscn` file using `ResourceSaver.save()`. |
| 46 | +13. `create_scene` prints success/error messages. |
| 47 | +14. Godot script exits (`quit()`). |
| 48 | +15. `executeOperation` in `src/index.ts` resolves its promise with stdout/stderr. |
| 49 | +16. `handleCreateScene` processes stdout/stderr and returns a result object to the MCP client. |
| 50 | + |
| 51 | +## Data Flow |
| 52 | +- **Input:** MCP `CallToolRequest` with JSON arguments (camelCase or snake_case). |
| 53 | +- **Internal:** |
| 54 | + - Node.js normalizes arguments to snake_case. |
| 55 | + - Node.js serializes snake_case arguments to a JSON string. |
| 56 | + - JSON string passed as a command-line argument to GDScript. |
| 57 | + - GDScript parses the JSON string back into a Dictionary. |
| 58 | +- **Output:** MCP response object containing success/error message and relevant data (e.g., created scene path). Stdout/stderr from Godot script captured by Node.js. |
| 59 | + |
| 60 | +## External Dependencies |
| 61 | +- **@modelcontextprotocol/sdk:** Core dependency for MCP communication. Managed via `package.json` and `npm`. |
| 62 | +- **Godot Engine:** External executable. Path is auto-detected or configured via environment variable/server config. The server relies on Godot's command-line interface and headless execution capabilities. |
| 63 | + |
| 64 | +## Recent Significant Changes |
| 65 | +- **Argument Passing:** Switched from `spawn` with `shell: true` and manual escaping to `spawn` with `shell: false` and passing the raw JSON string. This resolved issues with argument parsing on Windows. |
| 66 | +- **MCP SDK Usage:** Refactored tool registration from `server.registerTool` to using `server.setRequestHandler` for `ListToolsRequestSchema` and `CallToolRequestSchema`, aligning with current SDK practices. |
| 67 | +- **GDScript Error Fix:** Removed incorrect `scene_root.owner = scene_root` assignment in `create_scene` function, resolving a Godot engine assertion error during `PackedScene.pack()`. |
| 68 | +- **Logging:** Added detailed debug logging in both `index.ts` and `godot_operations.gd` to trace argument passing and execution flow. |
| 69 | +- **`add_node` Fixes:** Debugged and resolved multiple issues in the `add_node` GDScript function: |
| 70 | + - Addressed initial "RID allocation leak" errors by adding `scene_root.free()`. |
| 71 | + - Resolved subsequent node duplication issues (likely caused by freeing `scene_root` too early) by adding a small delay (`OS.delay_msec(100)`) after `ResourceSaver.save()` but before `scene_root.free()` in `add_node`. |
| 72 | + - Corrected a "Can't free a RefCounted object" error introduced by incorrectly trying to free the `PackedScene` resource. |
| 73 | +- **RID Leak Fix:** Added missing `scene_root.free()` calls to the end of `create_scene`, `load_sprite`, `export_mesh_library`, and `save_scene` functions in `godot_operations.gd` to resolve RID allocation leaks observed during testing. |
| 74 | +- **`resave_resources` Fix:** Addressed issues with the `resave_resources` tool: |
| 75 | + - Modified `godot_operations.gd` (`_init`) to make the JSON parameter optional and default to an empty dictionary if not provided. |
| 76 | + - Added explicit parameter validation checks within specific GDScript functions that require them. |
| 77 | + - Added a small delay (`OS.delay_msec(100)`) before `quit()` in `godot_operations.gd` to potentially aid output buffer flushing. |
| 78 | + - Updated the `handleUpdateProjectUids` function in `index.ts` to infer success based on the Godot script's exit code (0) and the absence of "ERROR" in stderr, improving reliability over parsing stdout. |
| 79 | + |
| 80 | +## User Feedback Integration |
| 81 | +- N/A (No user feedback integrated yet for this specific task). |
0 commit comments