← Platforms

Godot

Remote debugging for Godot 4 games — a pure GDScript addon that connects your running game to OmniDebugLink, so AI tools can inspect the live scene tree, inject real input and key events, and capture screenshots on any export platform.

The Godot client is a zero-dependency GDScript addon: WebSocketPeer, JSON and image handling are all engine built-ins, so there is nothing to resolve, no native code and no threads. Drop the addon into a project, start it with a device token, and a game running on desktop, mobile or web exports becomes inspectable and drivable from your AI coding tool — traverse the scene tree, read logs, tap buttons, type text, fire InputMap actions, even inject hardware-level key events like Escape.

Core features

Pure GDScript, zero dependencies

No third-party packages, no native plugins, no C# required. The addon is plain GDScript source you can read in one sitting, and it ships under the MIT license.

One code path for every export

Desktop, mobile and web exports all run the same addon — the connection, heartbeat and reconnect logic never forks per platform.

Main-threaded by design

Everything pumps from _process on the main thread, so task handlers can safely touch any Godot API — and handlers may await frames or timers without blocking other tasks.

Real input, including keys

Injection goes through the real event pipeline (Viewport.push_input / Input.parse_input_event), so GUI, _unhandled_input and InputMap actions behave exactly as with a physical user. send_key delivers genuine key events — Escape, arrows, Enter — not soft approximations.

Scene-tree introspection

Flat tree dumps carrying paths, types, scripts, displayed text and visibility; per-node deep views with properties, signal connections and groups; property read/write with type coercion.

Read-only mode built in

OmniDebugLink.actions_enabled gates every write task. Set it false and the SDK becomes a read-only observer, announcing that mode when it connects.

Requirements

Install the addon

The Godot editor has no "install from git URL" flow, so the addon is copied into your project. Two options:

1

Option A — copy from the repository

Download the repository as a ZIP or clone it, then copy only the addons/omni_debug_link/ folder into your project, so your project contains res://addons/omni_debug_link/plugin.cfg. Do not drop the whole repository into addons/ — the plugin is discovered by that exact path.

git clone https://github.com/omnidebuglink/omnidebuglink_godot.git
2

Option B — Godot Asset Library

Once listed there: AssetLib tab → search "OmniDebugLink" → Download.

3

Enable the plugin

Project → Project Settings → Plugins → OmniDebugLink → Enable. Enabling registers the OmniDebugLink autoload in your project settings automatically. If you would rather not enable the plugin, add the autoload by hand instead: Project Settings → Autoload → path res://addons/omni_debug_link/runtime/omnidebug_link.gd.

Wire it up

1

Start it

That is all. The SDK keeps the connection, announces its capabilities, and answers tasks. When you are done — or on a release build's title screen — call OmniDebugLink.stop().

func _ready() -> void:
    OmniDebugLink.start("your-client-token")
2

Gate shipped builds

Recommended for released games: keep the SDK disabled unless a debug flag is set.

if OS.get_environment("OMNIDEBUGLINK_TOKEN") != "":
    OmniDebugLink.start(OS.get_environment("OMNIDEBUGLINK_TOKEN"))
else:
    OmniDebugLink.actions_enabled = false  # or don't start() at all

Runtime API

MemberMeaning
OmniDebugLink.start(client_token, url := "")Connect (the optional url overrides the endpoint, for self-hosting)
OmniDebugLink.stop()Disconnect and stop all timers
OmniDebugLink.actions_enabledMaster switch for every write task; false = read-only observation mode (reported on connect)
OmniDebugLink.connected() / state_changedConnection state and signal for your own UI
OmniDebugLink.log / log_warning / log_error(msg)Feed your own entries into read_logs
OmniDebugLink.tasks.register(type, handler, description, payload_schema)Register custom tasks
OmniDebugLink.tasks.unregister(type)Remove a task

Built-in tasks (25)

Everything below ships in the addon. The AI sees the full list — with per-task payload schemas — automatically; you never need to configure anything server-side. Write tasks are all gated by actions_enabled.

Read tasks

TaskWhat it does
scene_traverseFlat dump of the scene tree: paths, types, scripts, displayed text and visibility, capped at 3000 nodes.
find_objectsSearch by node name (substring or regex), displayed text, or type; matches return centers and a click_target that feeds ui_click.
view_componentOne node in depth: properties, signal connections, groups and children.
get_propRead a property with JSON-friendly conversion (nodes become paths, vectors become arrays).
read_logsFiltered query (level / substring / limit / time) over SDK and engine logs.
wait_forPoll every 200 ms until a node appears or a property reaches a value; timeouts return found: false, not errors. Keeps working under time_scale = 0 and while the tree is paused.
get_perfFPS, process/physics times, draw calls, memory and video memory, your custom performance monitors, optional frame-time percentiles (p50/p95/p99).
screenshotViewport capture as JPEG, long edge capped at 1920 px and auto-compressed to fit the frame budget.

Write tasks

TaskWhat it does
ui_clickClick a Control by path or displayed text (exact match first, substring fallback, index to disambiguate) through the real GUI event pipeline; returns the path of the node that actually received it.
tap_screenTap normalized 0-1 coordinates (origin top-left); an optional touch flag adds ScreenTouch events for mobile paths.
swipeDrag with per-frame deltas and relative motion, so inertia and velocity tracking behave; the optional touch flag uses ScreenDrag.
long_pressPress and hold (default 800 ms), by coordinates or node path.
input_textType into LineEdit / TextEdit (or any node with a text property) and fire the change signals.
send_keyInject key events — Escape, arrows, Enter… — as real InputEventKey through Input.parse_input_event, so they reach GUI focus and _unhandled_input exactly like a physical keyboard. Key by name or keycode, with tap / press / release modes.
send_actionFire an InputMap action by name via InputEventAction.
set_propProperty write with type coercion — JSON arrays become Vector2 / Color / … — and the value is read back to confirm.
set_time_scaleSet Engine.time_scale and/or SceneTree.paused: slow motion, or freeze for inspection.
call_methodCall any method on any node — the escape hatch that lets the AI drive game logic directly.
change_scene / reload_sceneSwitch or reload the current scene.
list_dir / read_fileRead-only access under user://, size-capped; res:// game source never leaves the device.

Basics

TaskWhat it does
echo / pingRound-trip connectivity checks.
get_statsUptime, task count, connection state, library and Godot versions, platform, current scene.

Custom tasks

Register your own task with a type, handler, description and payload schema. Handlers run on the main thread, may await (frames, timers), and return any JSON-friendly Dictionary; signal failure by returning OmniDebugLink.task_error("message"). Registering or unregistering automatically re-announces capabilities — no changes needed on the server side.

func _ready() -> void:
    OmniDebugLink.start("your-client-token")
    OmniDebugLink.tasks.register(
        "give_gold",
        func(payload: Dictionary) -> Dictionary:
            var amount := int(payload.get("amount", 100))
            GameState.gold += amount
            return {"gold": GameState.gold},
        "Adds gold to the player wallet.",
        {
            "type": "object",
            "properties": {"amount": {"type": "integer", "minimum": 1}},
            "required": ["amount"],
        }
    )

What read_logs captures

SourceCaptured?
OmniDebugLink.log / log_warning / log_erroralways
Everything the engine prints — print, push_error, engine errorsenable Project Settings → Debug → File Logging; the SDK tails user://logs/godot.log and merges it in

Good to know