signals
Manage signal connections in Godot scenes by listing, connecting, or disconnecting signals between nodes with specified methods and flags.
Instructions
Signal connection management.
Actions (required params -> optional):
list (scene_path -> project_path): all signal connections
connect (scene_path, signal, from, to, method -> flags, project_path)
disconnect (scene_path, signal, from, to, method -> project_path)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform | |
| project_path | No | Path to Godot project directory | |
| scene_path | No | Path to scene file | |
| signal | No | Signal name | |
| from | No | Source node path | |
| to | No | Target node path | |
| method | No | Target method name | |
| flags | No | Connection flags |
Implementation Reference
- src/tools/composite/signals.ts:28-161 (handler)Main handler function for the 'signals' tool. Supports three actions: 'list' (parses scene to show all signal connections), 'connect' (appends a new [connection] line to the scene file), and 'disconnect' (removes a matching [connection] line from the scene file). Includes validation and duplicate/missing connection checks.
export async function handleSignals(action: string, args: Record<string, unknown>, config: GodotConfig) { const projectPath = (args.project_path as string) || config.projectPath const scenePath = args.scene_path as string if (!scenePath) throw new GodotMCPError('No scene_path specified', 'INVALID_ARGS', 'Provide scene_path.') const fullPath = safeResolve(projectPath || process.cwd(), scenePath) async function readScene() { try { return await readFile(fullPath, 'utf-8') } catch (error: unknown) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { throw new GodotMCPError(`Scene not found: ${scenePath}`, 'SCENE_ERROR', 'Check the file path.') } throw error } } switch (action) { case 'list': { const content = await readScene() const scene = parseSceneContent(content) return formatJSON({ scene: scenePath, count: scene.connections.length, // ⚡ Bolt: Return the pre-parsed connections array directly to avoid O(N) redundant object allocations connections: scene.connections, }) } case 'connect': { const signal = args.signal as string const from = args.from as string const to = args.to as string const method = args.method as string if (!signal || !from || !to || !method) { throw new GodotMCPError( 'signal, from, to, and method required', 'INVALID_ARGS', 'Provide signal name, source node, target node, and method name.', ) } const flags = args.flags as number | undefined if (flags !== undefined && typeof flags !== 'number') { throw new GodotMCPError('flags must be a number', 'INVALID_ARGS') } validateParameters(signal, from, to, method, flags) let content = await readScene() // Check for duplicate const scene = parseSceneContent(content) const existing = scene.connections.find( (c) => c.signal === signal && c.from === from && c.to === to && c.method === method, ) if (existing) { throw new GodotMCPError( 'Connection already exists', 'SIGNAL_ERROR', 'This signal connection is already defined.', ) } // Append connection const flagsAttr = flags !== undefined ? ` flags=${flags}` : '' const connectionLine = `\n[connection signal="${signal}" from="${from}" to="${to}" method="${method}"${flagsAttr}]\n` content = `${content.trimEnd()}\n${connectionLine}` await writeFile(fullPath, content, 'utf-8') return formatSuccess(`Connected: ${from}.${signal} -> ${to}.${method}()`) } case 'disconnect': { const signal = args.signal as string const from = args.from as string const to = args.to as string const method = args.method as string if (!signal || !from || !to || !method) { throw new GodotMCPError( 'signal, from, to, and method required', 'INVALID_ARGS', 'All four parameters are required.', ) } validateParameters(signal, from, to, method) const content = await readScene() const filtered: string[] = [] let pos = 0 const len = content.length let found = false while (pos < len) { let nextNewline = content.indexOf('\n', pos) if (nextNewline === -1) nextNewline = len const line = content.substring(pos, nextNewline) const trimmed = line.trim() if ( trimmed.startsWith('[connection') && trimmed.includes(`signal="${signal}"`) && trimmed.includes(`from="${from}"`) && trimmed.includes(`to="${to}"`) && trimmed.includes(`method="${method}"`) ) { found = true } else { filtered.push(line) } pos = nextNewline + 1 } if (!found) { throw new GodotMCPError( 'Connection not found', 'SIGNAL_ERROR', 'Check signal, from, to, and method parameters.', ) } await writeFile(fullPath, filtered.join('\n'), 'utf-8') return formatSuccess(`Disconnected: ${from}.${signal} -> ${to}.${method}()`) } default: throwUnknownAction(action, ['list', 'connect', 'disconnect']) } } - src/tools/registry.ts:270-289 (schema)Input schema definition for the 'signals' tool. Defines the 'action' enum (list/connect/disconnect) and parameters: project_path, scene_path, signal, from, to, method, flags. Only 'action' is required.
{ name: 'signals', description: 'Signal connection management.\n\nActions (required params -> optional):\n- list (scene_path -> project_path): all signal connections\n- connect (scene_path, signal, from, to, method -> flags, project_path)\n- disconnect (scene_path, signal, from, to, method -> project_path)', annotations: createAnnotations('Signals', { destructive: true }), inputSchema: { type: 'object' as const, properties: { action: { type: 'string', enum: ['list', 'connect', 'disconnect'], description: 'Action to perform' }, project_path: { type: 'string', description: 'Path to Godot project directory' }, scene_path: { type: 'string', description: 'Path to scene file' }, signal: { type: 'string', description: 'Signal name' }, from: { type: 'string', description: 'Source node path' }, to: { type: 'string', description: 'Target node path' }, method: { type: 'string', description: 'Target method name' }, flags: { type: 'number', description: 'Connection flags' }, }, required: ['action'], }, }, - src/tools/registry.ts:27-27 (registration)Import registration: imports handleSignals from './composite/signals.js'
import { handleSignals } from './composite/signals.js' - src/tools/registry.ts:499-499 (registration)Handler mapping: maps the 'signals' tool name to the handleSignals function in the TOOL_HANDLERS record used by registerTools().
signals: handleSignals, - src/tools/composite/signals.ts:12-26 (helper)validateParameters helper: validates that signal parameters are strings/numbers and don't contain invalid characters (newlines, quotes, brackets).
function validateParameters(...params: unknown[]) { for (const param of params) { if (typeof param !== 'string' && typeof param !== 'number' && param !== undefined) { throw new GodotMCPError('Invalid parameter type', 'INVALID_ARGS', 'Signal parameters must be strings or numbers.') } const s = String(param) if (s.includes('\n') || s.includes('\r') || s.includes('"') || s.includes(']')) { throw new GodotMCPError( 'Invalid characters in parameters', 'INVALID_ARGS', 'Signal parameters must not contain newlines, double quotes, or closing brackets (]).', ) } } }