godot-mcp-pro-integration

v2026.09.25

AI-powered Godot 4 development with 175 MCP tools for scene manipulation, animation, 3D, physics, shaders, and runtime testing via WebSocket connection

GitHub
安装命令
npx skhub add reason-machines/godot-mcp-pro-integration
Markdown
SKILL.md

Godot MCP Pro Integration

Skill by ara.so — MCP Skills collection.

Godot MCP Pro connects AI assistants (Claude, Cursor, Copilot) to your Godot 4 editor through WebSocket, providing 175 tools for real-time scene manipulation, script editing, input simulation, runtime analysis, and more.

Architecture

AI Assistant ←—MCP/stdio—→ Node.js Server ←—WebSocket:6505—→ Godot Editor Plugin
  • Real-time: WebSocket connection for instant feedback
  • Editor Integration: Full access to Godot's editor API and UndoRedo system
  • JSON-RPC 2.0: Standard protocol with error codes and suggestions
  • Four Modes: Full (175), 3D (103), Lite (84), Minimal (35) tools to fit client limits

Installation

1. Install Godot Plugin (Free)

# Clone or download the free addon
git clone https://github.com/youichi-uda/godot-mcp-pro.git

Copy addons/godot_mcp/ into your Godot project's addons/ directory.

Enable in Godot: Project → Project Settings → Plugins → Godot MCP Pro → Enable

2. Install MCP Server (Paid)

The Node.js server is distributed separately as a paid package ($15 one-time):

After purchasing and extracting:

cd server
npm install
npm run build

3. Configure Your AI Client

Claude Code (.mcp.json)

{
  "mcpServers": {
    "godot-mcp-pro": {
      "command": "node",
      "args": ["/absolute/path/to/server/build/index.js"],
      "env": {
        "GODOT_MCP_PORT": "6505"
      }
    }
  }
}

Cursor / VS Code Copilot

Full mode (175 tools) recommended — both support large tool counts.

Windsurf / JetBrains Junie (100 tool limit)

{
  "mcpServers": {
    "godot-mcp-pro": {
      "command": "node",
      "args": ["/absolute/path/to/server/build/index.js", "--lite"]
    }
  }
}

OpenCode / Local LLMs

{
  "mcpServers": {
    "godot-mcp-pro": {
      "command": "node",
      "args": ["/absolute/path/to/server/build/index.js", "--minimal"]
    }
  }
}

4. Start Development

  1. Launch Godot editor with your project and plugin enabled
  2. Start your AI assistant (e.g., Claude Code)
  3. The MCP server auto-connects to Godot via WebSocket

Tool Categories & Key Commands

Project Management (7 tools)

# Get project info
get_project_info()
# Returns: {name, version, viewport_size, autoloads, main_scene}

# Search files
search_files(pattern: "*.gd", use_glob: true)

# Get filesystem tree
get_filesystem_tree(path: "res://", max_depth: 3, include_extensions: [".gd", ".tscn"])

# UID conversion
uid_to_project_path(uid: "uid://abc123")
project_path_to_uid(path: "res://scenes/player.tscn")

Scene Manipulation (9 tools)

# Get scene tree
get_scene_tree()
# Returns hierarchical node structure with properties

# Create new scene
create_scene(
  path: "res://scenes/enemy.tscn",
  root_type: "CharacterBody3D"
)

# Open scene
open_scene(scene_path: "res://scenes/level_01.tscn")

# Save current scene
save_scene()

# Add scene instance
add_scene_instance(
  scene_path: "res://actors/player.tscn",
  parent_path: "Level/Entities",
  instance_name: "Player"
)

# Play/stop scene
play_scene(mode: "current")  # "main", "current", or custom path
stop_scene()

Node Operations (17 tools)

# Add node
add_node(
  parent_path: "Player",
  node_type: "CollisionShape3D",
  node_name: "BodyCollision",
  properties: {
    "position": {"x": 0, "y": 1, "z": 0}
  }
)

# Update property (auto type parsing)
update_property(
  node_path: "Player",
  property: "speed",
  value: "10.0"  # Auto-converts to float
)

update_property(
  node_path: "Player/Sprite",
  property: "modulate",
  value: "#ff0000"  # Auto-converts to Color
)

