Skip to main content
Glama

sync_code_to_docs

Automatically synchronize documentation with code changes using AST-based drift detection to maintain accuracy between source code and documentation.

Instructions

Automatically synchronize documentation with code changes using AST-based drift detection (Phase 3)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectPathYesPath to the project root directory
docsPathYesPath to the documentation directory
modeNoSync mode: detect=analyze only, preview=show changes, apply=apply safe changes, auto=apply alldetect
autoApplyThresholdNoConfidence threshold (0-1) for automatic application of changes
createSnapshotNoCreate a snapshot before making changes (recommended)

Implementation Reference

  • Main handler function that executes the sync_code_to_docs tool logic: parses input, detects drift between code and docs, applies changes based on mode, computes stats, and returns structured results.
    export async function handleSyncCodeToDocs( args: unknown, context?: any, ): Promise<{ content: any[] }> { const startTime = Date.now(); try { const { projectPath, docsPath, mode, autoApplyThreshold, createSnapshot } = inputSchema.parse(args); await context?.info?.( `🔄 Starting code-to-documentation synchronization (mode: ${mode})...`, ); // Initialize drift detector const detector = new DriftDetector(projectPath); await detector.initialize(); // Create baseline snapshot if requested if (createSnapshot || mode !== "detect") { await context?.info?.("📸 Creating code snapshot..."); await detector.createSnapshot(projectPath, docsPath); } // Load previous snapshot for comparison await context?.info?.("🔍 Detecting documentation drift..."); const previousSnapshot = await detector.loadLatestSnapshot(); if (!previousSnapshot) { await context?.info?.( "â„šī¸ No previous snapshot found. Creating baseline...", ); const baselineSnapshot = await detector.createSnapshot( projectPath, docsPath, ); const result: SyncResult = { mode, driftDetections: [], appliedChanges: [], pendingChanges: [], stats: { filesAnalyzed: baselineSnapshot.files.size, driftsDetected: 0, changesApplied: 0, changesPending: 0, breakingChanges: 0, estimatedUpdateTime: "0 minutes", }, snapshotId: baselineSnapshot.timestamp, }; 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: "Baseline Created", description: "Baseline snapshot created. Run sync again after code changes to detect drift.", }, ], }; return formatMCPResponse(response, { fullResponse: true }); } // Create current snapshot and detect drift const currentSnapshot = await detector.createSnapshot( projectPath, docsPath, ); const driftResults = await detector.detectDrift( previousSnapshot, currentSnapshot, ); await context?.info?.( `📊 Found ${driftResults.length} file(s) with documentation drift`, ); // Process based on mode const appliedChanges: AppliedChange[] = []; const pendingChanges: PendingSuggestion[] = []; for (const driftResult of driftResults) { if (driftResult.hasDrift) { for (const suggestion of driftResult.suggestions) { if (mode === "apply" || mode === "auto") { // Apply changes based on confidence const shouldApply = mode === "auto" || (suggestion.autoApplicable && suggestion.confidence >= autoApplyThreshold); if (shouldApply) { try { await applyDocumentationChange( suggestion, context, projectPath, ); appliedChanges.push({ docFile: suggestion.docFile, section: suggestion.section, changeType: "updated", confidence: suggestion.confidence, details: suggestion.reasoning, }); } catch (error: any) { await context?.warn?.( `Failed to apply change to ${suggestion.docFile}: ${error.message}`, ); pendingChanges.push({ docFile: suggestion.docFile, section: suggestion.section, reason: `Auto-apply failed: ${error.message}`, suggestion, requiresReview: true, }); } } else { pendingChanges.push({ docFile: suggestion.docFile, section: suggestion.section, reason: "Requires manual review", suggestion, requiresReview: true, }); } } else { // Preview/detect mode - just collect suggestions pendingChanges.push({ docFile: suggestion.docFile, section: suggestion.section, reason: "Detected drift", suggestion, requiresReview: !suggestion.autoApplicable, }); } } } } // Calculate stats const stats = calculateSyncStats( driftResults, appliedChanges, pendingChanges, ); // Store sync results in knowledge graph await storeSyncResults(projectPath, driftResults, appliedChanges, context); const result: SyncResult = { mode, driftDetections: driftResults, appliedChanges, pendingChanges, stats, snapshotId: currentSnapshot.timestamp, }; const response: MCPToolResponse<typeof result> = { success: true, data: result, metadata: { toolVersion: "3.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, recommendations: generateRecommendations(result), nextSteps: generateNextSteps(result), }; await context?.info?.( `✅ Synchronization complete: ${appliedChanges.length} applied, ${pendingChanges.length} pending`, ); return formatMCPResponse(response, { fullResponse: true }); } catch (error: any) { const errorResponse: MCPToolResponse = { success: false, error: { code: "SYNC_FAILED", message: `Documentation synchronization failed: ${error.message}`, resolution: "Check project and documentation paths are correct", }, metadata: { toolVersion: "3.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; return formatMCPResponse(errorResponse, { fullResponse: true }); } }
  • MCP Tool registration defining the 'sync_code_to_docs' tool with name, description, and detailed input schema.
    export const syncCodeToDocs: Tool = { name: "sync_code_to_docs", description: "Automatically synchronize documentation with code changes using AST-based drift detection (Phase 3)", inputSchema: { type: "object", properties: { projectPath: { type: "string", description: "Path to the project root directory", }, docsPath: { type: "string", description: "Path to the documentation directory", }, mode: { type: "string", enum: ["detect", "preview", "apply", "auto"], default: "detect", description: "Sync mode: detect=analyze only, preview=show changes, apply=apply safe changes, auto=apply all", }, autoApplyThreshold: { type: "number", minimum: 0, maximum: 1, default: 0.8, description: "Confidence threshold (0-1) for automatic application of changes", }, createSnapshot: { type: "boolean", default: true, description: "Create a snapshot before making changes (recommended)", }, }, required: ["projectPath", "docsPath"], }, };
  • Zod input validation schema used within the handler to parse and validate tool arguments.
    const inputSchema = z.object({ projectPath: z.string().describe("Path to the project root"), docsPath: z.string().describe("Path to the documentation directory"), mode: z .enum(["detect", "preview", "apply", "auto"]) .default("detect") .describe( "Mode: detect=analyze only, preview=show changes, apply=apply safe changes, auto=apply all changes", ), autoApplyThreshold: z .number() .min(0) .max(1) .default(0.8) .describe("Confidence threshold for automatic application (0-1)"), createSnapshot: z .boolean() .default(true) .describe("Create a snapshot before making changes"), });
  • Helper function to apply a specific documentation change: updates file content and metadata.
    async function applyDocumentationChange( suggestion: DriftSuggestion, context?: any, projectPath?: string, ): Promise<void> { const filePath = suggestion.docFile; // Read current file const content = await fs.readFile(filePath, "utf-8"); // Find and replace the section const sectionPattern = new RegExp( `(#{1,6}\\s+${escapeRegex(suggestion.section)}[\\s\\S]*?)(?=#{1,6}\\s+|$)`, "g", ); let newContent = content; const match = sectionPattern.exec(content); if (match) { // Replace existing section newContent = content.replace(sectionPattern, suggestion.suggestedContent); await context?.info?.( `âœī¸ Updated section '${suggestion.section}' in ${path.basename( filePath, )}`, ); } else { // Append new section newContent = content + "\n\n" + suggestion.suggestedContent; await context?.info?.( `➕ Added section '${suggestion.section}' to ${path.basename(filePath)}`, ); } // Write back to file await fs.writeFile(filePath, newContent, "utf-8"); // Update freshness metadata try { let currentCommit: string | undefined; if (projectPath) { try { const git = simpleGit(projectPath); const isRepo = await git.checkIsRepo(); if (isRepo) { const log = await git.log({ maxCount: 1 }); currentCommit = log.latest?.hash; } } catch { // Git not available, continue without it } } await updateDocFrontmatter(filePath, { last_updated: new Date().toISOString(), last_validated: new Date().toISOString(), auto_updated: true, validated_against_commit: currentCommit, }); await context?.info?.( `đŸˇī¸ Updated freshness metadata for ${path.basename(filePath)}`, ); } catch (error) { // Non-critical error, just log it await context?.warn?.(`Failed to update freshness metadata: ${error}`); } }

Latest Blog Posts

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