#!/usr/bin/env node /** * Godot MCP Server * * An MCP server that provides Godot game engine tools to AI assistants. * Works with Claude Desktop, Cursor, Codex, or any MCP-compatible client. * * Architecture (connect-or-spawn): * When started, the server probes for an existing primary instance. * - If found → enters PROXY mode (forwards tool calls via HTTP) * - If absent → enters PRIMARY mode (owns Godot bridge + HTTP API) * * Primary mode: * - WebSocket server on port 6505 for Godot plugin communication * - HTTP server on port 6506 for proxy instances * - MCP protocol via stdio for the launching AI client * * Proxy mode: * - MCP protocol via stdio for the launching AI client * - Forwards tool calls to the primary via HTTP */ import type { ToolDefinition } from './types.js'; import { type ToolCallResult } from './primary-http.js'; /** * Toolsets to have on from the very first list_tools, from * GODOT_MCP_TOOLSETS (comma-separated; "all" for everything). * * Why this exists: enabling a toolset mid-session only works if the client * re-fetches list_tools. The server does send notifications/tools/list_changed, * but several clients cache the list for the session and never ask again — and * then the agent is stuck, because `enable_toolset`'s own advice ("call * list_tools again") is a client action an agent cannot perform. Measured on a * real session: after enable_toolset('runtime') the runtime tools stayed * unreachable for the rest of the session. * * Presetting sidesteps the refresh entirely — the tools are simply there. A user * who always drives the running game puts `runtime` here once, in their client * config, and never thinks about it again. */ export declare function initialToolsets(rawEnv?: string): Set; /** * True when a call must be refused for lack of confirmation. * * A tool already in preview mode (dry_run, or gd_rename without apply) is not * blocked — the preview IS the confirmation step, and demanding both would be * noise. delete_file is skipped because it has always enforced its own confirm * gate deeper down; double-reporting would just confuse the caller. * * `requireConfirm` is a parameter rather than a direct env read so the rule is * testable without mutating process.env. */ export declare function needsConfirmation(name: string, args: Record, requireConfirm?: boolean): boolean; /** * Refuse a call that passes an argument the tool does not declare. * * Silently dropping them is how three separate mistakes went wrong in one * session, each surfacing somewhere unrelated: * - `create_animation({player_path})` used the default node and reported * "Node '.' is CharacterBody2D, expected AnimationPlayer". * - `compare_screenshots({image_a, image_b})` reported * "Could not load image: res://__mcp_rejected_path__" — the path guard * complaining about a default, not about the two keys that do not exist. * The agent then debugs the wrong thing. Naming the nearest valid key turns a * confusing chain into one obvious correction. * * Deliberately not a full JSON-Schema validation: types and required fields are * the handler's business and it reports them better. This is only about keys * that will be thrown away. */ export declare function unknownArgumentError(name: string, args: Record, toolLookup?: (n: string) => ToolDefinition | undefined): ToolCallResult | null; /** * The toolset tools, answered by whatever process is serving tools/list. * * A proxy forwarded these to the primary, which flipped its own activeToolsets * while the proxy kept serving the list from the set it started with: the call * answered ok/enabled:true and the tools never appeared for that client. Every * client after the first is a proxy, so that was most of them. Nothing here * touches the editor, and execution is not gated on toolsets, so answering * locally is both correct and enough. * * Returns null when `name` is not one of them. */ export declare function handleToolsetTool(name: string, toolArgs: Record): ToolCallResult | null; //# sourceMappingURL=index.d.ts.map