# Get node properties
get_node_properties(
  node_path: "Player",
  include_readonly: false
)

# Move/reparent node
move_node(
  node_path: "Enemy",
  new_parent_path: "Level/Enemies",
  new_index: 0
)

# Duplicate node
duplicate_node(
  node_path: "Enemy",
  new_name: "Enemy2"
)

# Delete node
delete_node(node_path: "OldNode")

# Connect signal
connect_signal(
  source_path: "Button",
  signal_name: "pressed",
  target_path: "Game",
  method_name: "_on_button_pressed"
)

# Groups
set_node_groups(node_path: "Enemy", groups: ["enemies", "ai"])
find_nodes_in_group(group_name: "enemies")

# Selection
select_nodes(node_paths: ["Player", "Player/Camera3D"], focus_first: true)
get_editor_selection()
clear_editor_selection()

Script Management (8 tools)

# List all scripts
list_scripts()
# Returns: [{path, class_name, extends, methods, signals}]

# Read script
read_script(script_path: "res://scripts/player.gd")

# Create script
create_script(
  path: "res://scripts/enemy.gd",
  template: "CharacterBody3D",
  class_name: "Enemy"
)

# Edit script (search/replace)
edit_script(
  script_path: "res://scripts/player.gd",
  mode: "search_replace",
  search: "var speed = 5.0",
  replace: "var speed = 10.0"
)

# Full replacement
edit_script(
  script_path: "res://scripts/player.gd",
  mode: "replace",
  content: "extends CharacterBody3D\n\nfunc _ready():\n\tpass"
)

# Attach script to node
attach_script(
  node_path: "Enemy",
  script_path: "res://scripts/enemy.gd"
)

# Validate syntax
validate_script(script_path: "res://scripts/player.gd")

# Search in files
search_in_files(
  pattern: "func _process",
  extensions: [".gd"],
  case_sensitive: false
)

Runtime Testing (19 tools)

# Get running game scene tree
get_game_scene_tree()

# Get node properties in running game
get_game_node_properties(node_path: "/root/Level/Player")

# Set property in running game
set_game_node_property(
  node_path: "/root/Level/Player",
  property: "position",
  value: {"x": 100, "y": 200, "z": 0}
)

# Execute code in game context
execute_game_script(code: """
var player = get_node('/root/Level/Player')
player.health = 100
print('Player health reset')
""")

# Input simulation
simulate_key(key: "Space", pressed: true)
simulate_key(key: "Space", pressed: false)

simulate_mouse_click(
  position: {"x": 512, "y": 300},
  button: "left"
)

simulate_action(action: "jump", strength: 1.0)

simulate_sequence(events: [
  {"type": "key", "key": "Right", "pressed": true},
  {"type": "wait", "frames": 30},
  {"type": "key", "key": "Space", "pressed": true},
  {"type": "wait", "frames": 5},
  {"type": "key", "key": "Space", "pressed": false},
  {"type": "key", "key": "Right", "pressed": false}
])

# Capture frames
capture_frames(
  duration_frames: 60,
  interval_frames: 10,
  output_dir: "res://test_captures"
)

# Monitor properties
monitor_properties(
  node_path: "/root/Level/Player",
  properties: ["position", "velocity"],
  duration_frames: 120,
  interval_frames: 5
)

# Recording/replay
start_recording()
stop_recording()  # Returns recording data
replay_recording(recording_data: {...})

# Find nodes
find_nodes_by_script(script_path: "res://scripts/enemy.gd")
find_ui_elements(node_type: "Button")
find_nearby_nodes(
  position: {"x": 100, "y": 200, "z": 0},
  radius: 50.0,
  node_type: "Area3D"
)

# UI interaction
click_button_by_text(text: "Start Game")

# Wait for condition
wait_for_node(
  node_path: "/root/Level/Boss",
  timeout_seconds: 5.0
)

Animation (6 tools)

# List animations
list_animations(player_path: "Player/AnimationPlayer")

# Create animation
create_animation(
  player_path: "Player/AnimationPlayer",
  animation_name: "run",
  length: 1.0,
  loop: true
)

