Just for Fun

dsh-godot-tool

fromlan/dsh-godot-tool · ★ 3

fromlan

Godot Agent RPC — lets an AI agent drive the Godot editor.

License
MIT
Added
2026-08-23
Maintenance
active
Install kind
github

No manifest check is available; listing does not imply compatibility or security review.

安装

dsh plugin --profile web add github:fromlan/dsh-godot-tool

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

For install and environment setup, plus plugin development tutorials, see DSH community docs ↗

README

Source README · body: en · Read Chinese README

Table of contents
  1. Contents
  2. Quick start
  3. 1. Install the Godot addon
  4. 2. Install the harness plugin
  5. 3. Point the addon at the harness endpoint
  6. 4. Enable the tools you want
  7. 5. Verify the connection
  8. How it works
  9. Wire protocol
  10. Transport
  11. Message shapes
  12. Endpoint file
  13. Method roster (27 RPCs + ping)
  14. Timeout ladder
  15. Playback error collection
  16. Security model
  17. Glossary (EN ↔ 中文)
  18. Godot addon (agent_rpc)
  19. What the addon folder ships
  20. Installation
  21. Endpoint configuration
  22. Handshake
  23. Troubleshooting
  24. Harness plugin (dsh-godot-tool)
  25. What it does
  26. Configuration
  27. Tool gate
  28. Export shape
  29. Model experience
  30. Known limitations
  31. Godot mode (agent preset)
  32. What it is
  33. Installation
  34. Bridge lifecycle and mode switching
  35. Maintenance
  36. Development
  37. Repository layout
  38. Running the harness-plugin tests
  39. Smoke-testing against a real Godot editor
  40. Keeping the docs in sync
  41. Compatibility
  42. License

English | 中文

A pair of plugins plus a Godot mode (agent preset) that let an AI agent drive a Godot 4.x editor over loopback TCP JSON-lines: open / reload scenes, run / stop the current or main scene, watch playback errors, inspect scene trees and scripts, set project settings, lint GDScript, list project files, and export builds.

Path What it is Runs in
addons/agent_rpc/ Godot editor addon (EditorPlugin + EditorDebuggerPlugin) — TCP client, message dispatch, play-error ring buffer Godot editor process
dsh-godot-tool/ DeepSeek Harness plugin (@deepseek-ai/dsh-godot-tool) — loopback TCP server (GodotRpcBridge) + 27 godot_* model-facing tools DeepSeek Harness
agent-presets/godot/ Godot mode agent preset — assembles the two halves into a lean Godot-dev agent (persona, shell/fs/jobs, compaction, subagents, goals, plan mode) DeepSeek Harness

The two halves speak one wire protocol: newline-delimited JSON over TCP on 127.0.0.1:8765 (fallback 8765–8774), token-authenticated handshake, loopback-only. The Godot mode assembles the two halves into a ready-to-use Godot-dev agent on the harness side. This document is the single reference — quick start first, then the wire protocol, then each half's and the mode's manual.

┌─────────────────────────────┐          ┌──────────────────────────────┐
│  Godot 4.x editor           │  TCP     │  DeepSeek Harness            │
│                             │  JSON-L  │                             │
│  addons/agent_rpc (client)  │◄────────►│  dsh-godot-tool (server)    │
│  EditorPlugin + debugger    │  8765    │  GodotRpcBridge + 27 tools  │
└──────────────┬──────────────┘          └──────────────┬───────────────┘
               │ polls endpoint file (1 s)              │ publishes
               ▼                                        ▼
      ~/.pi/agent/x-agent-godot-rpc.json   ~/.dsh/godot/endpoint.json
               (addon default)              (harness plugin default)

Godot mode (agent-presets/godot/) assembles the dsh-godot-tool/ plugin into a lean Godot-dev agent on the harness side — see Godot mode.

Contents


Quick start

1. Install the Godot addon

Copy addons/agent_rpc/ into your Godot project's addons/ folder, then enable Agent RPC in Project Settings → Plugins.

