Stage API Reference
Complete parameter schemas for all 9 Stage MCP tools.
spatial_snapshot
Get an instant picture of every tracked node in the running game.
{
perspective?: "camera" | "node" | "point" // default: "camera"
focal_node?: string // node name or path; anchors perspective for "node"
focal_point?: [number, number, number] // world-space point; used for "point" perspective
radius?: number // default: 50.0
detail?: "summary" | "standard" | "full" // default: "standard"
groups?: string[] // filter to nodes belonging to these groups
class_filter?: string[] // Godot class names to include
include_offscreen?: boolean // default: false
token_budget?: number // default: 1500 (standard tier)
expand?: string // node path to expand with extra detail
}Response:
{
frame: number
timestamp_ms: number
node_count: number
included_nodes: number
truncated: boolean
nodes: {
[name: string]: {
class: string
path: string
global_position: [number, number, number]
velocity?: [number, number, number]
rotation_deg?: [number, number, number]
scale?: [number, number, number]
visible?: boolean
collision_layer?: number
collision_mask?: number
on_floor?: boolean
on_wall?: boolean
// ...additional properties for detail="full"
}
}
}spatial_delta
Get only what changed since the last spatial_snapshot baseline.
{
perspective?: "camera" | "node" | "point" // default: "camera"
radius?: number // default: 50.0
groups?: string[] // filter to nodes in these groups
class_filter?: string[]
token_budget?: number
}Delta computes changes against the baseline established by the most recent spatial_snapshot call. There is no since_frame parameter — the baseline is stored automatically when you call spatial_snapshot.
Response:
{
frame: number
baseline_frame: number
elapsed_ms: number
changed_node_count: number
nodes: {
[name: string]: {
// Only fields that changed since the baseline snapshot
global_position?: [number, number, number]
velocity?: [number, number, number]
// ...any other changed properties
}
}
}spatial_query
Run geometric queries against the current game state.
{
query_type: "nearest" | "radius" | "area" | "raycast" | "path_distance" | "relationship"
from: string | [number, number, number] // node name/path or [x, y, z] coordinate
to?: string | [number, number, number] // used by path_distance and relationship
k?: number // max results for nearest, default: 5
radius?: number // search radius for radius query, default: 20.0
groups?: string[]
class_filter?: string[]
token_budget?: number
}Response (nearest/radius):
{
result: {
origin: [number, number, number]
radius?: number // radius queries only
results: Array<{
node: string
class: string
global_position: [number, number, number]
distance: number
}>
}
}Response (area):
{
result: {
bounds: { min: [number, number, number], max: [number, number, number] }
results: Array<{
node: string
class: string
global_position: [number, number, number]
}>
}
}Response (raycast):
{
result: {
hit: boolean
node?: string
class?: string
global_position?: [number, number, number]
normal?: [number, number, number]
distance?: number
}
}Response (path_distance):
{
result: {
from: [number, number, number]
to: [number, number, number]
distance: number
reachable: boolean
waypoints: Array<[number, number, number]>
}
}Response (relationship):
{
result: {
from: string
to: string
distance: number
bearing_from_a: number // horizontal bearing from A to B (degrees)
bearing_from_b: number // horizontal bearing from B to A (degrees)
relative: [number, number, number] // offset from A to B in world space
occluded: boolean
}
}spatial_inspect
Deep inspection of a single node.
{
node: string // node name or scene path; required
include?: Array<
"transform" | "physics" | "state" | "children" |
"signals" | "script" | "spatial_context" | "resources"
>
// default: ["transform", "physics", "state", "children", "signals", "script", "spatial_context"]
}Response:
{
node: string
path: string
class: string
frame: number
transform?: {
global_position: [number, number, number]
rotation_deg: [number, number, number]
scale: [number, number, number]
}
physics?: {
velocity?: [number, number, number]
collision_layer?: number
collision_mask?: number
on_floor?: boolean
on_wall?: boolean
}
state?: {
visible: boolean
// ...node-class-specific state properties
}
children?: Array<{
name: string
class: string
relative_position: [number, number, number]
}>
signals?: Array<{
signal: string
connected_to: string
method: string
}>
script?: {
path: string
// ...exported script properties
}
spatial_context?: {
parent: {
name: string
class: string
relative_position: [number, number, number]
}
nearby: Array<{
node: string
class: string
distance: number
}>
}
resources?: {
// ...resource references attached to this node
}
}spatial_watch
Monitor nodes continuously for changes.
// Add a watch
{
action: "add"
watch: {
node: string
conditions?: Array<{
property: string
operator: "lt" | "gt" | "eq" | "changed"
value?: any
}> // default: []
track?: Array<"position" | "state" | "signals" | "physics" | "all">
// default: ["All"]
}
}
// Remove a watch
{
action: "remove"
watch_id: string
}
// List active watches
{
action: "list"
}
// Remove all watches
{
action: "clear"
}Add response:
{
watch_id: string
node: string
track: string[]
}List response:
{
watches: Array<{
watch_id: string
node: string
track: string[]
}>
}Remove response:
{
watch_id: string
result: "ok"
}spatial_config
Configure MCP behavior (clustering, bearing format, token limits, static patterns).
{
static_patterns?: string[] // node name patterns treated as static (not tracked)
state_properties?: { [class: string]: string[] } // extra properties to capture per class
cluster_by?: "group" | "class" | "proximity" | "none"
bearing_format?: "cardinal" | "degrees" | "both"
expose_internals?: boolean // include internal/hidden nodes
poll_interval?: number // polling interval in ms
token_hard_cap?: number // hard cap on response tokens
}No parameters → returns current config:
{
static_patterns: string[]
state_properties: { [class: string]: string[] }
cluster_by: string
bearing_format: string
expose_internals: boolean
poll_interval: number
token_hard_cap: number
}When parameters are set, the response echoes the new values with "result": "ok".
spatial_action
Interact with the running game: control execution, modify state, inject input, spawn/remove nodes.
// Pause or unpause the game
{ action: "pause", paused: boolean }
// Step N physics frames (game must be paused)
{ action: "advance_frames", frames: number }
// Advance by N seconds (game must be paused)
{ action: "advance_time", seconds: number }
// Move a node to a position
{
action: "teleport"
node: string
position: [number, number, number]
rotation_deg?: number
}
// Set a property on a node
{
action: "set_property"
node: string
property: string
value: any
}
// Call a method on a node
{
action: "call_method"
node: string
method: string
args?: any[]
}
// Emit a signal from a node
{
action: "emit_signal"
node: string
signal: string
args?: any[]
}
// Instantiate a scene as a child node
{
action: "spawn_node"
scene_path: string
parent: string
name?: string
}
// Remove a node from the scene tree
{
action: "remove_node"
node: string
}
// Simulate input action press
{
action: "action_press"
input_action: string
strength?: number
}
// Simulate input action release
{
action: "action_release"
input_action: string
}
// Simulate keyboard input
{
action: "inject_key"
keycode: string
pressed?: boolean
echo?: boolean
}
// Simulate mouse button click
{
action: "inject_mouse_button"
button: string
pressed?: boolean
position?: [number, number]
}All actions accept an optional return_delta: boolean (default: false) that appends a spatial_delta to the response.
Response:
{
action: string
result: "ok" | "error"
return_value?: any // for call_method with return value
delta?: object // if return_delta: true
error?: string // on failure
}scene_tree
Get scene tree structure without spatial data.
{
action: "roots" | "children" | "subtree" | "ancestors" | "find"
node?: string // required for children/subtree/ancestors
depth?: number // default: 3
find_by?: "name" | "class" | "group" | "script"
find_value?: string // search term for find action
include?: Array<"class" | "groups" | "script" | "visible" | "process_mode">
// default: ["Class", "Groups"]
token_budget?: number
}Response:
{
root: string
frame: number
node_count: number
tree: {
name: string
class: string
groups?: string[]
children: Array</* recursive */>
}
}clips
Manage dashcam clips and analyze recorded gameplay frame by frame.
// Mark the current moment and trigger clip capture
{
action: "add_marker"
marker_label?: string
marker_frame?: number // defaults to current frame
}
// Force-save dashcam buffer as clip
{
action: "save"
}
// Dashcam buffer state and config
{
action: "status"
}
// List saved clips
{
action: "list"
}
// Remove a clip
{
action: "delete"
clip_id: string
}
// List markers in a clip
{
action: "markers"
clip_id: string
}
// Spatial state at a frame
{
action: "snapshot_at"
clip_id?: string // defaults to most recent
at_frame?: number
at_time_ms?: number // alternative: find nearest frame
detail?: "summary" | "standard" | "full"
}
// Position/property timeseries
{
action: "trajectory"
clip_id?: string
node: string
from_frame?: number
to_frame?: number
properties?: string[] // default: ["position"]
sample_interval?: number // default: 1
}
// Search frames for spatial conditions
{
action: "query_range"
clip_id?: string
from_frame?: number
to_frame?: number
node?: string
condition?: {
type: "moved" | "proximity" | "velocity_spike" | "property_change"
| "state_transition" | "signal_emitted" | "entered_area" | "collision"
// + type-specific fields (target, threshold, property, signal)
}
token_budget?: number
}
// Compare two frames
{
action: "diff_frames"
clip_id?: string
frame_a: number
frame_b: number
}
// Search events
{
action: "find_event"
clip_id?: string
event_type?: string
event_filter?: string
from_frame?: number
to_frame?: number
}
// Viewport screenshot at a frame
{
action: "screenshot_at"
clip_id?: string
at_frame?: number
at_time_ms?: number
}
// List screenshot metadata
{
action: "screenshots"
clip_id?: string
}List response:
{
clips: Array<{
clip_id: string
frame_count: number
duration_ms: number
created_at: string // ISO 8601
markers: Array<{
marker_id: string
frame: number
marker_label: string
}>
}>
}query_range response:
{
clip_id: string
start_frame: number
end_frame: number
frame_count: number
frames: Array<{
frame: number
timestamp_ms: number
nodes: {
[name: string]: {
class: string
global_position?: [number, number, number]
velocity?: [number, number, number]
// ...other properties per detail level
}
}
}>
}