# Add track
add_animation_track(
  player_path: "Player/AnimationPlayer",
  animation_name: "run",
  track_type: "value",  # "value", "position", "rotation", "method", "bezier"
  node_path: "Player/Sprite",
  property: "frame"
)

# Set keyframe
set_animation_keyframe(
  player_path: "Player/AnimationPlayer",
  animation_name: "run",
  track_index: 0,
  time: 0.0,
  value: 0
)

set_animation_keyframe(
  player_path: "Player/AnimationPlayer",
  animation_name: "run",
  track_index: 0,
  time: 1.0,
  value: 8
)

# Get animation info
get_animation_info(
  player_path: "Player/AnimationPlayer",
  animation_name: "run"
)

# Remove animation
remove_animation(
  player_path: "Player/AnimationPlayer",
  animation_name: "old_anim"
)

3D & Physics

# Add collision shape resource
add_resource(
  node_path: "Player/CollisionShape3D",
  resource_type: "BoxShape3D",
  properties: {
    "size": {"x": 1, "y": 2, "z": 1}
  }
)

# Add mesh resource
add_resource(
  node_path: "Enemy/MeshInstance3D",
  resource_type: "SphereMesh",
  properties: {
    "radius": 0.5,
    "height": 1.0
  }
)

# Set 3D node properties
update_property(
  node_path: "Player",
  property: "position",
  value: {"x": 0, "y": 0, "z": 0}
)

update_property(
  node_path: "Player",
  property: "rotation_degrees",
  value: {"x": 0, "y": 90, "z": 0}
)

# Add light
add_node(
  parent_path: "Level",
  node_type: "DirectionalLight3D",
  node_name: "Sun",
  properties: {
    "light_energy": 1.0,
    "rotation_degrees": {"x": -45, "y": 0, "z": 0}
  }
)

# Add camera
add_node(
  parent_path: "Player",
  node_type: "Camera3D",
  node_name: "Camera",
  properties: {
    "position": {"x": 0, "y": 2, "z": 5},
    "current": true
  }
)

Shader Tools (6 tools)

# Create shader
create_shader(
  path: "res://shaders/water.gdshader",
  template: "canvas_item"  # "spatial", "canvas_item", "sky", "fog"
)

# Edit shader
edit_shader(
  shader_path: "res://shaders/water.gdshader",
  mode: "replace",
  content: """
shader_type canvas_item;

uniform float wave_speed : hint_range(0.0, 10.0) = 2.0;

void fragment() {
    COLOR = vec4(0.0, 0.5, 1.0, 1.0);
}
"""
)

# Assign shader material
assign_shader_material(
  node_path: "Water",
  shader_path: "res://shaders/water.gdshader"
)

# Set shader parameter
set_shader_param(
  node_path: "Water",
  param_name: "wave_speed",
  value: 3.5
)

# Get shader parameters
get_shader_params(node_path: "Water")

TileMap (6 tools)

# Set single cell
tilemap_set_cell(
  tilemap_path: "Level/TileMapLayer",
  coords: {"x": 5, "y": 3},
  source_id: 0,
  atlas_coords: {"x": 2, "y": 1}
)

# Fill rectangle
tilemap_fill_rect(
  tilemap_path: "Level/TileMapLayer",
  start_coords: {"x": 0, "y": 0},
  end_coords: {"x": 10, "y": 10},
  source_id: 0,
  atlas_coords: {"x": 1, "y": 0}
)

# Get cell info
tilemap_get_cell(
  tilemap_path: "Level/TileMapLayer",
  coords: {"x": 5, "y": 3}
)

# Get used cells
tilemap_get_used_cells(tilemap_path: "Level/TileMapLayer")

# Clear all
tilemap_clear(tilemap_path: "Level/TileMapLayer")

Editor Integration

# Get editor errors
get_editor_errors()
# Returns: [{type, message, file, line, stack_trace}]

# Execute editor script (runs in EditorScript context)
execute_editor_script(code: """
var editor = get_editor_interface()
print('Current scene: ', editor.get_edited_scene_root().name)
""")

# Capture editor viewport
get_editor_screenshot(
  output_path: "res://editor_screenshot.png",
  viewport: "3D"  # "2D", "3D", "Script"
)

