Skip to main content
Glama

signals

Destructive

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

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform
project_pathNoPath to Godot project directory
scene_pathNoPath to scene file
signalNoSignal name
fromNoSource node path
toNoTarget node path
methodNoTarget method name
flagsNoConnection flags

Implementation Reference

  • 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'])
      }
    }
  • 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'],
      },
    },
  • Import registration: imports handleSignals from './composite/signals.js'
    import { handleSignals } from './composite/signals.js'
  • Handler mapping: maps the 'signals' tool name to the handleSignals function in the TOOL_HANDLERS record used by registerTools().
    signals: handleSignals,
  • 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 (]).',
          )
        }
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description lists actions that imply mutation (connect/disconnect), which aligns with the destructiveHint annotation. However, it does not add behavioral details beyond the actions themselves, such as error handling, permission requirements, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, using a structured list format that is easy to parse. Every sentence adds meaningful information, and the format front-loads the key actions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with multiple actions and parameters, the description is adequate. It covers the main functionality and parameter usage. However, without an output schema, some context about return values or error conditions could be helpful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already provides 100% description coverage for all 8 parameters. The description adds value by explaining which parameters are required versus optional for each action, going beyond the schema's flat listing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool manages signal connections and lists three specific actions (list, connect, disconnect) with their parameters. It distinguishes itself from siblings by focusing exclusively on signals, which is a focused domain.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a minimal usage guide by showing required parameters for each action, but it does not explicitly tell the agent when to use this tool versus alternatives or when not to use it. There are no exclusions or context about prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/n24q02m/better-godot-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server