Skip to main content
Glama

documcp

by tosin2013
generate-contextual-content.ts21.6 kB
/** * Context-Aware Content Generator (Phase 3) * * Generates documentation content based on actual code structure * Uses AST analysis and knowledge graph for accurate, contextual documentation */ import { Tool } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import path from "path"; import { ASTAnalyzer, FunctionSignature, ClassInfo, InterfaceInfo, } from "../utils/ast-analyzer.js"; import { formatMCPResponse, MCPToolResponse } from "../types/api.js"; import { handleMemoryRecall } from "../memory/index.js"; const inputSchema = z.object({ filePath: z.string().describe("Path to the source code file"), documentationType: z .enum(["tutorial", "how-to", "reference", "explanation", "all"]) .default("reference") .describe("Type of documentation to generate"), includeExamples: z .boolean() .default(true) .describe("Include code examples in generated documentation"), style: z .enum(["concise", "detailed", "verbose"]) .default("detailed") .describe("Documentation style"), outputFormat: z .enum(["markdown", "mdx", "html"]) .default("markdown") .describe("Output format for generated documentation"), }); export interface GeneratedContent { filePath: string; documentationType: string; sections: GeneratedSection[]; metadata: ContentMetadata; } export interface GeneratedSection { title: string; content: string; category: "tutorial" | "how-to" | "reference" | "explanation"; codeReferences: string[]; confidence: number; } export interface ContentMetadata { generatedAt: string; codeAnalysis: { functions: number; classes: number; interfaces: number; complexity: number; }; similarExamples: number; confidence: number; } /** * Main content generation handler */ export async function handleGenerateContextualContent( args: unknown, context?: any, ): Promise<{ content: any[] }> { const startTime = Date.now(); try { const { filePath, documentationType, includeExamples, style } = inputSchema.parse(args); await context?.info?.( `📝 Generating ${documentationType} documentation for ${path.basename( filePath, )}...`, ); // Initialize AST analyzer const analyzer = new ASTAnalyzer(); await analyzer.initialize(); // Analyze the file await context?.info?.("🔍 Analyzing code structure..."); const analysis = await analyzer.analyzeFile(filePath); if (!analysis) { throw new Error(`Failed to analyze file: ${filePath}`); } // Query knowledge graph for similar projects await context?.info?.("🧠 Retrieving contextual information..."); const similarProjects = await findSimilarProjects(analysis, context); // Generate documentation sections const sections: GeneratedSection[] = []; if (documentationType === "reference" || documentationType === "all") { sections.push( ...generateReferenceDocumentation(analysis, similarProjects, style), ); } if (documentationType === "tutorial" || documentationType === "all") { sections.push( ...generateTutorialDocumentation( analysis, similarProjects, includeExamples, style, ), ); } if (documentationType === "how-to" || documentationType === "all") { sections.push( ...generateHowToDocumentation( analysis, similarProjects, includeExamples, style, ), ); } if (documentationType === "explanation" || documentationType === "all") { sections.push( ...generateExplanationDocumentation(analysis, similarProjects, style), ); } const metadata: ContentMetadata = { generatedAt: new Date().toISOString(), codeAnalysis: { functions: analysis.functions.length, classes: analysis.classes.length, interfaces: analysis.interfaces.length, complexity: analysis.complexity, }, similarExamples: similarProjects.length, confidence: calculateOverallConfidence(sections), }; const result: GeneratedContent = { filePath, documentationType, sections, metadata, }; const response: MCPToolResponse<typeof result> = { success: true, data: result, metadata: { toolVersion: "3.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, recommendations: [ { type: "info", title: "Documentation Generated", description: `Generated ${sections.length} documentation section(s) with ${metadata.confidence}% confidence`, }, ], nextSteps: [ { action: "Review generated content", description: "Review and refine generated documentation for accuracy", priority: "high", }, { action: "Add to documentation site", description: "Integrate generated content into your documentation structure", priority: "medium", }, { action: "Validate content", toolRequired: "validate_diataxis_content", description: "Run validation to ensure generated content meets quality standards", priority: "medium", }, ], }; await context?.info?.( `✅ Generated ${sections.length} documentation section(s)`, ); return formatMCPResponse(response, { fullResponse: true }); } catch (error: any) { const errorResponse: MCPToolResponse = { success: false, error: { code: "GENERATION_FAILED", message: `Content generation failed: ${error.message}`, resolution: "Ensure the file path is valid and the file can be parsed", }, metadata: { toolVersion: "3.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; return formatMCPResponse(errorResponse, { fullResponse: true }); } } /** * Generate reference documentation */ function generateReferenceDocumentation( analysis: any, _similarProjects: any[], _style: string, ): GeneratedSection[] { const sections: GeneratedSection[] = []; // Generate function reference if (analysis.functions.length > 0) { sections.push(generateFunctionReference(analysis.functions, _style)); } // Generate class reference if (analysis.classes.length > 0) { sections.push(generateClassReference(analysis.classes, _style)); } // Generate interface reference if (analysis.interfaces.length > 0) { sections.push(generateInterfaceReference(analysis.interfaces, _style)); } // Generate type reference if (analysis.types.length > 0) { sections.push(generateTypeReference(analysis.types, _style)); } return sections; } /** * Generate function reference documentation */ function generateFunctionReference( functions: FunctionSignature[], _style: string, ): GeneratedSection { let content = "# Function Reference\n\n"; for (const func of functions.filter((f) => f.isExported)) { content += `## \`${func.name}\`\n\n`; if (func.docComment) { content += `${cleanDocComment(func.docComment)}\n\n`; } // Signature const params = func.parameters .map((p) => `${p.name}: ${p.type || "any"}`) .join(", "); const returnType = func.returnType || "void"; const asyncPrefix = func.isAsync ? "async " : ""; content += "**Signature:**\n\n"; content += "```typescript\n"; content += `${asyncPrefix}function ${func.name}(${params}): ${returnType}\n`; content += "```\n\n"; // Parameters if (func.parameters.length > 0) { content += "**Parameters:**\n\n"; for (const param of func.parameters) { const optionalMarker = param.optional ? " (optional)" : ""; const defaultValue = param.defaultValue ? ` = ${param.defaultValue}` : ""; content += `- \`${param.name}\`${optionalMarker}: \`${ param.type || "any" }\`${defaultValue}\n`; } content += "\n"; } // Return value if (func.returnType && func.returnType !== "void") { content += "**Returns:**\n\n"; content += `- \`${func.returnType}\`\n\n`; } if (_style === "detailed" || _style === "verbose") { content += `**Complexity:** ${func.complexity}\n\n`; } content += "---\n\n"; } return { title: "Function Reference", content, category: "reference", codeReferences: functions.map((f) => f.name), confidence: 0.9, }; } /** * Generate class reference documentation */ function generateClassReference( classes: ClassInfo[], _style: string, ): GeneratedSection { let content = "# Class Reference\n\n"; for (const cls of classes.filter((c) => c.isExported)) { content += `## \`${cls.name}\`\n\n`; if (cls.docComment) { content += `${cleanDocComment(cls.docComment)}\n\n`; } // Inheritance if (cls.extends) { content += `**Extends:** \`${cls.extends}\`\n\n`; } if (cls.implements.length > 0) { content += `**Implements:** ${cls.implements .map((i) => `\`${i}\``) .join(", ")}\n\n`; } // Properties if (cls.properties.length > 0) { content += "### Properties\n\n"; for (const prop of cls.properties) { const visibility = prop.visibility !== "public" ? `${prop.visibility} ` : ""; const readonly = prop.isReadonly ? "readonly " : ""; const static_ = prop.isStatic ? "static " : ""; content += `- ${visibility}${static_}${readonly}\`${prop.name}\`: \`${ prop.type || "any" }\`\n`; } content += "\n"; } // Methods if (cls.methods.length > 0) { content += "### Methods\n\n"; for (const method of cls.methods.filter((m) => m.isPublic)) { const params = method.parameters .map((p) => `${p.name}: ${p.type || "any"}`) .join(", "); const returnType = method.returnType || "void"; const asyncPrefix = method.isAsync ? "async " : ""; content += `#### \`${method.name}\`\n\n`; if (method.docComment) { content += `${cleanDocComment(method.docComment)}\n\n`; } content += "```typescript\n"; content += `${asyncPrefix}${method.name}(${params}): ${returnType}\n`; content += "```\n\n"; } } content += "---\n\n"; } return { title: "Class Reference", content, category: "reference", codeReferences: classes.map((c) => c.name), confidence: 0.9, }; } /** * Generate interface reference documentation */ function generateInterfaceReference( interfaces: InterfaceInfo[], _style: string, ): GeneratedSection { let content = "# Interface Reference\n\n"; for (const iface of interfaces.filter((i) => i.isExported)) { content += `## \`${iface.name}\`\n\n`; if (iface.docComment) { content += `${cleanDocComment(iface.docComment)}\n\n`; } if (iface.extends.length > 0) { content += `**Extends:** ${iface.extends .map((e) => `\`${e}\``) .join(", ")}\n\n`; } // Properties if (iface.properties.length > 0) { content += "### Properties\n\n"; content += "```typescript\n"; content += `interface ${iface.name} {\n`; for (const prop of iface.properties) { const readonly = prop.isReadonly ? "readonly " : ""; content += ` ${readonly}${prop.name}: ${prop.type || "any"};\n`; } content += "}\n"; content += "```\n\n"; } // Methods if (iface.methods.length > 0) { content += "### Methods\n\n"; for (const method of iface.methods) { const params = method.parameters .map((p) => `${p.name}: ${p.type || "any"}`) .join(", "); const returnType = method.returnType || "void"; content += `- \`${method.name}(${params}): ${returnType}\`\n`; } content += "\n"; } content += "---\n\n"; } return { title: "Interface Reference", content, category: "reference", codeReferences: interfaces.map((i) => i.name), confidence: 0.9, }; } /** * Generate type reference documentation */ function generateTypeReference(types: any[], _style: string): GeneratedSection { let content = "# Type Reference\n\n"; for (const type of types.filter((t: any) => t.isExported)) { content += `## \`${type.name}\`\n\n`; if (type.docComment) { content += `${cleanDocComment(type.docComment)}\n\n`; } content += "```typescript\n"; content += `type ${type.name} = ${type.definition};\n`; content += "```\n\n"; content += "---\n\n"; } return { title: "Type Reference", content, category: "reference", codeReferences: types.map((t: any) => t.name), confidence: 0.85, }; } /** * Generate tutorial documentation */ function generateTutorialDocumentation( analysis: any, _similarProjects: any[], includeExamples: boolean, _style: string, ): GeneratedSection[] { const sections: GeneratedSection[] = []; // Generate getting started tutorial const tutorialContent = generateGettingStartedTutorial( analysis, includeExamples, ); sections.push(tutorialContent); return sections; } /** * Generate getting started tutorial */ function generateGettingStartedTutorial( analysis: any, includeExamples: boolean, ): GeneratedSection { let content = "# Getting Started\n\n"; content += "This tutorial will guide you through using this module.\n\n"; content += "## Installation\n\n"; content += "```bash\n"; content += "npm install your-package\n"; content += "```\n\n"; content += "## Basic Usage\n\n"; if (includeExamples && analysis.functions.length > 0) { const mainFunction = analysis.functions.find((f: any) => f.name === "main") || analysis.functions[0]; content += `Import and use the main functions:\n\n`; content += "```typescript\n"; content += `import { ${mainFunction.name} } from 'your-package';\n\n`; const exampleParams = mainFunction.parameters .map((p: any) => { if (p.type === "string") return `"example"`; if (p.type === "number") return "42"; if (p.type === "boolean") return "true"; return "{}"; }) .join(", "); content += `// Example usage\n`; content += `const result = ${mainFunction.isAsync ? "await " : ""}${ mainFunction.name }(${exampleParams});\n`; content += "console.log(result);\n"; content += "```\n\n"; } content += "## Next Steps\n\n"; content += "- Explore the [API Reference](#reference) for detailed documentation\n"; content += "- Check out [How-To Guides](#how-to) for specific use cases\n"; content += "- Read the [Explanation](#explanation) for deeper understanding\n\n"; return { title: "Getting Started Tutorial", content, category: "tutorial", codeReferences: analysis.functions.map((f: any) => f.name), confidence: 0.75, }; } /** * Generate how-to documentation */ function generateHowToDocumentation( analysis: any, _similarProjects: any[], includeExamples: boolean, _style: string, ): GeneratedSection[] { const sections: GeneratedSection[] = []; // Generate how-to guides based on common patterns if (analysis.functions.some((f: any) => f.isAsync)) { sections.push(generateAsyncHowTo(analysis, includeExamples)); } if (analysis.classes.length > 0) { sections.push(generateClassUsageHowTo(analysis, includeExamples)); } return sections; } /** * Generate async usage how-to */ function generateAsyncHowTo( analysis: any, includeExamples: boolean, ): GeneratedSection { let content = "# How to Handle Async Operations\n\n"; content += "This module uses async/await for asynchronous operations.\n\n"; if (includeExamples) { const asyncFunc = analysis.functions.find((f: any) => f.isAsync); if (asyncFunc) { content += "## Example\n\n"; content += "```typescript\n"; content += `try {\n`; content += ` const result = await ${asyncFunc.name}();\n`; content += ` console.log('Success:', result);\n`; content += `} catch (error) {\n`; content += ` console.error('Error:', error);\n`; content += `}\n`; content += "```\n\n"; } } return { title: "Async Operations Guide", content, category: "how-to", codeReferences: analysis.functions .filter((f: any) => f.isAsync) .map((f: any) => f.name), confidence: 0.8, }; } /** * Generate class usage how-to */ function generateClassUsageHowTo( analysis: any, includeExamples: boolean, ): GeneratedSection { let content = "# How to Use Classes\n\n"; const firstClass = analysis.classes[0]; if (firstClass && includeExamples) { content += `## Creating an Instance\n\n`; content += "```typescript\n"; content += `const instance = new ${firstClass.name}();\n`; content += "```\n\n"; if (firstClass.methods.length > 0) { content += `## Using Methods\n\n`; content += "```typescript\n"; const publicMethod = firstClass.methods.find((m: any) => m.isPublic); if (publicMethod) { content += `const result = ${ publicMethod.isAsync ? "await " : "" }instance.${publicMethod.name}();\n`; } content += "```\n\n"; } } return { title: "Class Usage Guide", content, category: "how-to", codeReferences: analysis.classes.map((c: any) => c.name), confidence: 0.8, }; } /** * Generate explanation documentation */ function generateExplanationDocumentation( analysis: any, _similarProjects: any[], _style: string, ): GeneratedSection[] { const sections: GeneratedSection[] = []; // Generate architecture explanation sections.push(generateArchitectureExplanation(analysis)); return sections; } /** * Generate architecture explanation */ function generateArchitectureExplanation(analysis: any): GeneratedSection { let content = "# Architecture\n\n"; content += "## Overview\n\n"; content += `This module consists of ${analysis.functions.length} function(s), ${analysis.classes.length} class(es), and ${analysis.interfaces.length} interface(s).\n\n`; if (analysis.classes.length > 0) { content += "## Class Structure\n\n"; content += "The module uses object-oriented patterns with the following classes:\n\n"; for (const cls of analysis.classes.filter((c: any) => c.isExported)) { content += `- **${cls.name}**: ${cls.methods.length} method(s), ${cls.properties.length} property(ies)\n`; } content += "\n"; } if (analysis.complexity > 20) { content += "## Complexity\n\n"; content += `This module has a moderate to high complexity score (${analysis.complexity}), indicating sophisticated logic and multiple control flow paths.\n\n`; } return { title: "Architecture Explanation", content, category: "explanation", codeReferences: [ ...analysis.functions.map((f: any) => f.name), ...analysis.classes.map((c: any) => c.name), ], confidence: 0.7, }; } /** * Find similar projects in knowledge graph */ async function findSimilarProjects( analysis: any, context?: any, ): Promise<any[]> { try { const query = `${analysis.language} ${analysis.functions.length} functions ${analysis.classes.length} classes`; const results = await handleMemoryRecall({ query, type: "analysis", limit: 5, }); return results.memories || []; } catch (error) { await context?.warn?.(`Failed to retrieve similar projects: ${error}`); return []; } } /** * Calculate overall confidence */ function calculateOverallConfidence(sections: GeneratedSection[]): number { if (sections.length === 0) return 0; const avgConfidence = sections.reduce((sum, s) => sum + s.confidence, 0) / sections.length; return Math.round(avgConfidence * 100); } /** * Clean JSDoc comment */ function cleanDocComment(comment: string): string { return comment .replace(/\/\*\*|\*\//g, "") .replace(/^\s*\* ?/gm, "") .trim(); } /** * Tool definition */ export const generateContextualContent: Tool = { name: "generate_contextual_content", description: "Generate context-aware documentation using AST analysis and knowledge graph insights (Phase 3)", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Path to the source code file to document", }, documentationType: { type: "string", enum: ["tutorial", "how-to", "reference", "explanation", "all"], default: "reference", description: "Type of Diataxis documentation to generate", }, includeExamples: { type: "boolean", default: true, description: "Include code examples in generated documentation", }, style: { type: "string", enum: ["concise", "detailed", "verbose"], default: "detailed", description: "Documentation detail level", }, outputFormat: { type: "string", enum: ["markdown", "mdx", "html"], default: "markdown", description: "Output format for generated content", }, }, required: ["filePath"], }, };

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/tosin2013/documcp'

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