# Capture running game
get_game_screenshot(output_path: "res://game_screenshot.png")

# Clear output panel
clear_output()

# Get output log
get_output_log(max_lines: 100)

# Reload plugin (auto-reconnects)
reload_plugin()

# Reload project (rescan filesystem)
reload_project()

# Get signals
get_signals(node_path: "Player")

Common Patterns

Creating a Character Controller

# 1. Create scene
create_scene(
  path: "res://actors/player.tscn",
  root_type: "CharacterBody3D"
)

open_scene(scene_path: "res://actors/player.tscn")

# 2. Add collision
add_node(
  parent_path: ".",
  node_type: "CollisionShape3D",
  node_name: "Collision"
)

add_resource(
  node_path: "Collision",
  resource_type: "CapsuleShape3D",
  properties: {"radius": 0.5, "height": 2.0}
)

# 3. Add mesh
add_node(
  parent_path: ".",
  node_type: "MeshInstance3D",
  node_name: "Mesh"
)

add_resource(
  node_path: "Mesh",
  resource_type: "CapsuleMesh",
  properties: {"radius": 0.5, "height": 2.0}
)

# 4. Add camera
add_node(
  parent_path: ".",
  node_type: "Camera3D",
  node_name: "Camera",
  properties: {
    "position": {"x": 0, "y": 1.6, "z": 0}
  }
)

# 5. Attach script
create_script(
  path: "res://scripts/player.gd",
  template: "CharacterBody3D",
  class_name: "Player"
)

attach_script(
  node_path: ".",
  script_path: "res://scripts/player.gd"
)

# 6. Edit script
edit_script(
  script_path: "res://scripts/player.gd",
  mode: "replace",
  content: """
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

func _physics_process(delta):
    if not is_on_floor():
        velocity.y -= gravity * delta
    
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY
    
    var input_dir = Input.get_vector("left", "right", "forward", "back")
    var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    
    if direction:
        velocity.x = direction.x * SPEED
        velocity.z = direction.z * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        velocity.z = move_toward(velocity.z, 0, SPEED)
    
    move_and_slide()
"""
)

save_scene()

Automated Testing Workflow

# 1. Start game
play_scene(mode: "main")

# Wait for scene to load
wait_for_node(node_path: "/root/Level", timeout_seconds: 3.0)

# 2. Execute test setup
execute_game_script(code: """
var player = get_node('/root/Level/Player')
player.position = Vector3(0, 1, 0)
player.health = 100
""")

# 3. Record input sequence
start_recording()

simulate_sequence(events: [
  {"type": "key", "key": "Right", "pressed": true},
  {"type": "wait", "frames": 60},
  {"type": "key", "key": "Space", "pressed": true},
  {"type": "wait", "frames": 5},
  {"type": "key", "key": "Space", "pressed": false},
  {"type": "wait", "frames": 30},
  {"type": "key", "key": "Right", "pressed": false}
])

var recording = stop_recording()

# 4. Monitor result
monitor_properties(
  node_path: "/root/Level/Player",
  properties: ["position", "velocity", "is_on_floor"],
  duration_frames: 120,
  interval_frames: 10
)

# 5. Capture evidence
capture_frames(
  duration_frames: 120,
  interval_frames: 20,
  output_dir: "res://test_results"
)

# 6. Stop game
stop_scene()

# 7. Verify results
get_output_log(max_lines: 50)
get_editor_errors()

Batch Refactoring

# Find all CharacterBody3D nodes
find_nodes_by_type(node_type: "CharacterBody3D")

# Set gravity scale on all
batch_set_property(
  node_type: "CharacterBody3D",
  property: "motion_mode",
  value: 0  # MOTION_MODE_GROUNDED
)

# Find signal connections
find_signal_connections(scene_path: "res://scenes/level.tscn")

# Find script usage
find_script_references(resource_path: "res://scripts/old_player.gd")

# Cross-scene property update
cross_scene_set_property(
  node_type: "DirectionalLight3D",
  property: "light_energy",
  value: 1.5,
  scene_pattern: "res://scenes/*.tscn"
)

# Detect circular dependencies
detect_circular_dependencies()

CLI Mode (Alternative to MCP)

