godot_search_docs
Search Godot 4.x API documentation to find class overviews, method details, and relevant information. Automatically detects Godot 3 API queries and suggests Godot 4 equivalents.
Instructions
Search Godot 4.x API documentation. Returns class overviews, method details, or fuzzy search results. Automatically detects Godot 3 API queries and suggests Godot 4 equivalents — the #1 source of AI-generated GDScript bugs.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Class name (e.g., 'CharacterBody2D'), method (e.g., 'CharacterBody2D.move_and_slide'), or search term |
Implementation Reference
- src/tools/docs-search.ts:93-197 (handler)Main handler function that executes the godot_search_docs tool logic. Handles migration mapping checks, class.method queries, exact class lookups, fuzzy search, and fallback responses with proper formatting.
async (args) => { const { query } = args; // Check migration mapping first — headline feature const migration = checkMigration(query); // Check if this is a class.method query const dotIndex = query.indexOf("."); if (dotIndex !== -1) { const className = query.substring(0, dotIndex); const methodName = query.substring(dotIndex + 1); const classDoc = docsIndex.get(className); if (classDoc) { const method = classDoc.methods.find((m) => m.name === methodName); if (method) { let text = `## ${className}.${method.name}\n\n`; text += `**Signature:** ${method.signature}\n\n`; text += method.description; if (migration) text = migration + "\n\n---\n\n" + text; return { content: [{ type: "text", text }] }; } } } // Check for exact class match const classDoc = docsIndex.get(query); if (classDoc) { let text = `## ${classDoc.name}\n\n`; text += `**Inherits:** ${classDoc.inherits}\n\n`; text += classDoc.description + "\n\n"; text += `### Properties (${classDoc.properties.length})\n`; for (const p of classDoc.properties.slice(0, 20)) { text += `- \`${p.name}: ${p.type}\`\n`; } if (classDoc.properties.length > 20) { text += `- ... and ${classDoc.properties.length - 20} more\n`; } text += `\n### Methods (${classDoc.methods.length})\n`; for (const m of classDoc.methods.slice(0, 20)) { text += `- \`${m.signature}\`\n`; } if (classDoc.methods.length > 20) { text += `- ... and ${classDoc.methods.length - 20} more\n`; } if (classDoc.signals.length > 0) { text += `\n### Signals (${classDoc.signals.length})\n`; for (const s of classDoc.signals) { text += `- \`${s.name}\`\n`; } } if (migration) text = migration + "\n\n---\n\n" + text; return { content: [{ type: "text", text }] }; } // If we only have a migration hint and no docs match if (migration) { return { content: [{ type: "text", text: migration }] }; } // Fuzzy search across all classes/methods const results: string[] = []; for (const [, doc] of docsIndex) { if (doc.name.toLowerCase().includes(query.toLowerCase())) { results.push(`- **${doc.name}** — ${doc.description.substring(0, 100)}...`); } for (const m of doc.methods) { if (m.name.toLowerCase().includes(query.toLowerCase())) { results.push(`- **${doc.name}.${m.name}** — ${m.signature}`); } } } if (results.length > 0) { const text = `## Search results for "${query}"\n\n${results.slice(0, 20).join("\n")}`; return { content: [{ type: "text", text }] }; } // Check migration map as last resort for partial matches const partialMigrations: string[] = []; for (const [key, val] of Object.entries(MIGRATION_MAP)) { if (key.toLowerCase().includes(query.toLowerCase())) { partialMigrations.push(`- **${key}** → ${val.replacement}: ${val.note}`); } } if (partialMigrations.length > 0) { return { content: [ { type: "text", text: `No Godot 4 API matches for "${query}". Did you mean a Godot 3 API?\n\n${partialMigrations.join("\n")}`, }, ], }; } return { content: [ { type: "text", text: `No results found for "${query}". Note: Full API docs will be available after running the docs build script. The Godot 3→4 migration mapping is available immediately.`, }, ], }; } - src/tools/docs-search.ts:86-91 (schema)Zod schema definition for the tool input - accepts a 'query' string parameter that can be a class name, method reference (e.g., 'CharacterBody2D.move_and_slide'), or search term.
query: z .string() .describe( "Class name (e.g., 'CharacterBody2D'), method (e.g., 'CharacterBody2D.move_and_slide'), or search term" ), }, - src/tools/docs-search.ts:81-199 (registration)Registration function that registers the godot_search_docs tool with the MCP server, including tool name, description, input schema, hints, and async handler function.
export function registerDocsSearch(server: McpServer, _ctx: ServerContext): void { server.tool( "godot_search_docs", "Search Godot 4.x API documentation. Returns class overviews, method details, or fuzzy search results. Automatically detects Godot 3 API queries and suggests Godot 4 equivalents — the #1 source of AI-generated GDScript bugs.", { query: z .string() .describe( "Class name (e.g., 'CharacterBody2D'), method (e.g., 'CharacterBody2D.move_and_slide'), or search term" ), }, { readOnlyHint: true, idempotentHint: true, openWorldHint: false }, async (args) => { const { query } = args; // Check migration mapping first — headline feature const migration = checkMigration(query); // Check if this is a class.method query const dotIndex = query.indexOf("."); if (dotIndex !== -1) { const className = query.substring(0, dotIndex); const methodName = query.substring(dotIndex + 1); const classDoc = docsIndex.get(className); if (classDoc) { const method = classDoc.methods.find((m) => m.name === methodName); if (method) { let text = `## ${className}.${method.name}\n\n`; text += `**Signature:** ${method.signature}\n\n`; text += method.description; if (migration) text = migration + "\n\n---\n\n" + text; return { content: [{ type: "text", text }] }; } } } // Check for exact class match const classDoc = docsIndex.get(query); if (classDoc) { let text = `## ${classDoc.name}\n\n`; text += `**Inherits:** ${classDoc.inherits}\n\n`; text += classDoc.description + "\n\n"; text += `### Properties (${classDoc.properties.length})\n`; for (const p of classDoc.properties.slice(0, 20)) { text += `- \`${p.name}: ${p.type}\`\n`; } if (classDoc.properties.length > 20) { text += `- ... and ${classDoc.properties.length - 20} more\n`; } text += `\n### Methods (${classDoc.methods.length})\n`; for (const m of classDoc.methods.slice(0, 20)) { text += `- \`${m.signature}\`\n`; } if (classDoc.methods.length > 20) { text += `- ... and ${classDoc.methods.length - 20} more\n`; } if (classDoc.signals.length > 0) { text += `\n### Signals (${classDoc.signals.length})\n`; for (const s of classDoc.signals) { text += `- \`${s.name}\`\n`; } } if (migration) text = migration + "\n\n---\n\n" + text; return { content: [{ type: "text", text }] }; } // If we only have a migration hint and no docs match if (migration) { return { content: [{ type: "text", text: migration }] }; } // Fuzzy search across all classes/methods const results: string[] = []; for (const [, doc] of docsIndex) { if (doc.name.toLowerCase().includes(query.toLowerCase())) { results.push(`- **${doc.name}** — ${doc.description.substring(0, 100)}...`); } for (const m of doc.methods) { if (m.name.toLowerCase().includes(query.toLowerCase())) { results.push(`- **${doc.name}.${m.name}** — ${m.signature}`); } } } if (results.length > 0) { const text = `## Search results for "${query}"\n\n${results.slice(0, 20).join("\n")}`; return { content: [{ type: "text", text }] }; } // Check migration map as last resort for partial matches const partialMigrations: string[] = []; for (const [key, val] of Object.entries(MIGRATION_MAP)) { if (key.toLowerCase().includes(query.toLowerCase())) { partialMigrations.push(`- **${key}** → ${val.replacement}: ${val.note}`); } } if (partialMigrations.length > 0) { return { content: [ { type: "text", text: `No Godot 4 API matches for "${query}". Did you mean a Godot 3 API?\n\n${partialMigrations.join("\n")}`, }, ], }; } return { content: [ { type: "text", text: `No results found for "${query}". Note: Full API docs will be available after running the docs build script. The Godot 3→4 migration mapping is available immediately.`, }, ], }; } ); } - src/tools/docs-search.ts:63-79 (helper)Helper function that checks if a query matches a Godot 3 API term and returns migration guidance to the Godot 4 equivalent, supporting both exact and case-insensitive matching.
function checkMigration(query: string): string | null { // Direct match if (MIGRATION_MAP[query]) { const m = MIGRATION_MAP[query]; return `⚠️ Godot 3 API: ${m.note}\nGodot 4 equivalent: ${m.replacement}`; } // Case-insensitive check const lower = query.toLowerCase(); for (const [key, val] of Object.entries(MIGRATION_MAP)) { if (key.toLowerCase() === lower) { return `⚠️ Godot 3 API: ${val.note}\nGodot 4 equivalent: ${val.replacement}`; } } return null; } - src/tools/docs-search.ts:9-48 (helper)Migration map data structure containing Godot 3 to Godot 4 API mappings for classes, methods, syntax changes, and constants. Used by the handler to detect and suggest replacements for deprecated APIs.
const MIGRATION_MAP: Record<string, { replacement: string; note: string }> = { // Classes KinematicBody: { replacement: "CharacterBody3D", note: "KinematicBody was renamed to CharacterBody3D in Godot 4" }, KinematicBody2D: { replacement: "CharacterBody2D", note: "KinematicBody2D was renamed to CharacterBody2D in Godot 4" }, Spatial: { replacement: "Node3D", note: "Spatial was renamed to Node3D in Godot 4" }, Area: { replacement: "Area3D", note: "Area was renamed to Area3D in Godot 4" }, RigidBody: { replacement: "RigidBody3D", note: "RigidBody was renamed to RigidBody3D in Godot 4" }, StaticBody: { replacement: "StaticBody3D", note: "StaticBody was renamed to StaticBody3D in Godot 4" }, CollisionShape: { replacement: "CollisionShape3D", note: "CollisionShape was renamed to CollisionShape3D in Godot 4" }, MeshInstance: { replacement: "MeshInstance3D", note: "MeshInstance was renamed to MeshInstance3D in Godot 4" }, Camera: { replacement: "Camera3D", note: "Camera was renamed to Camera3D in Godot 4" }, Light: { replacement: "Light3D", note: "Light was renamed to Light3D in Godot 4" }, DirectionalLight: { replacement: "DirectionalLight3D", note: "DirectionalLight was renamed to DirectionalLight3D in Godot 4" }, OmniLight: { replacement: "OmniLight3D", note: "OmniLight was renamed to OmniLight3D in Godot 4" }, SpotLight: { replacement: "SpotLight3D", note: "SpotLight was renamed to SpotLight3D in Godot 4" }, RayCast: { replacement: "RayCast3D", note: "RayCast was renamed to RayCast3D in Godot 4" }, Particles: { replacement: "GPUParticles3D", note: "Particles was renamed to GPUParticles3D in Godot 4" }, Particles2D: { replacement: "GPUParticles2D", note: "Particles2D was renamed to GPUParticles2D in Godot 4" }, // Methods & functions yield: { replacement: "await", note: "yield(obj, 'signal') → await obj.signal" }, instance: { replacement: "instantiate()", note: "instance() was renamed to instantiate() in Godot 4" }, rand_range: { replacement: "randf_range() / randi_range()", note: "rand_range() was split into randf_range() and randi_range() in Godot 4" }, deg2rad: { replacement: "deg_to_rad()", note: "deg2rad() was renamed to deg_to_rad() in Godot 4" }, rad2deg: { replacement: "rad_to_deg()", note: "rad2deg() was renamed to rad_to_deg() in Godot 4" }, stepify: { replacement: "snapped()", note: "stepify() was renamed to snapped() in Godot 4" }, str2var: { replacement: "str_to_var()", note: "str2var() was renamed to str_to_var() in Godot 4" }, var2str: { replacement: "var_to_str()", note: "var2str() was renamed to var_to_str() in Godot 4" }, range_lerp: { replacement: "remap()", note: "range_lerp() was renamed to remap() in Godot 4" }, // Syntax "export var": { replacement: "@export var", note: "export var → @export var (annotation syntax in Godot 4)" }, "onready var": { replacement: "@onready var", note: "onready var → @onready var (annotation syntax in Godot 4)" }, tool: { replacement: "@tool", note: "tool → @tool (annotation syntax in Godot 4)" }, // Constants BUTTON_LEFT: { replacement: "MOUSE_BUTTON_LEFT", note: "BUTTON_LEFT was renamed to MOUSE_BUTTON_LEFT in Godot 4" }, BUTTON_RIGHT: { replacement: "MOUSE_BUTTON_RIGHT", note: "BUTTON_RIGHT was renamed to MOUSE_BUTTON_RIGHT in Godot 4" }, BUTTON_MIDDLE: { replacement: "MOUSE_BUTTON_MIDDLE", note: "BUTTON_MIDDLE was renamed to MOUSE_BUTTON_MIDDLE in Godot 4" }, };