2. Install the harness plugin

Two options:

  • From this repo (source): point the Harness at dsh-godot-tool/src/index.ts via a --patch overlay or a project plugin row, and add dsh-tools to your composition.
  • From the harness workspace: if you build inside the deepseek-harness repository, the package is packages/extensions/tool-godot (@deepseek-ai/dsh-godot-tool); this copy stays in sync with it.

3. Point the addon at the harness endpoint

The addon polls an endpoint file every second. The default is ~/.pi/agent/x-agent-godot-rpc.json; the harness plugin publishes $DSH_HOME/godot/endpoint.json by default. Point one at the other:

  • Set the environment variable AGENT_RPC_ENDPOINT (recommended), or
  • Set the ProjectSettings key agent_rpc/endpoint_file, or
  • Configure the harness plugin's endpointPath to the addon's file.

4. Enable the tools you want

Every godot_* tool is off by default (enabledTools is empty in the harness plugin config). Opt in explicitly:

Not needed when you use Godot mode — the preset already carries the tool-godot row (enabledTools = all 27 tools). The snippet below is only for bare deployments without agent presets (CLI/TUI, --patch source installs).

# dsh cordis.yml
- id: tool-godot
  name: '@deepseek-ai/dsh-godot-tool'
  config:
    enabledTools: ['godot_editor_info', 'godot_run_scene', 'godot_play_errors']

Then start the harness, open the Godot project, and the agent can drive the editor.

5. Verify the connection

  • The addon prints Agent RPC: connected to … in the Godot Output panel within ~1 s of the bridge listening.
  • From the agent side, call godot_editor_info — it returns { godotVersion, projectPath, editedScene, playing } when the wire path works end to end. If it fails, check the troubleshooting table.

How it works

Two processes, one loopback socket, one endpoint file that joins them:

  1. Harness starts first (or Godot — either order works). The dsh-godot-tool plugin binds 127.0.0.1:8765 (walking up to 8774 if busy), generates a 32-hex token, and atomically writes the endpoint file { host, port, token } for the addon to find.
  2. The addon polls that file every second and reconnects automatically whenever it changes or disappears. The file is resolved from AGENT_RPC_ENDPOINT, then agent_rpc/endpoint_file, then the legacy ~/.pi/... default.
  3. On the first TCP connect the addon sends editor_ready carrying its token and addonVersion; the bridge authenticates it, records its projectPath, and assigns a clientId.
  4. The agent calls a godot_* tool → the bridge validates (allowlist, tool gate, param hygiene, cross-project routing) → sends a JSON-lines request → the addon acts on the editor → replies with ok:true/ok:false → the bridge resolves the tool call.
  5. During playback the addon pushes play_error events into the bridge's ring buffer (max 50); godot_play_errors reads them. The addon never auto-stops playback — the agent calls godot_stop_scene.

Wire protocol

Pinned to addon 0.6.3, protocol generations 1.0 + 1.2 + 1.3 (27 RPC methods). If you change either half, update this section in the same commit.

Transport

Property Value
Address Loopback only — 127.0.0.1
Port 8765, falling back to 8765–8774 when busy
Encoding UTF-8, newline-delimited JSON (one object per line, no length prefix, no binary)
Auth 32-hex token presented by the addon on editor_ready

Message shapes

// Request — server → addon
type GodotRpcRequest = {
  id: string;        // randomUUID from the caller
  method: string;    // see the roster below
  ...params          // method-specific
};

// Response — addon → server (paired by id)
type GodotRpcResponse =
  | { id: string; ok: true;  result: unknown; routedTo?: string }
  | { id: string; ok: false; error: string;    routedTo?: string };

// Event — addon → server (no id)
type GodotRpcEvent =
  | { type: "editor_ready"; godotVersion: string; projectPath: string;
      token?: string; addonVersion?: string; clientId?: string }
  | { type: "scene_changed"; path: string; clientId?: string }
  | { type: "play_error"; severity: string; message: string; clientId?: string }
  | { type: "disconnected"; clientId?: string };

