import type { ChildProcess } from 'child_process'; /** * Thrown when the bridge socket closes (Godot exited, port closed, or peer * dropped the connection mid-flight). Lets callers distinguish * "session ended" from generic transport errors. */ export declare class BridgeDisconnectedError extends Error { constructor(message: string); } export declare const BRIDGE_WAIT_SPAWNED_TIMEOUT_MS = 8000; /** * Normalize a path for cross-platform comparison. * Folds Windows backslashes to forward slashes and strips trailing slashes, * so Node's `path.normalize` output matches Godot's `globalize_path("res://")`. */ export declare function normalizeForCompare(p: string): string; /** * Extract JSON from Godot output by finding the first { or [ and matching to the end. * This strips debug logs, version banners, and other noise. */ export declare function extractJson(output: string): string; /** * Strip Godot banner and debug lines from output, keeping only meaningful content. */ export declare function cleanOutput(output: string): string; export declare function cleanStdout(stdout: string): string; export interface GodotProcess { process: ChildProcess; output: string[]; errors: string[]; totalErrorsWritten: number; exitCode: number | null; hasExited: boolean; sessionToken: string; } export type RuntimeSessionMode = 'spawned' | 'attached'; export interface RuntimeStopResult { mode: RuntimeSessionMode; output: string[]; errors: string[]; externalProcessPreserved?: boolean; } export interface GodotServerConfig { godotPath?: string; debugMode?: boolean; } export interface OperationParams { [key: string]: unknown; } export interface OperationResult { stdout: string; stderr: string; } export interface ToolAnnotations { readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean; title?: string; } export interface ToolDefinition { name: string; description: string; inputSchema: { type: string; properties: Record; required: string[]; }; outputSchema?: { type: string; properties?: Record; required?: string[]; }; annotations?: ToolAnnotations; } export interface ToolResponse { content: Array<{ type: string; text?: string; [k: string]: unknown; }>; isError?: boolean; structuredContent?: Record; [k: string]: unknown; } export type ToolHandler = (runner: GodotRunner, args: OperationParams) => Promise | ToolResponse; export declare function normalizeParameters(params: OperationParams): OperationParams; export declare function convertCamelToSnakeCase(params: OperationParams): OperationParams; /** * Check whether a display server (X11 / Wayland) is available on the current * platform. On macOS and Windows the display subsystem is always present; * on Linux we probe the standard environment variables. */ export declare function checkDisplayAvailable(): boolean; export declare function validatePath(path: string): boolean; /** * Stricter check for paths that must stay inside `projectPath`. Rejects `..` * (via `validatePath`) and absolute paths that escape the project root. * `path.join('/project', '/etc/passwd')` resolves to `/etc/passwd`, so the * basic `..`-substring check alone permits absolute-path traversal. * * Tolerates a leading `res://` (Godot's project-root URI) by stripping it * before resolving — autoload entries and resource paths use this prefix. */ export declare function validateSubPath(projectPath: string, userPath: string): boolean; /** * Validate a Godot scene-tree path (NodePath). Scene-tree paths are a * separate namespace from filesystem paths — they address nodes inside * a scene, not files on disk, so the project-root containment check * in `validateSubPath` does not apply. * * Rejects empty strings and `..` segments. Accepts both relative * (`root/Player`) and absolute (`/root/Player`) Godot forms; the * codebase convention is the relative form. */ export declare function validateNodePath(path: string): boolean; /** * True when `child` resolves to `parent` or a path beneath it. Used by * defense-in-depth checks on bridge-returned paths (e.g. screenshot files * that must live under `.mcp/screenshots/`). */ export declare function isUnderDir(parent: string, child: string): boolean; /** * Return `error.message` when `error` is an `Error`, otherwise `'Unknown error'`. * Centralizes the catch-block boilerplate so handlers can build error responses * without repeating the `instanceof Error` ternary. */ export declare function getErrorMessage(error: unknown): string; /** * Build the absolute path to a project's `project.godot` manifest. Use this * instead of `join(dir, 'project.godot')` ad hoc. */ export declare function projectGodotPath(projectDir: string): string; /** * Extract the first [ERROR] message from GDScript stderr output. * Falls back to a generic message if no [ERROR] line is found. */ export declare function extractGdError(stderr: string): string; export declare function createErrorResponse(message: string, possibleSolutions?: string[]): { content: Array<{ type: 'text'; text: string; }>; isError: boolean; }; /** * Wrap a structured payload as a ToolResponse that satisfies the MCP * outputSchema contract. The same payload is emitted both as a JSON text * content block (for lenient clients) and as `structuredContent` (required * by strict clients per MCP spec revision 2025-06-18). * * `extraContent` is prepended for handlers that also return non-text blocks * (e.g. `take_screenshot`'s inline image). */ export declare function createStructuredResponse>(payload: T, extraContent?: Array<{ type: string; [k: string]: unknown; }>): ToolResponse; interface ValidatedProjectArgs { projectPath: string; } interface ValidatedSceneArgs { projectPath: string; scenePath: string; } type ValidationErrorResult = ReturnType; export declare function validateProjectArgs(args: OperationParams): ValidatedProjectArgs | ValidationErrorResult; export declare function validateSceneArgs(args: OperationParams, opts?: { sceneRequired?: boolean; }): ValidatedSceneArgs | ValidationErrorResult; export declare class GodotRunner { private godotPath; private operationsScriptPath; private bridge; private validatedPaths; private cachedVersion; activeProcess: GodotProcess | null; activeProjectPath: string | null; activeSessionMode: RuntimeSessionMode | null; activeBridgePort: number | null; private socket; private rxChunks; private rxTotal; private inFlight; constructor(config?: GodotServerConfig); private isValidGodotPathSync; private spawnAsync; private isValidGodotPath; detectGodotPath(): Promise; getGodotPath(): string | null; /** * Read the port currently baked into the project's bridge script. Returns * null if the file is missing or malformed. Thin pass-through to * BridgeManager — used by bridge-wait-timeout race detection. */ readBakedBridgePort(projectPath: string): number | null; getVersion(): Promise; executeOperation(operation: string, params: OperationParams, projectPath: string, timeoutMs?: number): Promise; launchEditor(projectPath: string): ChildProcess; runProject(projectPath: string, scene?: string, background?: boolean, bridgePort?: number): Promise; attachProject(projectPath: string, bridgePort?: number): Promise; stopProject(): Promise; hasActiveRuntimeSession(): boolean; /** * Send a JSON command to the McpBridge over a long-lived TCP connection. * * MCP serializes tool calls so we hold one in-flight command at a time. The * socket is lazy-connected on first call and persists across commands until * `closeConnection` (or a peer-side close). A close mid-flight rejects with * `BridgeDisconnectedError`; a per-command timeout rejects but does NOT * close the socket — a slow command does not invalidate the session. */ sendCommand(command: string, params?: Record, timeoutMs?: number): Promise; /** * Tear down the bridge socket. Idempotent. Any in-flight command is * rejected with a session-ended error. */ closeConnection(): void; private resetRxBuffer; getErrorCount(): number; getErrorsSince(marker: number): string[]; private static readonly SCRIPT_ERROR_PATTERNS; private static readonly RETRYABLE_BRIDGE_COMMANDS; extractRuntimeErrors(lines: string[]): string[]; private sendCommandWithReconnect; sendCommandWithErrors(command: string, params?: Record, timeoutMs?: number): Promise<{ response: string; runtimeErrors: string[]; }>; /** * Shared poll loop for `waitForBridge` (spawned) and `waitForBridgeAttached`. * Sends `ping` payloads until the bridge replies with a pong that * `validatePong` accepts, the deadline passes, or `shouldAbort` reports * the spawned process has exited. */ private pollBridge; waitForBridgeAttached(timeoutMs?: number, intervalMs?: number): Promise<{ ready: boolean; error?: string; }>; waitForBridge(timeoutMs?: number, intervalMs?: number): Promise<{ ready: boolean; error?: string; }>; getRecentErrors(count?: number): string[]; } export {}; //# sourceMappingURL=godot-runner.d.ts.map