get_function_docs
Retrieve official documentation for a specific MTA:SA function or event using its canonical name. Avoid manual web browsing for accurate, cached results.
Instructions
Fetch authoritative docs for exactly ONE MTA:SA function/event by canonical name. Preferred over manual web browsing. For multiple names, use get_multiple_function_docs in one call.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| function_name | Yes | Function/event name (case-insensitive) | |
| use_cache | No | Whether to use cached documentation | |
| include_optional_arguments | No | Whether to include optional arguments in the parameters section |
Implementation Reference
- src/index.ts:305-396 (registration)Registration of the 'get_function_docs' tool with schema and handler callback on the MCP server.
// Register tool: get_function_docs server.registerTool( "get_function_docs", { description: "Fetch authoritative docs for exactly ONE MTA:SA function/event by canonical name. Preferred over manual web browsing. For multiple names, use get_multiple_function_docs in one call.", inputSchema: { function_name: z .string() .describe("Function/event name (case-insensitive)"), use_cache: z .boolean() .optional() .default(true) .describe("Whether to use cached documentation"), include_optional_arguments: z .boolean() .optional() .default(false) .describe( "Whether to include optional arguments in the parameters section", ), }, }, async ({ function_name, use_cache, include_optional_arguments, }): Promise<CallToolResult> => { const normalizedName = normalizeFunctionInput(function_name); if (!normalizedName) { return { content: [ { type: "text", text: "Function name is required.", }, ], }; } const func = findMetadataByName(normalizedName); if (!func) { return { content: [ { type: "text", text: `Function/event "${function_name}" not found in MTA:SA metadata. Use search_functions or search_events to find the correct name.`, }, ], }; } const doc = await fetchFunctionDoc(func.name, use_cache); if (!doc) { return { content: [ { type: "text", text: `Failed to fetch documentation for ${func.name}. The wiki page may not exist or be unavailable.`, }, ], }; } const formatted = formatDocumentation(doc, func, { includeExamples: true, includeOptionalArguments: include_optional_arguments, }); const suggestedRelated = findRelatedFunctions(func.name, 5) .filter((candidate) => candidate.name !== func.name) .slice(0, 5); let relatedHint = ""; if (suggestedRelated.length > 0) { relatedHint = "\n\nSuggested follow-up entries:\n" + formatFunctionList(suggestedRelated) + "\n\nNext: call `get_multiple_function_docs` with the exact names above if you need implementation context."; } return { content: [ { type: "text", text: `${formatted}${relatedHint}`, }, ], }; }, ); - src/index.ts:306-328 (schema)Input schema definition for get_function_docs: function_name (string, required), use_cache (boolean, optional, default true), include_optional_arguments (boolean, optional, default false).
server.registerTool( "get_function_docs", { description: "Fetch authoritative docs for exactly ONE MTA:SA function/event by canonical name. Preferred over manual web browsing. For multiple names, use get_multiple_function_docs in one call.", inputSchema: { function_name: z .string() .describe("Function/event name (case-insensitive)"), use_cache: z .boolean() .optional() .default(true) .describe("Whether to use cached documentation"), include_optional_arguments: z .boolean() .optional() .default(false) .describe( "Whether to include optional arguments in the parameters section", ), }, }, - src/index.ts:329-396 (handler)Handler function for get_function_docs: normalizes function_name, looks up metadata via findMetadataByName, fetches docs via fetchFunctionDoc, formats output via formatDocumentation, and suggests related functions.
async ({ function_name, use_cache, include_optional_arguments, }): Promise<CallToolResult> => { const normalizedName = normalizeFunctionInput(function_name); if (!normalizedName) { return { content: [ { type: "text", text: "Function name is required.", }, ], }; } const func = findMetadataByName(normalizedName); if (!func) { return { content: [ { type: "text", text: `Function/event "${function_name}" not found in MTA:SA metadata. Use search_functions or search_events to find the correct name.`, }, ], }; } const doc = await fetchFunctionDoc(func.name, use_cache); if (!doc) { return { content: [ { type: "text", text: `Failed to fetch documentation for ${func.name}. The wiki page may not exist or be unavailable.`, }, ], }; } const formatted = formatDocumentation(doc, func, { includeExamples: true, includeOptionalArguments: include_optional_arguments, }); const suggestedRelated = findRelatedFunctions(func.name, 5) .filter((candidate) => candidate.name !== func.name) .slice(0, 5); let relatedHint = ""; if (suggestedRelated.length > 0) { relatedHint = "\n\nSuggested follow-up entries:\n" + formatFunctionList(suggestedRelated) + "\n\nNext: call `get_multiple_function_docs` with the exact names above if you need implementation context."; } return { content: [ { type: "text", text: `${formatted}${relatedHint}`, }, ], }; }, ); - src/utils/loader.ts:167-272 (helper)fetchFunctionDoc: fetches wiki documentation for a function by name, with caching (SQLite-backed), HTML fetching from multiple URL variants, parsing, embedding generation, and storage.
export const fetchFunctionDoc = async ( functionName: string, useCache: boolean = true, ): Promise<CachedDoc | null> => { try { const canonicalName = normalizeInputName(functionName); if (canonicalName.length === 0) { throw new Error("Function name is required"); } // Check cache if (useCache) { const cached = queries.getDoc().get(canonicalName) as | CachedDoc | undefined; if (cached && Date.now() - cached.timestamp < CACHE_DURATION) { console.error(`Using cached doc for ${canonicalName}`); return cached; } } // Try multiple wiki URL variants console.error(`Fetching ${canonicalName} from wiki...`); const urlVariants = getUrlVariants(canonicalName); let html: string | null = null; let usedUrl: string | null = null; for (const variant of urlVariants) { const url = `${WIKI_BASE_URL}/${variant}`; try { const response = await fetch(url); if (response.ok) { html = await response.text(); // Use the final redirected URL if available usedUrl = response.url || url; break; } } catch { // ignore and try next } } if (!html || !usedUrl) { throw new Error(`Could not fetch wiki page for ${canonicalName}`); } const docData = parseDocumentation(html, canonicalName, usedUrl); // Extract related functions from wiki const relatedList: string[] = []; const seeAlsoMatch = html.match(/See [Aa]lso[\s\S]*?(?=<h[23]|$)/i); if (seeAlsoMatch) { const links = seeAlsoMatch[0].matchAll(/title="([^"]+)"/g); for (const link of links) { const relatedName = link[1]; if (!relatedName) { continue; } const resolvedName = resolveFunctionName(relatedName); if (resolvedName) { relatedList.push(resolvedName); } } } // Generate embedding from full text const embedding = generateTextEmbedding(docData.full_text); const embeddingBuffer = vectorToBuffer(embedding); const relatedFromParser = docData.related_functions .split(",") .map((name) => resolveFunctionName(normalizeInputName(name))) .filter((name): name is string => typeof name === "string"); const doc: CachedDoc = { ...docData, function_name: canonicalName, related_functions: dedupe([...relatedFromParser, ...relatedList]).join( ", ", ), }; // Store in database queries .insertDoc() .run( doc.function_name, doc.url, doc.description, doc.syntax, doc.examples, doc.parameters, doc.returns, doc.related_functions, doc.full_text, doc.timestamp, embeddingBuffer, doc.deprecated || null, ); return doc; } catch (error) { console.error(`Failed to fetch ${functionName}:`, error); return null; } }; - src/utils/formatter.ts:61-116 (helper)formatDocumentation: formats a CachedDoc + MtasaFunction into a Markdown string with description, syntax, parameters (optionally filtered for optional args), returns, examples, and related functions.
export const formatDocumentation = ( doc: CachedDoc, funcInfo: MtasaFunction, optionsOrIncludeExamples?: boolean | FormatDocumentationOptions, ): string => { const { includeExamples, includeOptionalArguments } = normalizeOptions( optionsOrIncludeExamples, ); let output = `# ${funcInfo.name}\n\n`; output += `**Category:** ${funcInfo.category}\n`; output += `**Side:** ${funcInfo.side}\n`; output += `**URL:** ${doc.url}\n\n`; if (doc.deprecated) { output += `⚠️ **DEPRECATED:** ${doc.deprecated}\n\n`; } if (doc.description) { output += `## Description\n${doc.description}\n\n`; } if (doc.syntax) { output += `## Syntax\n`; const syntaxParts = doc.syntax.split("\n---\n"); syntaxParts.forEach((syntax) => { output += `\`\`\`lua\n${syntax}\n\`\`\`\n\n`; }); } const parameters = includeOptionalArguments ? doc.parameters : filterOptionalArguments(doc.parameters); if (parameters) { output += `## Parameters\n${parameters}\n\n`; } if (doc.returns) { output += `## Returns\n${doc.returns}\n\n`; } if (includeExamples && doc.examples) { output += `## Examples\n\n`; const exampleParts = doc.examples.split("\n---\n"); exampleParts.forEach((example, i) => { output += `### Example ${i + 1}\n\`\`\`lua\n${example}\n\`\`\`\n\n`; }); } if (doc.related_functions) { output += `## Related Functions\n${doc.related_functions}\n\n`; } return output; };