Scripting and CI
The CLI's automation contract in bash and PowerShell: exit codes, JSON and TSV output, discovery, plus a complete CI smoke test.
The CLI is built to live inside scripts: results go to stdout, errors go to stderr as the JSON-RPC code and message, and exit codes follow one contract: 0 success, 1 command or connection error, 2 usage error. Everything on this page is composition on top of that.
Single commands are identical in every shell. godot-mcp project info pastes into bash, PowerShell, or cmd unchanged. The tabs below appear where pipelines diverge; PowerShell examples use its native object pipeline (ConvertFrom-Json) rather than transliterated bash.
Machine output
Piped output is pretty-printed JSON. The CLI detects the pipe and never emits color or table layout there, so a script has nothing to strip. (On a terminal, results render as tables and key/value boxes instead; --format json pins exact JSON anywhere.) --format tsv (global flags precede the group) re-renders a result for text tools: an object becomes key<TAB>value rows, an array of objects becomes a header row plus one row per element, and nested values stay as compact JSON. --format ndjson emits one compact JSON value per line when the result is a top-level array, and the whole result on a single line otherwise (most commands wrap arrays in an object, so expect one line there). Unlike TSV, nesting survives intact. The GODOT_MCP_FORMAT environment variable pins a format for a whole shell or CI job; the flag always wins, and an unrecognized value warns and falls back to the default.
godot-mcp engine commands --group node | jq -r '.methods[]'
godot-mcp --format tsv project info | awk -F'\t' '$1 == "main_scene" { print $2 }'(godot-mcp engine commands --group node | ConvertFrom-Json).methods
# PowerShell parses JSON natively, so TSV is rarely needed:(godot-mcp project info | ConvertFrom-Json).main_sceneDrive every node of a type
Queries compose with actions. batch find-nodes-by-type returns paths that feed straight back into node set:
godot-mcp batch find-nodes-by-type --type Label \ | jq -r '.matches[].path' \ | while read -r p; do godot-mcp node set --node-path "$p" --property visible --value false done(godot-mcp batch find-nodes-by-type --type Label | ConvertFrom-Json).matches.path | ForEach-Object { godot-mcp node set --node-path $_ --property visible --value false }Preflight, then launch
godot-mcp doctor checks the environment (godot binary, project, addon install state, port config, dotnet) and exits 1 only on a real failure. “No editor running yet” is a warning, so it is safe to run before you have launched anything. godot-mcp status gives the machine-readable liveness verdict (running / starting / crashed / closed), and the launch policy follows from it: never start a second editor when one is running. godot-mcp status --all scans the editor auto range (9080-9095, plus any pinned port) and the game range (9200-9215), then lists every live instance with its port, project, version, and pid. That is the multi-instance view for when two editors are open at once. godot-mcp launch applies that policy for you: it starts one editor for the project, waits until it answers, and returns non-zero if it never does.
The cold half: import, check, test, export, run
Five subcommands spawn the engine directly instead of talking to an editor, which is what a CI job needs before an editor exists. Each writes the engine’s output to <project>/.godot/godot-mcp-<name>.log and returns the exact argv it ran, so a failing step is reproducible by hand.
godot-mcp importbuilds the.godot/import cache a fresh checkout has none of. Run it first. It refuses while an editor is running on the project, because both own that cache; the live-editor form isimport reimport --path res://....godot-mcp check . --jobs 4parses every.gdunder the paths you name with--check-onlyand exits1withfile:lineper failure. Directories are walked recursively, skipping.godot/andaddons/unless the path you name is itself inside addons.godot-mcp testruns the pure-logic tests inres://test/cold, with no editor and no scene tree. A test file is any script that extendsRefCounted, everytest_*method is a test, and the method takes one argument: a harness carryingt.ok,t.eq,t.neandt.fail. It exits1on a failed assertion, on a script error raised inside a test, and on a file that does not parse.godot-mcp export "<preset>"runs the headless export, writes to the preset’s ownexport_pathunless--outsays otherwise, and reports the file produced, its size, and the engine’s parsed errors. An export that exits 0 having written nothing exits non-zero here, which is what missing export templates look like.godot-mcp run [scene] --headlessstarts the game standalone with no editor at all. With thegodot_mcp/runtime/direct_serverproject setting on, it waits for the game’s own channel and reports the port to drive withgodot-mcp --game.
export, import and test are addon group names as well. A command name after any of them (export list-presets, export info, import reimport, test report) still reaches the running editor.
A complete CI smoke test
#!/usr/bin/env bashset -euo pipefail
godot-mcp doctor --project . --json > /dev/null # environment sanity: exit 1 on real problemsgodot-mcp import # build .godot/ on a fresh checkoutgodot-mcp check . --jobs 4 # parse every .gd; exit 1 on any failuregodot-mcp test # run res://test/; exit 1 on any failure
godot-mcp launch --headless # one editor, and never a secondEDITOR_PID=$(godot-mcp status | jq -r .pid)
godot-mcp project info > /dev/null # the editor answersgodot-mcp scene play --mode maingodot-mcp runtime eval --code 'emit(get_tree().current_scene.name)' \ | jq -e '.output[0] == "Main"' # assert against the running gamegodot-mcp scene stop
kill $EDITOR_PID # export reads the files on disk, so close firstgodot-mcp export "Windows Desktop" # exit 1 if nothing landed$ErrorActionPreference = "Stop"
godot-mcp doctor --project . --json | Out-Null # environment sanity: exit 1 on real problemsif ($LASTEXITCODE) { exit 1 }godot-mcp import | Out-Null # build .godot/ on a fresh checkoutif ($LASTEXITCODE) { exit 1 }godot-mcp check . --jobs 4 | Out-Null # parse every .gdif ($LASTEXITCODE) { exit 1 }godot-mcp test | Out-Null # run res://test/if ($LASTEXITCODE) { exit 1 }
godot-mcp launch --headless | Out-Null # one editor, and never a secondif ($LASTEXITCODE) { exit 1 }$editorPid = (godot-mcp status | ConvertFrom-Json).pid
godot-mcp project info | Out-Null # the editor answersif ($LASTEXITCODE) { exit 1 }godot-mcp scene play --mode main | Out-Null$name = (godot-mcp runtime eval --code "emit(get_tree().current_scene.name)" | ConvertFrom-Json).output[0]if ($name -ne "Main") { exit 1 } # assert against the running gamegodot-mcp scene stop | Out-Null
Stop-Process $editorPid # export reads the files on disk, so close firstgodot-mcp export "Windows Desktop" | Out-Null # exit 1 if nothing landedif ($LASTEXITCODE) { exit 1 }Every step exits non-zero on failure, so set -e (bash) or the explicit $LASTEXITCODE checks (PowerShell) turn any broken link into a red build.
The catalog is data
engine commands returns the whole tool surface as JSON: the method list, a group map, and per-command param docs (attached for --group, or --docs for the full catalog). Generators become trivial: bindings, wrappers, forms, docs that cannot drift.
# every node command with its required flagsgodot-mcp engine commands --group node \ | jq -r '.docs | to_entries[] | .key + " " + ([.value.params[]? | select(.required) | "--" + .name] | join(" "))'# every node command with its required flags$cat = godot-mcp engine commands --group node | ConvertFrom-Json$cat.docs.PSObject.Properties | ForEach-Object { $_.Name + " " + ((@($_.Value.params) | Where-Object required | ForEach-Object { "--" + $_.name }) -join " ")}Standalone game rigs
With the direct-to-player channel enabled, a QA rig needs no editor at all: run a debug build, then godot-mcp --game runtime tree, --game runtime eval …, and --game input … drive it directly. See Playtest loop.