| name | godot |
| description | MANDATORY for ALL GDScript/.gd files and Godot resources (.tscn, .tres). Invoke this skill FIRST before writing ANY Godot code—never write GDScript directly without this skill. Provides documentation lookup, syntax checking, and testing instructions. |
Godot development
Requirements:
- Godot 4+
godot-docsMCP server (npm package@fernforestgames/mcp-server-godot-docs)godot-editorMCP server (GitHub projectfernforestgames/mcp-server-godot-editorinstalled intoaddons/)
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: Use LSP to check for errors and warnings
- [ ] Step 7: 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
@exportor dependency injection instead of hardcoded node paths - Prefer methods with static typing over equivalent
StringNamevariants
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
Use 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: Use LSP to check for errors and warnings
Use the ide:getDiagnostics tool to check for any syntax or typechecking errors reported by the Godot LSP server. Note that this will only work correctly after godot --import (or else you may see spurious/out-of-date information), and only if the LSP server is running.
Step 7: 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.
Input synthesis
Use godot-editor:synthesize_input to simulate user input events.
For example:
type: "key", keycode: "Space", pressed: true(uses Key enum names)type: "mouse_button", button_index: 1, position: {x, y}, pressed: truetype: "mouse_motion", position: {x, y}type: "action", action: "ui_accept", pressed: true
Node interaction
Use godot-editor:click_node and godot-editor:hover_node to interact with nodes without knowing their screen position.
Identification (use exactly one):
node_path: "Main/UI/Button"(relative to /root)unique_name: Scene-unique%nodesaccessibility_name: Control'saccessibility_nameproperty
Works with Control, Node2D, and Node3D (projected via camera).
Examples:
godot-editor:click_node
unique_name: "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"
Example workflow for UI testing
godot-editor:play_main_scenegodot-editor:take_screenshot, verify initial stategodot-editor:hover_nodewith unique_name "PlayButton"godot-editor:take_screenshot, verify hover visual feedbackgodot-editor:click_nodewith unique_name "PlayButton"godot-editor:take_screenshot, verify button was clicked (new screen, animation, etc.)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")