detect_documentation_gaps
Analyze code repositories to identify missing documentation content and gaps in existing documentation.
Instructions
Analyze repository and existing documentation to identify missing content and gaps
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| repositoryPath | Yes | Path to the repository to analyze | |
| documentationPath | No | Path to existing documentation (if any) | |
| analysisId | No | Optional existing analysis ID to reuse | |
| depth | No | standard |
Implementation Reference
- src/tools/detect-gaps.ts:65-225 (handler)Primary handler function for the detect_documentation_gaps tool. Orchestrates analysis from multiple sources (analyzeRepository, CodeScanner, validateDiataxisContent) to detect gaps in documentation structure, content coverage, API endpoints, classes, functions, and framework-specific docs.export async function detectDocumentationGaps( args: unknown, ): Promise<{ content: any[] }> { const startTime = Date.now(); const { repositoryPath, documentationPath, analysisId: existingAnalysisId, depth, } = inputSchema.parse(args); try { // Step 1: Get or perform repository analysis let analysisId = existingAnalysisId; let repositoryAnalysis: any; if (!analysisId) { const analysisResult = await analyzeRepository({ path: repositoryPath, depth, }); if (analysisResult.content && analysisResult.content[0]) { // The analyze_repository tool returns the analysis data directly as JSON text repositoryAnalysis = JSON.parse(analysisResult.content[0].text); // Check if the analysis was successful if (repositoryAnalysis.success === false) { throw new Error("Repository analysis failed"); } analysisId = repositoryAnalysis.id; // Use the 'id' field from the analysis } else { throw new Error("Repository analysis failed - no content returned"); } } else { // Try to retrieve existing analysis (simplified for this implementation) // In a full implementation, this would retrieve from persistent storage } // Step 2: Perform deep code analysis const codeScanner = new CodeScanner(repositoryPath); const codeAnalysis = await codeScanner.analyzeRepository(); // Step 3: Analyze existing documentation structure const documentationAnalysis = await analyzeExistingDocumentation( documentationPath || path.join(repositoryPath, "docs"), ); // Step 4: Perform content validation if documentation exists let validationResult: any = null; if (documentationAnalysis.exists && documentationPath) { try { const validation = await handleValidateDiataxisContent({ contentPath: documentationPath, analysisId: analysisId, validationType: "all", includeCodeValidation: true, confidence: "moderate", }); if ( validation && (validation as any).content && (validation as any).content[0] ) { const validationData = JSON.parse( (validation as any).content[0].text, ); if (validationData.success) { validationResult = validationData.data; } } } catch (error) { // Validation errors are non-fatal - continue without validation data console.warn( "Content validation failed, continuing without validation data:", error, ); } } // Step 5: Identify gaps based on project and code analysis const gaps = identifyDocumentationGaps( repositoryAnalysis, documentationAnalysis, validationResult, codeAnalysis, ); // Step 6: Generate recommendations const recommendations = generateRecommendations( gaps, repositoryAnalysis, codeAnalysis, ); // Step 7: Calculate coverage scores const contentCoverage = calculateContentCoverage( documentationAnalysis, gaps, ); const gapAnalysis: GapAnalysisResult = { repositoryPath, documentationPath, analysisId: analysisId || "unknown", overallScore: calculateOverallScore(gaps, contentCoverage), gaps, strengths: identifyStrengths(documentationAnalysis, validationResult), recommendations, missingStructure: identifyMissingStructure(documentationAnalysis), contentCoverage, }; const response: MCPToolResponse<typeof gapAnalysis> = { success: true, data: gapAnalysis, metadata: { toolVersion: "1.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, recommendations: [ { type: gapAnalysis.overallScore < 60 ? "critical" : gapAnalysis.overallScore < 80 ? "warning" : "info", title: "Documentation Gap Analysis Complete", description: `Found ${gaps.length} gaps. Overall documentation score: ${gapAnalysis.overallScore}%`, }, ], nextSteps: recommendations.immediate.map((rec) => ({ action: rec, toolRequired: getRecommendedTool(rec), description: rec, priority: "high" as const, })), }; return formatMCPResponse(response); } catch (error) { const errorResponse: MCPToolResponse = { success: false, error: { code: "GAP_DETECTION_FAILED", message: `Failed to detect documentation gaps: ${error}`, resolution: "Ensure repository and documentation paths are accessible", }, metadata: { toolVersion: "1.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; return formatMCPResponse(errorResponse); } }
- src/tools/detect-gaps.ts:9-23 (schema)Zod input schema defining parameters for the tool: repositoryPath (required), documentationPath (optional), analysisId (optional), depth (enum with default).const inputSchema = z.object({ repositoryPath: z.string().describe("Path to the repository to analyze"), documentationPath: z .string() .optional() .describe("Path to existing documentation (if any)"), analysisId: z .string() .optional() .describe("Optional existing analysis ID to reuse"), depth: z .enum(["quick", "standard", "comprehensive"]) .optional() .default("standard"), });
- src/tools/detect-gaps.ts:281-529 (helper)Key helper function that implements the gap detection logic based on code analysis, documentation structure, and validation results. Identifies missing sections, undocumented code elements, and framework-specific gaps.function identifyDocumentationGaps( repoAnalysis: any, docsAnalysis: any, validationResult: any, codeAnalysis: CodeAnalysisResult, ): DocumentationGap[] { const gaps: DocumentationGap[] = []; // Check for missing Diataxis structure if (!docsAnalysis.exists) { gaps.push({ category: "general", gapType: "missing_section", description: "No documentation directory found", priority: "critical", recommendation: "Create documentation structure using setup_structure tool", estimatedEffort: "moderate", }); return gaps; // If no docs exist, return early } const diataxisCategories = [ "tutorials", "how-to", "reference", "explanation", ]; for (const category of diataxisCategories) { if (!docsAnalysis.structure[category]?.exists) { gaps.push({ category: category as any, gapType: "missing_section", description: `Missing ${category} documentation section`, priority: category === "tutorials" || category === "reference" ? "high" : "medium", recommendation: `Create ${category} directory and add relevant content`, estimatedEffort: "moderate", }); } else if (docsAnalysis.structure[category].files.length === 0) { gaps.push({ category: category as any, gapType: "incomplete_content", description: `${category} section exists but has no content`, priority: "high", recommendation: `Add content to ${category} section using populate_diataxis_content tool`, estimatedEffort: "substantial", }); } } // Code-based gaps using actual code analysis // Check for API documentation gaps based on actual endpoints found if ( codeAnalysis.apiEndpoints.length > 0 && !hasApiDocumentation(docsAnalysis) ) { gaps.push({ category: "reference", gapType: "missing_section", description: `Found ${codeAnalysis.apiEndpoints.length} API endpoints but no API documentation`, priority: "critical", recommendation: "Create API reference documentation for discovered endpoints", estimatedEffort: "substantial", relatedFiles: [ ...new Set(codeAnalysis.apiEndpoints.map((ep) => ep.filePath)), ], }); } // Check for undocumented API endpoints const undocumentedEndpoints = codeAnalysis.apiEndpoints.filter( (ep) => !ep.hasDocumentation, ); if (undocumentedEndpoints.length > 0) { gaps.push({ category: "reference", gapType: "missing_examples", description: `${undocumentedEndpoints.length} API endpoints lack inline documentation`, priority: "high", recommendation: "Add JSDoc comments to API endpoint handlers", estimatedEffort: "moderate", relatedFiles: [ ...new Set(undocumentedEndpoints.map((ep) => ep.filePath)), ], }); } // Check for class/interface documentation const undocumentedClasses = codeAnalysis.classes.filter( (cls) => cls.exported && !cls.hasJSDoc, ); if (undocumentedClasses.length > 0) { gaps.push({ category: "reference", gapType: "incomplete_content", description: `${undocumentedClasses.length} exported classes lack documentation`, priority: "medium", recommendation: "Add JSDoc comments to exported classes and create API reference", estimatedEffort: "moderate", relatedFiles: [ ...new Set(undocumentedClasses.map((cls) => cls.filePath)), ], }); } // Check for interface documentation const undocumentedInterfaces = codeAnalysis.interfaces.filter( (iface) => iface.exported && !iface.hasJSDoc, ); if (undocumentedInterfaces.length > 0) { gaps.push({ category: "reference", gapType: "incomplete_content", description: `${undocumentedInterfaces.length} exported interfaces lack documentation`, priority: "medium", recommendation: "Add JSDoc comments to exported interfaces and create type documentation", estimatedEffort: "moderate", relatedFiles: [ ...new Set(undocumentedInterfaces.map((iface) => iface.filePath)), ], }); } // Check for function documentation const undocumentedFunctions = codeAnalysis.functions.filter( (func) => func.exported && !func.hasJSDoc, ); if (undocumentedFunctions.length > 0) { gaps.push({ category: "reference", gapType: "incomplete_content", description: `${undocumentedFunctions.length} exported functions lack documentation`, priority: "medium", recommendation: "Add JSDoc comments to exported functions and create API reference", estimatedEffort: "substantial", relatedFiles: [ ...new Set(undocumentedFunctions.map((func) => func.filePath)), ], }); } // Framework-specific documentation gaps if ( codeAnalysis.frameworks.includes("React") && !hasFrameworkDocumentation(docsAnalysis, "react") ) { gaps.push({ category: "how-to", gapType: "missing_section", description: "React framework detected but no React-specific documentation found", priority: "medium", recommendation: "Create React component usage and development guides", estimatedEffort: "moderate", }); } if ( codeAnalysis.frameworks.includes("Express") && !hasFrameworkDocumentation(docsAnalysis, "express") ) { gaps.push({ category: "how-to", gapType: "missing_section", description: "Express framework detected but no Express-specific documentation found", priority: "medium", recommendation: "Create Express server setup and API development guides", estimatedEffort: "moderate", }); } // Test documentation gaps if (codeAnalysis.hasTests && !hasTestingDocumentation(docsAnalysis)) { gaps.push({ category: "how-to", gapType: "missing_section", description: "Test files found but no testing documentation", priority: "medium", recommendation: "Create testing setup and contribution guides", estimatedEffort: "moderate", relatedFiles: codeAnalysis.testFiles, }); } // Technology-specific gaps based on repository analysis (fallback) if (repoAnalysis) { // Check for setup/installation guides if (repoAnalysis.packageManager && !hasInstallationGuide(docsAnalysis)) { gaps.push({ category: "tutorials", gapType: "missing_section", description: "Package manager detected but no installation guide found", priority: "high", recommendation: "Create installation and setup tutorial", estimatedEffort: "moderate", }); } // Check for Docker documentation if (repoAnalysis.hasDocker && !hasDockerDocumentation(docsAnalysis)) { gaps.push({ category: "how-to", gapType: "missing_section", description: "Docker configuration found but no Docker documentation", priority: "medium", recommendation: "Add Docker deployment and development guides", estimatedEffort: "moderate", }); } // Check for CI/CD documentation if (repoAnalysis.hasCICD && !hasCICDDocumentation(docsAnalysis)) { gaps.push({ category: "explanation", gapType: "missing_section", description: "CI/CD configuration found but no related documentation", priority: "medium", recommendation: "Document CI/CD processes and deployment workflows", estimatedEffort: "moderate", }); } } // Add validation-based gaps if (validationResult?.validationResults) { for (const result of validationResult.validationResults) { if (result.status === "fail") { gaps.push({ category: "general", gapType: "poor_structure", description: result.message, priority: "medium", recommendation: result.recommendation || "Fix validation issue", estimatedEffort: "minimal", }); } } } return gaps; }