godot

SKILL.md

Godot development

Requirements:

  • Godot 4+
  • godot-docs MCP server (npm package @fernforestgames/mcp-server-godot-docs)
  • godot-editor MCP server (GitHub project fernforestgames/mcp-server-godot-editor installed into addons/)

GDScript workflow

When writing or modifying GDScript, copy this checklist and track your progress:

- [ ] Step 1: Read source files
- [ ] Step 2: Look up relevant Godot documentation
- [ ] Step 3: Write or modify GDScript code
- [ ] Step 4: Import new files with Godot CLI
- [ ] Step 5: Typecheck and validate syntax
- [ ] Step 6: Test the changes

Step 1: Read source files

Read any relevant GDScript source files in the current project.

Step 2: Look up relevant Godot documentation

Use the godot-docs:get_godot_class tool to look up everything about a specific Godot class. Use the godot-docs:search_godot_docs tool to find a string in the Godot documentation.

Step 3: Write or modify GDScript code

Adhere to the following rules while writing GDScript:

  • Use the latest Godot 4.5 syntax and features, including typed arrays and dictionaries: Array[int], Dictionary[String, float], etc.
  • Always use static typing
  • Don't use := type inference declarations for AI-generated code (prefer explicit annotations)
  • Prefix private variables/methods with _
  • Indent with tabs
  • Use @export or dependency injection instead of hardcoded node paths
  • Prefer methods with static typing over equivalent StringName variants

Step 4: Import new files with Godot CLI

Run the Godot CLI with --import after adding new files, to make sure they get picked up by the editor:

godot --headless --import

This should be run in the project root directory (where project.godot is located). Remember that any folder containing .gdignore will be skipped during import.

Step 5: Typecheck and validate syntax

Directly invoke the provided check_syntax.sh script to check the syntax of all GDScript files in the project.

For example:

./.claude/skills/godot/scripts/check_syntax.sh

Step 6: Test the changes

Use the testing workflow described below to test your changes.

Testing workflow

Automated tests

Unit tests can be written with the GUT (Godot Unit Test) framework: https://github.com/bitwes/Gut https://gut.readthedocs.io

If not already present in the current project in the addons/ directory, do NOT attempt to install it. Ask the user to install it from the Godot Asset Library instead.

Important: use a sub-agent to write and run GUT tests, as this consumes a lot of tokens.

Writing tests

Test cases should be scripts that extend GutTest:

extends GutTest

# GUT assertions are untyped, so disable this warning
@warning_ignore_start("unsafe_call_argument")

func before_all():
	pass

func before_each():
	pass

func test_passes():
	assert_eq(1, 1)

func test_fails():
	assert_eq("hello", "goodbye")

func after_each():
	pass

func after_all():
	pass

Run the Godot CLI with --import after adding new files, to make sure they get picked up by the editor:

godot --headless --import

This should be run in the project root directory (where project.godot is located).

Assertions

assert_true(got)
assert_false(got)
assert_null(got)
assert_not_null(got)

assert_eq(got, expected)
assert_eq_deep(got, expected)  # Like assert_eq but more detailed failure messages for nested collections
assert_almost_eq(got, expected, error_interval)
assert_ne(got, not_expected)
assert_ne_deep(got, not_expected)
assert_almost_ne(got, not_expected, error_interval)
assert_gt(got, expected)
assert_gte(got, expected)
assert_lt(got, expected)
assert_lte(got, expected)

assert_same(got, expected)  # Checks that reference types (Object, Dictionary, Array) are exactly the same reference
assert_not_same(got, expected)

assert_has(obj, element)
assert_does_not_have(obj, element)

assert_is(obj, class_or_script_obj)
assert_typeof(obj, type_constant)  # TYPE_INT, TYPE_STRING, etc.
assert_not_typeof(obj, type_constant)

assert_engine_error(matching_text)
assert_engine_error_count(count)
assert_push_error(matching_text)
assert_push_error_count(count)
assert_push_warning(matching_text)
assert_push_warning_count(count)

assert_freed(obj)
assert_not_freed(obj)

Helpers

await wait_seconds(10)
await wait_idle_frames(5)  # 5 _process frames
await wait_physics_frames(2)
await wait_for_signal(scene_tree.node_removed, 3)  # time out after 3 seconds
await wait_until(callable, 3)  # time out after 3 seconds

Running tests

Once tests have been written and imported, run them using the GUT command line script:

godot --headless --script addons/gut/gut_cmdln.gd

# Run a specific test file
godot --headless --script addons/gut/gut_cmdln.gd -gtest=res://tests/sample_tests.gd

GUT is configured with an optional res://.gutconfig.json file. A sample can be generated with:

godot --headless --script addons/gut/gut_cmdln.gd -gprint_gutconfig_sample

Interactive testing with the Godot Editor MCP server

The godot-editor MCP server allows you to control running games from the editor, enabling automated UI testing and visual verification.

Starting and stopping scenes

godot-editor:play_main_scene      # Start the project's main scene
godot-editor:play_scene           # Start a specific scene by path (e.g., "res://levels/test.tscn")
godot-editor:stop_playing_scene   # Stop the currently running scene

Important: Always follow up play_main_scene or play_scene with another action (screenshot, input, etc.) or stop_playing_scene. Never leave a scene running indefinitely.

Taking screenshots

Use godot-editor:take_screenshot to capture the current game viewport. The screenshot is returned as an image that you can view directly.

Node interaction

Use godot-editor:click_node and godot-editor:hover_node to interact with controls and nodes (2D or 3D). Use godot-editor:get_node_tree to inspect the current scene tree and determine node paths.

godot-editor:click_node
  node_path: "/root/Main/UI/StartButton"
  button_index: 1  # Optional, defaults to left click
  offset: {x: 0, y: 0}  # Optional offset from center

godot-editor:hover_node
  unique_name: "InventorySlot"

godot-editor:click_node
  accessibility_name: "SubmitButton"

Input synthesis

Use godot-editor:synthesize_input to simulate user input events (only if you cannot use click_node or hover_node).

For example:

  • type: "key", keycode: "Space", pressed: true (uses Key enum names)
  • type: "mouse_button", button_index: 1, position: {x, y}, pressed: true
  • type: "mouse_motion", position: {x, y}
  • type: "action", action: "ui_accept", pressed: true

Example workflow for UI testing

  1. godot-editor:play_main_scene
  2. godot-editor:take_screenshot, verify initial state
  3. godot-editor:get_node_tree, check node paths
  4. godot-editor:hover_node with unique_name "PlayButton"
  5. godot-editor:take_screenshot, verify hover visual feedback
  6. godot-editor:click_node with unique_name "PlayButton"
  7. godot-editor:take_screenshot, verify button was clicked (new screen, animation, etc.)
  8. godot-editor:stop_playing_scene

Godot resource files (.tres, .tscn)

  • NEVER manually assign or generate uid:// fields—Godot fills these in automatically

Connecting @exports in scene files

When a script uses @export var some_node: SomeNodeType, you can assign it via a .tscn file:

[node name="MyNode" type="Node3D" parent="." node_paths=PackedStringArray("player", "camera")]
script = ExtResource("1_abc123")
player = NodePath("../Player")
camera = NodePath("CameraPivot/Camera3D")
Weekly Installs
4
GitHub Stars
2
First Seen
3 days ago
Installed on
mcpjam4
claude-code4
replit4
junie4
windsurf4
zencoder4