simulate_input
Execute batched keyboard, mouse, and UI click inputs sequentially in a running Godot project. Supports key presses, mouse clicks and motion, UI element clicking, and Godot input actions.
Instructions
Simulate batched sequential input in a running Godot project. Requires an active runtime session (run_project or attach_project). Use get_ui_elements first to discover element names and paths for click_element actions.
Each action object requires a "type" field. Valid types and their specific fields:
key: keyboard event (key: string, pressed: bool, shift/ctrl/alt: bool)
mouse_button: click at coordinates (x, y: number, button: "left"|"right"|"middle", pressed: bool, double_click: bool)
mouse_motion: move cursor (x, y: number, relative_x, relative_y: number)
click_element: click a UI element by node path or node name (element: string, button, double_click)
action: fire a Godot input action (action: string, pressed: bool, strength: 0–1)
wait: pause between actions (ms: number)
Examples:
Press and release Space: [{type:"key",key:"Space",pressed:true},{type:"wait",ms:100},{type:"key",key:"Space",pressed:false}]
Click a UI button (discover path with get_ui_elements first): [{type:"click_element",element:"StartButton"}]
Left-click at viewport coordinates: [{type:"mouse_button",x:400,y:300,button:"left",pressed:true},{type:"mouse_button",x:400,y:300,button:"left",pressed:false}]
Fire a Godot action: [{type:"action",action:"jump",pressed:true},{type:"wait",ms:200},{type:"action",action:"jump",pressed:false}]
Type "hello": [{type:"key",key:"H",pressed:true},{type:"key",key:"H",pressed:false},{type:"key",key:"E",pressed:true},{type:"key",key:"E",pressed:false},{type:"key",key:"L",pressed:true},{type:"key",key:"L",pressed:false},{type:"key",key:"L",pressed:true},{type:"key",key:"L",pressed:false},{type:"key",key:"O",pressed:true},{type:"key",key:"O",pressed:false}]
Returns { success, actions_processed, warnings? }. Errors if no runtime session is active.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| actions | Yes | Array of input actions to execute sequentially. Each object must have a "type" field. |
Implementation Reference
- src/tools/project-tools.ts:1186-1264 (handler)Handler function for the simulate_input tool. Validates runtime session, sends an 'input' command to the Godot bridge via UDP with the actions array, calculates a dynamic timeout based on wait durations, and returns the result including actions_processed count.
export async function handleSimulateInput(runner: GodotRunner, args: OperationParams) { args = normalizeParameters(args); const sessionError = ensureRuntimeSession(runner, 'simulate input'); if (sessionError) { return sessionError; } const actions = args.actions; if (!Array.isArray(actions) || actions.length === 0) { return createErrorResponse('actions must be a non-empty array of input actions', [ 'Provide at least one action object with a "type" field', ]); } // Calculate timeout: sum of all wait durations + 10s buffer let totalWaitMs = 0; for (const action of actions) { if ( typeof action === 'object' && action !== null && action.type === 'wait' && typeof action.ms === 'number' ) { totalWaitMs += action.ms; } } const timeoutMs = totalWaitMs + 10000; try { const { response: responseStr, runtimeErrors } = await runner.sendCommandWithErrors( 'input', { actions }, timeoutMs, ); let parsed: { success?: boolean; error?: string; actions_processed?: number }; try { parsed = JSON.parse(responseStr); } catch { return createErrorResponse(`Invalid response from bridge: ${responseStr}`, [ 'The game may not have fully initialized yet', 'Try again after a few seconds', ]); } if (parsed.error) { return createErrorResponse(`Input simulation error: ${parsed.error}`, [ 'Check action types and parameters', 'Ensure key names are valid Godot key names', ]); } const payload: Record<string, unknown> = { success: true, actions_processed: parsed.actions_processed, tip: 'Call take_screenshot to verify the input had the intended visual effect.', }; if (runtimeErrors.length > 0) { payload.warnings = runtimeErrors.slice(0, MAX_RUNTIME_ERROR_CONTEXT_LINES); } return { content: [ { type: 'text', text: JSON.stringify(payload), }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return createErrorResponse(`Failed to simulate input: ${errorMessage}`, [ 'Ensure the project is running (use run_project first)', 'The bridge may not be ready yet — use get_debug_output to investigate', 'Check that UDP port 9900 is not blocked', ]); } } - src/tools/project-tools.ts:285-365 (schema)Schema definition for the simulate_input tool. Describes the input schema with action types (key, mouse_button, mouse_motion, click_element, action, wait) and their respective properties. Specifies 'actions' array as required.
name: 'simulate_input', description: 'Simulate batched sequential input in a running Godot project. Requires an active runtime session (run_project or attach_project). Use get_ui_elements first to discover element names and paths for click_element actions.\n\nEach action object requires a "type" field. Valid types and their specific fields:\n- key: keyboard event (key: string, pressed: bool, shift/ctrl/alt: bool)\n- mouse_button: click at coordinates (x, y: number, button: "left"|"right"|"middle", pressed: bool, double_click: bool)\n- mouse_motion: move cursor (x, y: number, relative_x, relative_y: number)\n- click_element: click a UI element by node path or node name (element: string, button, double_click)\n- action: fire a Godot input action (action: string, pressed: bool, strength: 0–1)\n- wait: pause between actions (ms: number)\n\nExamples:\n1. Press and release Space: [{type:"key",key:"Space",pressed:true},{type:"wait",ms:100},{type:"key",key:"Space",pressed:false}]\n2. Click a UI button (discover path with get_ui_elements first): [{type:"click_element",element:"StartButton"}]\n3. Left-click at viewport coordinates: [{type:"mouse_button",x:400,y:300,button:"left",pressed:true},{type:"mouse_button",x:400,y:300,button:"left",pressed:false}]\n4. Fire a Godot action: [{type:"action",action:"jump",pressed:true},{type:"wait",ms:200},{type:"action",action:"jump",pressed:false}]\n5. Type "hello": [{type:"key",key:"H",pressed:true},{type:"key",key:"H",pressed:false},{type:"key",key:"E",pressed:true},{type:"key",key:"E",pressed:false},{type:"key",key:"L",pressed:true},{type:"key",key:"L",pressed:false},{type:"key",key:"L",pressed:true},{type:"key",key:"L",pressed:false},{type:"key",key:"O",pressed:true},{type:"key",key:"O",pressed:false}]\n\nReturns { success, actions_processed, warnings? }. Errors if no runtime session is active.', inputSchema: { type: 'object', properties: { actions: { type: 'array', description: 'Array of input actions to execute sequentially. Each object must have a "type" field.', items: { type: 'object', properties: { type: { type: 'string', enum: ['key', 'mouse_button', 'mouse_motion', 'click_element', 'action', 'wait'], description: 'The type of input action', }, key: { type: 'string', description: '[key] Key name (e.g. "W", "Space", "Escape", "Up")', }, pressed: { type: 'boolean', description: '[key, mouse_button, action] Whether the input is pressed (true) or released (false)', }, shift: { type: 'boolean', description: '[key] Shift modifier' }, ctrl: { type: 'boolean', description: '[key] Ctrl modifier' }, alt: { type: 'boolean', description: '[key] Alt modifier' }, button: { type: 'string', enum: ['left', 'right', 'middle'], description: '[mouse_button, click_element] Mouse button (default: left)', }, x: { type: 'number', description: '[mouse_button, mouse_motion] X position in viewport pixels', }, y: { type: 'number', description: '[mouse_button, mouse_motion] Y position in viewport pixels', }, relative_x: { type: 'number', description: '[mouse_motion] Relative X movement in pixels', }, relative_y: { type: 'number', description: '[mouse_motion] Relative Y movement in pixels', }, double_click: { type: 'boolean', description: '[mouse_button, click_element] Double click', }, element: { type: 'string', description: '[click_element] Identifies the UI element to click. Accepts: absolute node path (e.g. "/root/HUD/Button"), relative node path, or node name (BFS matched). Use get_ui_elements to discover valid names and paths.', }, action: { type: 'string', description: '[action] Godot input action name (as defined in Project Settings > Input Map)', }, strength: { type: 'number', description: '[action] Action strength (0–1, default 1.0)', }, ms: { type: 'number', description: '[wait] Duration in milliseconds to pause before the next action', }, }, required: ['type'], }, }, }, required: ['actions'], }, }, - src/dispatch.ts:88-88 (registration)Registration mapping the tool name 'simulate_input' to the handleSimulateInput handler in the tool dispatch table.
simulate_input: (runner, args) => handleSimulateInput(runner, args), - src/utils/godot-runner.ts:1013-1024 (helper)Helper method sendCommandWithErrors on GodotRunner that sends a UDP command (like 'input') to the Godot bridge and captures runtime errors. Used by handleSimulateInput to communicate with the running Godot process.
async sendCommandWithErrors( command: string, params: Record<string, unknown> = {}, timeoutMs: number = 10000, ): Promise<{ response: string; runtimeErrors: string[] }> { const marker = this.getErrorCount(); const response = await this.sendCommand(command, params, timeoutMs); const newErrors = this.getErrorsSince(marker); const runtimeErrors = this.activeSessionMode === 'spawned' ? this.extractRuntimeErrors(newErrors) : []; return { response, runtimeErrors }; }