Framing rule (0.6.3 fix): disambiguate request vs event by data.has("method"), never by data.has("type"). list_project_files accepts a type parameter for filtering, and a request carrying both method and type is silently dropped by the older heuristic.

Endpoint file

The server publishes { host, port, token } to a JSON file the addon polls every second (_endpoint_config_path() in plugin.gd); the addon reconnects automatically when the file changes.

Field Type Notes
host string Must be 127.0.0.1 or localhost on reuse; anything else is rejected and a fresh token is generated
port number 1–65535; outside this range falls back
token string 32-hex (/^[0-9a-f]{32}$/i); mismatch falls back
version number Wire format version; current = 1
updatedAt string ISO timestamp written on every successful listen

Written atomically (tmp + rename) and deliberately not deleted on server stop, so the next start reuses the token and skips handshake churn.

Method roster (27 RPCs + ping)

Tool (when enabled) RPC method Protocol gen
godot_editor_info get_editor_info 1.0
godot_open_scenes get_open_scenes 1.0
godot_edited_scene get_edited_scene 1.0
godot_open_scene open_scene 1.0
godot_reload_scene reload_scene 1.0
godot_run_scene run_current_scene (+ wait_ms) 1.0
godot_run_main_scene play_main_scene (+ wait_ms, ≡ F5) 1.0
godot_import_resources import_resources (+ optional paths) 1.0
godot_play_errors get_play_errors (+ optional clear) 1.0
godot_stop_scene stop_scene 1.0
godot_get_scene_tree get_scene_tree (+ max_depth) 1.0
godot_get_node_properties get_node_properties 1.0
godot_get_debugger_state get_debugger_state 1.2
godot_set_breakpoint set_breakpoint (+ condition?, remove?) 1.2
godot_find_unused_resources find_unused_resources (+ root?) 1.2
godot_get_project_setting get_project_setting 1.2
godot_set_project_setting set_project_setting 1.2
godot_lint_scripts lint_scripts 1.2
godot_export_project export_project (+ preset, output_dir, debug?) 1.2
godot_list_project_files list_project_files (+ type?, pattern?, limit?, cursor?) 1.3
godot_resolve_uid resolve_uid (uid? xor path?) 1.3
godot_wait_for_import_done wait_for_import_done (+ timeout_ms?) 1.3
godot_list_global_classes list_global_classes 1.3
godot_find_class_name_conflicts find_class_name_conflicts (+ include_addons?) 1.3
godot_inspect_script inspect_script 1.3
godot_list_export_presets list_export_presets 1.3
godot_check_export_templates check_export_templates 1.3

ping is a protocol-level health check with no user-facing tool. The allowlist lives in dsh-godot-tool/src/protocol.ts (GODOT_RPC_ALLOWED_METHODS) and the tool-to-method gate map in GODOT_RPC_METHOD_TOOL; requests outside the allowlist are rejected before they reach the bridge.

Timeout ladder

Constant Value Used by
GODOT_RPC_DEFAULT_PORT 8765 listen
GODOT_RPC_FALLBACK_PORT_END 8774 listen fallback
GODOT_RPC_DEFAULT_WAIT_MS 3000 play-error window
GODOT_RPC_MAX_WAIT_MS 15000 play-error window cap
GODOT_RPC_BASE_TIMEOUT_MS 8000 default request timeout
GODOT_RPC_EXPORT_TIMEOUT_MS 5 × 60 000 export_project hard kill
GODOT_RPC_EXPORT_GRACE_MS 15 000 slack after export returns
GODOT_RPC_GRACE_PERIOD_MS 8000 disconnect grace window
GODOT_LIST_FILES_DEFAULT_LIMIT / MAX_LIMIT 500 / 5000 list_project_files pagination
GODOT_WAIT_DEFAULT_TIMEOUT_MS / MAX 30 000 / 60 000 wait_for_import_done

Playback error collection

run_current_scene / play_main_scene clear the buffer, start playback, and return after a configurable window (default ~3 s, max 15 s):