For clients without MCP support or to minimize context overhead:

# Top-level help
node /path/to/server/build/cli.js --help

# Group help
node /path/to/server/build/cli.js scene --help
node /path/to/server/build/cli.js node --help

# Execute commands
node /path/to/server/build/cli.js project info
node /path/to/server/build/cli.js scene play --mode current
node /path/to/server/build/cli.js node add --type CharacterBody3D --name Player --parent .
node /path/to/server/build/cli.js script create --path res://player.gd --template CharacterBody3D
node /path/to/server/build/cli.js input simulate-key --key Space --pressed true

CLI requires:

  • Godot editor running with plugin enabled
  • Server built (npm run build)
  • Available port 6510-6514

Troubleshooting

WebSocket Connection Fails

Error: Could not connect to Godot (tried ports 6505-6509)

Solutions:

  1. Verify Godot editor is running with plugin enabled
  2. Check plugin status: Project → Project Settings → Plugins
  3. Restart Godot editor
  4. Check if port 6505 is blocked by firewall
  5. Try reloading plugin: reload_plugin()

"Tool not available" in Lite/Minimal mode

Different modes expose different tool sets:

  • Full (175): All tools
  • 3D (103): 3D-focused subset (excludes 2D-specific)
  • Lite (84): Essential tools (no profiling, advanced batch operations)
  • Minimal (35): Core CRUD only

Switch modes in your MCP config:

"args": ["/path/to/server/build/index.js", "--lite"]

Type Conversion Errors

update_property auto-parses strings to correct types:

# ✓ Correct
update_property(node_path: "Player", property: "speed", value: "10.0")
update_property(node_path: "Sprite", property: "modulate", value: "#ff0000")
update_property(node_path: "Node", property: "position", value: {"x": 1, "y": 2, "z": 3})

# ✗ Wrong (will fail)
update_property(node_path: "Player", property: "speed", value: 10)  # Pass as string

Script Validation Fails

# Always validate before saving
validate_script(script_path: "res://scripts/player.gd")

# Check editor errors
get_editor_errors()

Runtime Property Changes Don't Persist

Runtime changes via set_game_node_property only affect the running game instance. To persist:

# 1. Change in running game (for testing)
set_game_node_property(
  node_path: "/root/Level/Player",
  property: "speed",
  value: 15.0
)

# 2. Stop game
stop_scene()

# 3. Update scene file
update_property(
  node_path: "Player",
  property: "speed",
  value: "15.0"
)

save_scene()

Node Path Not Found

Always use full paths from scene root:

# ✓ Correct
update_property(node_path: "Level/Entities/Player", ...)

# ✗ Wrong
update_property(node_path: "Player", ...)  # If not at root

# Get current tree structure
get_scene_tree()

Performance Issues with Capture/Monitor

# Reduce frequency
capture_frames(
  duration_frames: 60,
  interval_frames: 30  # Every 30 frames instead of 10
)

monitor_properties(
  node_path: "/root/Player",
  properties: ["position"],  # Fewer properties
  duration_frames: 60,
  interval_frames: 15
)

Environment Variables

# WebSocket port (default: 6505)
GODOT_MCP_PORT=6505

# Server timeout (default: 5000ms)
GODOT_MCP_TIMEOUT=5000

# Debug logging
GODOT_MCP_DEBUG=true

Client Compatibility Reference

ClientModeTool LimitNotes
Claude CodeFullUnlimitedDeferred tool loading
VS Code CopilotFullUnlimitedVirtual Tools grouping
CursorFullUnlimitedDynamic Context Discovery
ClineFullUnlimitedUse enabledTools for whitelist
Roo CodeFullUnlimitedNo hard limit
WindsurfLite100Hard limit
JetBrains JunieLite100Hard limit
Gemini CLILite~100Use excludeTools to tune
Antigravity3D1003D-focused subset
OpenCodeMinimal/CLI~40Model degrades past 40
LM StudioMinimal/CLIVariesContext is bottleneck

Additional Resources

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.25

发布时间

2026年9月25日

分类

未分类

许可证

NOASSERTION

源路径

skills/godot-mcp-pro-integration

默认分支

main

最新提交

329e67c

Tree SHA

01fd22f