gdcli connects a coding agent to a running Godot editor. You describe a game feature in plain words ("a 2D character that runs and jumps, with ground to stand on"). The agent builds it the way a person would: it creates nodes, sets properties, writes and attaches scripts, paints tiles and makes animations. Every step is on the editor's undo stack. Then it runs the game, presses keys and reads values in the running game to check that the character really jumps.
It works with the official Godot 4.3 to 4.7 builds, standard or .NET, on Windows, macOS and Linux. Nothing is compiled into the engine and no API key goes into Godot: the agent you already use does the thinking, and gdcli gives it hands and eyes in the editor.
You: Make a 2D platformer character that moves left and right and jumps, with ground under it. Run it to check.
Agent: gd command scene_new --path res://main.tscn --root-type Node2D
gd command node_create --type CharacterBody2D --name Player --properties '{"position": [200, 300]}'
gd command node_create --type CollisionShape2D --parent Player --properties '{"shape": {"$new": "RectangleShape2D", ...}}'
gd command fs_write_text --path res://player.gd --text-file /tmp/player.gd
gd command node_attach_script --path Player --script res://player.gd
gd command project_input_map_set --action jump --events '[{"key": "Space"}]' --yes
gd command scene_save
gd command editor_play
gd command game_input --action jump --hold 0.1
gd command game_node_get --path Main/Player --properties '["velocity", "position"]' # it went up: done
Why
The usual way to let an AI write a Godot game is to have it edit .tscn and .gd files directly. That breaks in
several ways:
- Scene files are easy to corrupt.
- Changes skip the editor's undo stack.
- The model never sees how the game actually behaves.
- Any code it writes may end up running inside your editor.
gdcli takes the other road: the agent works through the editor.
What you can do with it
- Build scenes: nodes, properties, scripts, signals, groups, resources, animations (players, state machines, blend spaces, sprite frames), tile sets and tile maps with terrains, GridMap levels, themes, navigation meshes, import settings, ready-made particle effects and animations, collision shapes fitted to sprites and meshes.
- Change the project: project settings, input actions, moving and renaming files with references updated across scenes and scripts.
- Run and look inside: start the game from the editor and read its node tree and live values, play key, mouse and gamepad sequences, slow it down or speed it up, list what its UI shows, check several values at once, call methods, read errors and output with script stack traces, take screenshots, read the frame rate, memory, draw calls and orphan nodes.
- Debug: set breakpoints, pause, step, read the call stack and local variables of a stopped game.
- Without an editor: run a scene and get its errors with file and line, check scripts, run GUT or gdUnit4 tests, import, export, run a SceneTree script, look up any class of your Godot version, search the manual.
- From any agent: the same commands as a CLI (
gd), as a Claude Code plugin, and as an MCP server for Codex, Cursor, VS Code, Gemini CLI and others.
The three parts
agent / terminal Godot editor
┌──────────────┐ HTTP (loopback) ┌──────────────────────────────┐
│ gd command … │ ──────────────────▶ │ bridge addons/gdcli │──▶ scenes, undo stack, files
│ gd mcp │ token + handshake │ asks a person when needed │──▶ running game (debugger)
└──────┬───────┘ └──────────────────────────────┘
│ starts Godot directly (no editor)
└──▶ gd run / check / test / import / export / api / docs
The bridge listens on the loopback address only. Its token is kept in your home folder (~/.gdcli), and every
connection starts with an HMAC handshake that proves the other side holds the token. Details:
docs/protocol.md (in Chinese).
Getting started
1. What you need
- Godot 4.3 or newer, any official build (standard or .NET). gdcli does not install Godot.
- Node.js 22 or newer, for the
gdcommand line. - For the agent part: Claude Code, or any MCP client (Codex, Cursor, VS Code, Gemini CLI, ...).
2. Install the command line and point it at Godot
npm install -g @spardanviro/gdcli
gd doctor
gd doctor says which Godot it found and what is missing. gd looks for Godot in this order:
--godot <path>- the
GODOT_BINenvironment variable godot_binin~/.gdcli/config.jsongodot, thengodot4, on the PATH
On Windows, point it at the *_console.exe next to the editor, so that its output can be read.
gd and gdcli are the same command. If gd is taken by a shell alias (oh-my-zsh's git plugin makes it
git diff), use gdcli or unalias gd.
3. Put the bridge into your project
Pick one of these:
a. With gd (recommended): it always installs the bridge of the same version as gd.
gd new my-game --renderer mobile # a new project with the bridge already in it
# (renderers: forward_plus, mobile, gl_compatibility)
# or, for a project you already have:
cd your-game
gd bridge install # copies addons/gdcli and enables it; --force to replace another version
b. From the Godot Asset Library, inside the editor:
- Open your project in Godot and switch to the AssetLib tab at the top.
- Search for gdcli, open "gdcli bridge", click Download, then Install. Keep
addons/gdcliticked. - Project > Project Settings > Plugins: tick Enable next to gdcli. This also adds its small
GdcliGameautoload, which letsgdlook into the running game.
The page is godotengine.org/asset-library/asset/5539. The
Asset Library can be a version behind while a new release waits for review. gd warns when the bridge in a
project differs from its own; gd bridge install --force brings them in line.
c. From a release zip: download gdcli-bridge-<version>.zip from
Releases. Copy the addons/gdcli folder inside it into
your project's addons/ folder, then enable it as in b.3.
4. Open the editor and check the connection
gd open # starts the editor on the project (or open it yourself as usual)
gd status # lists the editors gd can reach: you should see your project
On a machine without a display (a server, CI, WSL without a desktop), gd host start runs a headless editor
instead, and gd host stop ends it.
5. Try a few commands yourself
gd command scene_new --path res://main.tscn --root-type Node2D --root-name Main
gd command node_create --type Sprite2D --name Logo --properties '{"position": [100, 50]}'
gd command scene_tree
gd command edit_undo # the same as Ctrl+Z in the editor
gd command edit_redo
gd command scene_save
gd run --headless --seconds 3 # runs the project for 3 seconds without an editor and reports its errors
You see each change appear in the editor as it happens. gd list lists every command the editor offers, with
its arguments (--json gives the full schemas).
6. Hand it to an agent
Claude Code
Install the plugin (optional, but it is what teaches Claude how to use gd well):
claude plugin marketplace add spardanviro/GodotCli
claude plugin install godot@gdcli
Without the marketplace, use gdcli-claude-plugin-<version>.zip from
Releases:
- Claude desktop app: Customize > Plugins > Add > Upload plugin, and pick the zip. The skills and hooks
work in sessions that run on your machine (Cowork, Claude Code), where
gdis installed. Chat on claude.ai loads only the skills and cannot reach your editor. - Claude Code, for one session:
claude --plugin-dir gdcli-claude-plugin-<version>.zip, orclaude --plugin-url <the zip's release URL>.
See plugin/README.md for the details.
Then open Claude Code in the project folder and say what you want. For example:
Make a 2D platformer character that runs and jumps, with ground to stand on. Run it and check that jumping works.
The enemy walks through walls. Find out why and fix it.
Add a pause menu with Resume and Quit, opened with Escape, and check that the game really stops.
Translate the main menu into Chinese and Japanese, and add a language switch.
What happens:
- The skills load when they are needed:
godot-clifor the commands, plus the topic or task one (tile maps, UI, shaders, multiplayer, troubleshooting, ...). - Claude builds through
gd command ..., and you watch the editor change.Ctrl+Zin the editor undoes its steps like your own. - When a change would run code inside the editor (a
@toolscript, an addon, an autoload, C#), a dialog appears in the editor showing the full content. Nothing happens until you click Allow. - It checks its work:
gd runfor errors, and the game commands to see the behaviour. - The plugin's hooks stop Claude from writing scripts and scenes straight to disk behind the editor's back. That is a guard rail, not a sandbox: for a real boundary, use Claude Code's sandbox as described in plugin/README.md.
Codex, Cursor, VS Code, Gemini CLI and other MCP clients
gd mcp is an MCP server on standard input and output. Every editor command is a tool of the same name, plus
gd_run, gd_check, gd_test, gd_api_*, gd_docs_*, gd_host_start and others that work without an editor.
gd mcp configure codex # shows the entry it would add to ~/.codex/config.toml
gd mcp configure codex --yes # writes it
gd mcp configure cursor --yes # cursor, vscode, gemini, claude: the project's own config file
gd skills install codex --yes # the 17 skills into ~/.codex/skills (the same SKILL.md format)
gd skills install --dir <folder> --yes # for any other agent that reads SKILL.md
A client with a cap on tools, or a small model, can take only some groups:
gd mcp --tools core,game (groups: core, game, animation, level, import, docs, test, exec, custom; gd mcp configure
takes the same option). The read commands are also MCP resources (godot://scene/tree, godot://node/{path},
godot://file/{path}, godot://class/{name}, ...), which a client can attach to a conversation directly.
The tools let the agent act; the skills tell it how to do it right. Person approval works the same for every client.
A walkthrough: a jumping character, by hand
The same flow the agent follows, step by step, so you can see what each command does. Start in a project with the bridge and the editor open.
# A scene with a character and a floor.
gd command scene_new --path res://main.tscn --root-type Node2D --root-name Main
gd command node_create --type CharacterBody2D --name Player --properties '{"position": [200, 200]}'
gd command node_create --type ColorRect --parent Player \
--properties '{"position": [-50, -50], "size": [100, 100], "color": "#4a90d9"}'
gd command node_create --type CollisionShape2D --parent Player \
--properties '{"shape": {"$new": "RectangleShape2D", "properties": {"size": [100, 100]}}}'
gd command node_create --type StaticBody2D --name Floor --properties '{"position": [400, 500]}'
gd command node_create --type CollisionShape2D --parent Floor \
--properties '{"shape": {"$new": "RectangleShape2D", "properties": {"size": [800, 40]}}}'
# Input actions the script will read.
gd command project_input_map_set --action move_left --events '[{"key": "A"}, {"key": "Left"}]' --yes
gd command project_input_map_set --action move_right --events '[{"key": "D"}, {"key": "Right"}]' --yes
gd command project_input_map_set --action jump --events '[{"key": "Space"}]' --yes
Write the script to a scratch file (player.gd):
extends CharacterBody2D
const SPEED := 300.0
const JUMP_VELOCITY := -600.0
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity += get_gravity() * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
velocity.x = Input.get_axis("move_left", "move_right") * SPEED
move_and_slide()
# Into the project, onto the node, and saved.
gd command fs_write_text --path res://player.gd --text-file player.gd
gd command node_attach_script --path Player --script res://player.gd
gd command project_settings_set --name application/run/main_scene --value '"res://main.tscn"' --yes
gd command scene_save
# Does it compile and run without errors?
gd check res://player.gd
gd run --seconds 3 --headless
# Does it behave? Run it from the editor and look inside.
gd command editor_play
gd command game_node_get --path Main/Player --properties '["position", "velocity"]'
gd command game_input --action jump --hold 0.1
gd command game_node_get --path Main/Player --properties '["position", "velocity"]' # velocity.y < 0: it jumped
gd command game_performance # fps, frame times, nodes
gd command editor_stop
Something wrong? Stop at a line and look:
gd command editor_play
gd command game_breakpoint --path res://player.gd --line 12
gd command game_debug # stopped? where, and why
gd command game_locals # the local variables at that line (delta)
gd command game_breakpoint --path res://player.gd --line 12 --enabled false
gd command game_continue
gd command editor_stop
--yes on the input actions and the main scene: they change project settings, which the bridge counts as
destructive and wants confirmed.
A fuller version, with tiles and checks, is in plugin/skills/godot-cli/references/walkthrough-platformer.md.
gdcli and Godot AI
gdcli has the same author as Godot AI, and the two take different roads to the same goal.
They share their Godot knowledge: the pitfalls collected for Godot AI became references in gdcli's skills.
Command reference
With an editor (the bridge runs in an open or headless editor)
Editor commands by area:
A project can add commands of its own in res://gdcli_commands/.
Without an editor (gd starts Godot itself)
Project and setup: gd new, gd bridge install, gd bridge status, gd open, gd doctor.
Global options: --json, --project <path>, --godot <path>, --instance <id> (when several editors have
the project open), --timeout <seconds>, --yes (confirm overwriting or deleting), --dry-run (say what would
change, change nothing).
Output
With --json, every command prints one envelope of a fixed shape on standard output, on failure too:
{
"success": true,
"command": "node_create",
"data": { "path": "Logo", "type": "Sprite2D" },
"errors": [],
"warnings": [],
"meta": { "protocol": 1, "undo": "gdcli: node_create Logo", "scene": "res://main.tscn" }
}
On failure each entry of errors has a code, message, hint and details. Exit codes: 0 success, 1 internal
error, 2 usage, 3 authentication, 4 precondition (no editor, confirmation needed, a person declined), 5 retry
later, 6 the command failed.
Safety
- Ordinary changes (nodes, properties, plain scripts) run at once and go on the undo stack.
- Destructive changes (deleting, overwriting, project settings) need
--yesfrom the caller. That is a speed bump, not an authorization. - Changes that would bring code into the editor process always wait for a person:
@toolscripts, scenes and resources with embedded scripts, any C# file,addons/, autoloads and other settings that decide which code loads, files a .NET build reads, andeval. The dialog shows the full content; what the caller says does not count. A headless editor has no one to ask and refuses. - Access modes live in the editor settings, per user and not in the project:
standard(default),ask(a person approves every destructive change),readonly, andfull(asks no one; for unattended headless editors and CI). - The game's own code runs with your rights, as when you press F5 yourself. To limit that, do not let your agent client auto-approve writing files and running the game.
The full rules: docs/protocol.md, section 4 (in Chinese).
Troubleshooting
gd cannot find Godot: run gd doctor. Set it with --godot, GODOT_BIN or ~/.gdcli/config.json; on
Windows point it at *_console.exe.
NO_EDITOR: no editor is open, or the project has no bridge. gd bridge install, then gd open; without a
display, gd host start.
NOT_APPROVED: the change needs a person's approval in the editor, and they declined, or the editor has no
window. Try again in an editor with a window, or do it another way that needs no approval. Do not work around it.
MULTIPLE_EDITORS: more than one editor has the project open; pick one with --instance <id> (from
gd status).
The bridge version differs from gd: gd bridge install --force installs the bridge that matches.
Exporting fails: export templates and presets are set up in the editor first (Editor > Manage Export
Templates, Project > Export). The godot-export skill explains it.
Evals
evals/ holds 40 tasks: scenes, scripts, UI, gameplay, 3D, animation, tiles, localization, audio buses, themes,
GridMap, shaders, particles,
debugging, and requests written in Chinese. 11 of them are hard ones checked by behaviour in the running game: a
patrol-and-chase state machine, saving and loading, a pause menu, a logic bug that prints no error.
- Replaying the reference answers passes every task; doing nothing fails them all (except "do not work around a refusal"). Both run in CI.
- With real Claude, the second baseline ran 31 tasks 3 times each: 93/93 passed (evals/results/claude-baseline.json).
GODOT_BIN=/path/to/godot node evals/run.mjs --agent claude --repeat 3 --baseline evals/results/claude-baseline.json
Details in evals/README.md.
Documentation
The detailed documents are in Chinese.
Repository layout
To report a security problem, see SECURITY.md.
The three parts are released together with the same version number, from this one repository: the plugin's command reference is generated from the bridge, so they cannot drift apart.
Development
npm install
npm run verify # versions, plugin check, hook tests, types, lint, unit tests, build
GODOT_BIN=/path/to/godot npm run test:bridge # the bridge's unit and end-to-end tests
GODOT_BIN=/path/to/godot npm run test:e2e # the built gd against a real editor
GODOT_BIN=/path/to/godot npm run test:windowed # a game with a window: clicks at game_ui's positions, screenshots
npm run test:package # pack, install into an empty folder, run
CI tests Godot 4.3 to 4.7 on Linux, and 4.7 on Windows, macOS and the .NET build; the command line on all three systems; and a game with a window on a virtual display.