{
  "started": true,
  "playing": true,
  "waitMs": 3000,
  "playMethod": "play_current_scene",
  "errors": [{ "severity": "error", "message": "..." }]
}

Sources (rpc_debugger.gd): the Output dock's ERROR / WARN messages, the Debugger Errors page, breakpoint hit reasons. The addon never auto-stops playback on error — the caller must invoke stop_scene.

Security model

Gate Where What it does
Server loopback bind bridge.ts — server.listen(port, '127.0.0.1') The kernel refuses non-loopback binds
Token handshake editor_ready — 32-hex compare Failures counted as missing_token (addon < 0.2.0) / bad_token (stale token)
Method allowlist protocol.ts — GODOT_RPC_ALLOWED_METHODS Anything outside is rejected before the bridge sees it
Tool gate (double layer) registration + dispatch Only enabledTools are registered (model schema never contains a disabled tool), and the bridge re-checks the gating tool per wire method
Param hygiene protocol.ts — checkHygiene Strings ≤ 4096, arrays ≤ 512, nested strings ≤ 4096
set_project_setting denylist protocol.ts Forbids autoload/*, input/*, editor_plugins/enabled, debug logging/warnings/shapes/colors, TLS certificate-bundle override, project_settings_override/*
Cross-project routing bridge.ts A request only reaches a client whose projectPath shares the calling session's cwd
Disconnect rejection bridge.ts In-flight requests routed to a disconnected client fail with client disconnected

Glossary (EN ↔ 中文)

English 中文 One-line definition
addon 插件 Godot editor extension; lives in <project>/addons/<name>/; declared in project.godot [editor_plugins]
EditorPlugin 编辑器插件基类 Godot base class for code that runs in the editor process
EditorDebuggerPlugin 编辑器调试器插件基类 Hooks ScriptEditorDebugger signals to capture runtime errors without spawning a process
EditorInterface 编辑器接口单例 Static accessor for opening scenes, controlling playback, listing open scenes, etc.
ProjectSettings 项目设置 Godot's project-level configuration store; keys use /-separated paths like autoload/Foo
autoload 自动加载 Singleton scripts registered under autoload/* keys. Denylisted for set_project_setting
endpoint file 端点文件 The bridge's published {host, port, token} JSON file the addon polls every second
handshake 握手 The editor_ready event sent on first TCP connect, carrying the addon-supplied token and addonVersion
token 令牌 32-hex shared secret that authorises the addon to talk to the bridge
addonVersion 插件版本 The addon's plugin.cfg version, surfaced on editor_ready so the server can warn about protocol mismatches
routedTo 路由目标 The clientId actually used when a request omits one and the bridge auto-routes to the first authenticated client
tool gate 工具闸 The double-layer (registration + dispatch) check that a tool is enabled before an RPC is admitted
JSON-lines JSON 行协议 UTF-8 stream where each \n-delimited line is one JSON object
play error 播放期错误 Runtime error / warning captured during a run_current_scene / play_main_scene session
ping 健康检查 The single RPC method that does not correspond to a user-facing tool
ring buffer 环形缓冲 Bounded buffer (max 50) holding recent play_error events

Godot addon (agent_rpc)

The client half — a TCP JSON-lines bridge that lets an AI agent drive the editor: loopback-only transport, token handshake, default-off tool gate.

What the addon folder ships

addons/agent_rpc/
  plugin.cfg        # plugin manifest (name, version="0.6.3", entry script)
  plugin.gd         # EditorPlugin — TCP client, message dispatch, play-error ring buffer
  rpc_debugger.gd   # EditorDebuggerPlugin — hooks ScriptEditorDebugger output / debug_data / breaked

No autoloads, no scene files — everything runs in the editor process (@tool).

Installation

  1. Copy addons/agent_rpc/ into your Godot project's addons/ folder.
  2. In Godot: Project Settings → Plugins → enable "Agent RPC".
  3. Start your agent (e.g. DeepSeek Harness with dsh-godot-tool). It listens on 127.0.0.1:8765, falling back to 8765–8774, and publishes an endpoint file the addon polls.
  4. The addon connects within ~1 s and prints Agent RPC: connected to … in the Godot Output panel.

After upgrading the addon, re-install it and reload the project or restart Godot.

Endpoint configuration

The addon polls an endpoint file every second and reconnects automatically. The file is resolved in this order:

  1. Environment variable AGENT_RPC_ENDPOINT — e.g. point it at $DSH_HOME/godot/endpoint.json when the DeepSeek Harness dsh-godot-tool plugin publishes there. (Recommended.)
  2. ProjectSetting key agent_rpc/endpoint_file — an explicit per-project path.
  3. Legacy default — ~/.pi/agent/x-agent-godot-rpc.json.

The environment variable wins; both override the legacy default. The file itself must contain { "host": "127.0.0.1", "port": 8765, "token": "<32-hex>", "version": 1 }.

Handshake

On the first TCP connect the addon sends an editor_ready event carrying its token and addonVersion. Reusing the previous endpoint file's token makes "start Godot first, then start the agent" land in roughly one second without re-installing anything.

Troubleshooting

Symptom Likely cause First check
No connection, missing_token handshake failure Addon predates 0.2.0 (no token on editor_ready) plugin.cfg version; re-install the addon
No connection, bad_token handshake failure Addon holds a stale token; server wrote a new one Confirm both ends read the same endpoint file; re-install the addon to force a re-read
run_current_scene returns empty errors despite a script error EditorDebuggerPlugin did not hook ScriptEditorDebugger (debugger UI not yet built, or Godot version drift) Open Godot's Debugger panel and check the plugin is active
Playback stuck after run_current_scene wait_ms elapsed but the addon never auto-stops This is by design — call stop_scene
export_project hangs past 5 minutes Headless Godot blocked on a missing export template or a long script startup Bridge kills the process at the export timeout; check Godot's output
set_project_setting rejected with "forbidden prefix" Wrote to autoload/*, input/*, editor_plugins/enabled, etc. The denylist in the security model is final
list_project_files always times out Server used the older data.has("type") framing heuristic The framing rule must be data.has("method") — see message shapes
Editor freezes ~30 s when linting fails --check-only ran on the main thread Use the threaded worker pattern in plugin.gd; _exit_tree must wait for the thread

Harness plugin (dsh-godot-tool)

The server half — @deepseek-ai/dsh-godot-tool, a loopback TCP JSON-lines server plus 27 godot_* model-facing tools.

What it does

  1. GodotRpcBridge (src/bridge.ts) — a node:net server bound to loopback that speaks the addon's wire protocol: newline-delimited JSON, token handshake (editor_ready), per-connection clientId, request/response correlation by id, and play_error/scene_changed event capture.
  2. 27 tools (src/tools.ts) — one defineTool per wire method, each dispatching through the bridge. The complete roster matches the method roster.

The plugin publishes an endpoint file the addon polls every second ($DSH_HOME/godot/endpoint.json by default, or Config.endpointPath), so "start the harness first, then open Godot" lands in roughly a second without reinstalling the addon.

Configuration

Field Default Meaning
port 8765 Preferred loopback listen port; the bridge walks up to fallbackPortEnd.
fallbackPortEnd 8774 Last port tried before reporting port exhaustion.
token auto-generated 32-hex Shared secret the addon must present on editor_ready.
endpointPath ~/.dsh/godot/endpoint.json Endpoint file the addon polls; point it at the same file the addon reads.
enabledTools [] Tool names to register. Default empty — the model sees no godot tool until the deployment opts in.

The endpoint file is written atomically (tmp + rename) and deliberately retained on plugin unload: the next start reuses the token and avoids handshake churn.

Tool gate

enabledTools is the double-layer switch from the reference desktop implementation, collapsed into one plugin:

  • Registration layer — only opted-in tools are registered, so the model's function-call schema never contains a disabled tool.
  • Dispatch layer — the bridge re-checks the gating tool for every wire method (GODOT_RPC_METHOD_TOOL), so even a direct bridge caller cannot reach a disabled method.

Export shape

A function/namespace plugin: it exports name / inject / Config / apply and NO default. A stray export default would collapse the module via the Loader's unwrapExports and drop inject — the same failure mode as the harness postmortem 0001-acp-default-export-drops-inject.

Model experience

Tool schemas — the model sees the generated godot_* schemas for the tools listed in Config.enabledTools only; descriptions name the wire method's parameters and any context-size caps (scene tree serialization budget, list pagination). Fixed schema cost on every request for each enabled tool; prefix-stable while the tool set is unchanged.

Tool-call results — each successful call returns the addon's result verbatim as the canonical JSON value (e.g. get_editor_info → { godotVersion, projectPath, editedScene, playing }). A wire-level ok:false or a local refusal (method not allowed, tool disabled, param hygiene, unknown client, cross-project route, timeout, client disconnected) surfaces as Error: <message>. Result tokens scale with the addon's response, bounded by its serialization budgets (5000-node scene tree, 500/5000 file pagination, 50-error ring).

Known limitations

  • Wire protocol pinned to the reference addon — the 27-method allowlist, timeout ladder, and denylist mirror agent_rpc 0.6.x; a newer addon protocol generation requires extending src/protocol.ts.
  • No condition expressions for breakpoints — the Godot 4 breakpoint API ignores conditions; set_breakpoint reports conditionIgnored and the model must rely on line breakpoints only.
  • play_error capture depends on editor debugger UI timing — EditorDebuggerPlugin hooks the ScriptEditorDebugger; on some Godot versions the hook attaches only after the Debugger panel is built. Verify with a real editor before relying on error collection.
  • Single active client routing only — when several Godot instances connect, an unspecified call routes to the first authenticated client; clientId selection is not yet exposed as a tool parameter.

Godot mode (agent preset)

The third half — assembles the two halves above into a ready-to-use Godot-dev agent. Two pieces in this repo: agent-presets/godot/ (the preset itself) and examples/web-profile.cordis.patch.yml (optional host-plane wiring for deployments without presets).

What it is

A DeepSeek Harness agent preset: preset.yml declares the mode metadata (name, description, order) and agent.cordis.yml is the agent-plane composition. It assembles the repo's two halves into a lean agent for Godot game development:

  • Persona — a Godot 4.x dev agent: GDScript, scenes (.tscn), resources (.tres), shaders, editor plugins, export builds; drive the editor through the godot_* tools first, use PowerShell for headless runs and anything outside the editor bridge.
  • Tools — shell (PowerShell on Windows), file read/write/search, background jobs (tool-jobs), compaction (long sessions), subagent delegation, goals, plan mode, web search (fetch disabled), todo, ask-user.
  • Deliberately absent — workflow orchestration, ralph loops, skill loading, and other heavy machinery.
  • Editor driving — the godot_* tools are registered by the tool-godot row inside the preset (the dsh-godot-tool/ plugin in this repo). The preset's standing mount is created once per process, so there is still exactly one bridge and one endpoint file (~/.dsh/godot/endpoint.json) for the Godot agent_rpc addon to poll; the bridge starts when Godot mode is first used. The row registers into this preset's scope layer, so only Godot-mode sessions see the godot_* tools — the catalogs of standard/code/other modes stay clean.

Installation

  1. Preset — copy agent-presets/godot/ to ~/.dsh/.agent-presets/godot/ (the harness user-preset directory), then select Godot模式 in the GUI.
  2. No host wiring needed — the tool-godot row is already inside the preset (enabledTools = all 27 tools); trim the list to tighten the tool set. Do not also register tool-godot in the web profile's cordis.patch.yml — back on the host plane, the godot_* tools become globally visible in every mode. Only deployments without agent presets (CLI/TUI, --patch source installs) wire it manually per examples/web-profile.cordis.patch.yml.
  3. Open the Godot project, start the harness, and the agent drives the editor in Godot mode.

Bridge lifecycle and mode switching

  • The bridge starts with the preset's standing mount on the first session that picks Godot mode and stays up for the harness process lifetime; switching a session away from Godot mode (or deleting it) does not stop the bridge — that is the preset architecture's standing-mount behavior. Tool visibility follows the scope, though: after the switch the session no longer sees the godot_* tools, and other modes are unaffected throughout.
  • Do not duplicate this preset with the GUI's "copy preset" to make a second mode — the copy would mount a second bridge that fights this one for the port and overwrites the endpoint file.

Maintenance

  • agent.cordis.yml comments mark deployment assumptions (Windows shell, tool-godot owned by the preset); adjust them per the comments when moving to another deployment.

Development

Repository layout

addons/agent_rpc/          # Godot editor addon (client half), GDScript
  plugin.cfg / plugin.gd / rpc_debugger.gd
dsh-godot-tool/            # Harness plugin (server half), TypeScript
  src/bridge.ts            # GodotRpcBridge — loopback TCP server, handshake, routing
  src/protocol.ts          # method allowlist, timeout ladder, hygiene, denylist
  src/tools.ts             # the 27 godot_* tool definitions
  src/endpoint.ts          # endpoint-file publish / read / remove
  src/index.ts             # plugin entry (name / inject / Config / apply)
  tests/                   # vitest suites (bridge, protocol, tools, loader-composition, preset-scope)
agent-presets/godot/       # Godot mode agent preset (preset.yml + agent.cordis.yml)
examples/                  # optional host-plane wiring for non-preset deployments (web-profile.cordis.patch.yml)

Running the harness-plugin tests

The TypeScript side is tested with vitest; tests/fake-addon.ts fakes the Godot addon over a real loopback socket, so the suites exercise the actual wire protocol without a Godot editor:

cd dsh-godot-tool
pnpm exec vitest run

Suites: bridge.spec.ts (transport, handshake, routing, timeouts), protocol.spec.ts (allowlist, hygiene, denylist, endpoint schema), tools.spec.ts (tool gate), loader-composition.spec.ts (export shape), preset-scope.spec.ts (scope layering: a preset mount registers the godot_* tools only in that scope's layer while the host global catalog stays clean).

Smoke-testing against a real Godot editor

.smoke-test/ is a minimal throwaway Godot 4 project (main scene prints SMOKE_OK from Godot 4.7) with the addon installed under its own addons/. It is excluded from git; use it to sanity-check the wire path by hand:

  1. Open .smoke-test/project.godot in the Godot editor (the addon plugin is enabled in that project).
  2. Start the harness with tool-godot enabled; the bridge publishes its endpoint file.
  3. Run the scene (F6): the Output panel shows Agent RPC: connected to … and the print line; then drive it from the agent with godot_editor_info / godot_run_scene / godot_play_errors.

Keeping the docs in sync

The README pins the wire protocol to the addon version and protocol generations. If you change either half (a new method, a timeout constant, a config field), update the same commit:

  • dsh-godot-tool/src/protocol.ts — allowlist, timeout ladder, hygiene limits, denylist, endpoint schema.
  • The method roster, timeout ladder, and security model tables above.
  • The addon's ADDON_VERSION (plugin.gd) and version in plugin.cfg when the addon changes.
  • When the Godot mode changes, sync the Godot mode section.

Compatibility

Addon 0.6.3, harness plugin 0.1.0-rc.5 (@deepseek-ai/dsh-godot-tool), protocol generations 1.0 + 1.2 + 1.3 (27 RPCs). Tested against Godot 4.4 stable and 4.7 stable (Windows). The addon reads AGENT_RPC_ENDPOINT / agent_rpc/endpoint_file since this repository's patch; the legacy ~/.pi/... default still works. Newer addon versions may grow methods, but the 1.0 set stays intact. The Godot mode (agent-presets/godot/) is maintained in the same repo and tracks the same version lock.

License

MIT — see LICENSE.