check_documentation_links
Validate external, internal, and anchor links in documentation to identify broken references before deployment.
Instructions
Comprehensive link checking for documentation deployment with external, internal, and anchor link validation
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| documentation_path | No | Path to the documentation directory to check | ./docs |
| check_external_links | No | Validate external URLs (slower but comprehensive) | |
| check_internal_links | No | Validate internal file references | |
| check_anchor_links | No | Validate anchor links within documents | |
| timeout_ms | No | Timeout for external link requests in milliseconds | |
| max_concurrent_checks | No | Maximum concurrent link checks | |
| allowed_domains | No | Whitelist of allowed external domains (empty = all allowed) | |
| ignore_patterns | No | URL patterns to ignore during checking | |
| fail_on_broken_links | No | Fail the check if broken links are found | |
| output_format | No | Output format for results | detailed |
Implementation Reference
- The primary handler function for the 'check_documentation_links' tool. It orchestrates scanning documentation files, extracting links, checking their validity (internal, external, anchor), and generating a comprehensive report with summaries and recommendations.export async function checkDocumentationLinks( input: Partial<LinkCheckInput>, ): Promise<MCPToolResponse<LinkCheckReport>> { const startTime = Date.now(); try { // Validate input with defaults const validatedInput = LinkCheckInputSchema.parse(input); const { documentation_path, check_external_links, check_internal_links, check_anchor_links, timeout_ms, max_concurrent_checks, allowed_domains, ignore_patterns, fail_on_broken_links, } = validatedInput; // Scan documentation files const documentationFiles = await scanDocumentationFiles(documentation_path); if (documentationFiles.length === 0) { return { success: false, error: { code: "NO_DOCUMENTATION_FILES", message: "No documentation files found in the specified path", details: `Searched in: ${documentation_path}`, resolution: "Verify the documentation_path parameter points to a directory containing markdown files", }, metadata: { toolVersion: "1.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; } // Extract all links from documentation files const allLinks = await extractLinksFromFiles( documentationFiles, documentation_path, ); // Filter links based on configuration const filteredLinks = filterLinks(allLinks, { checkExternalLinks: check_external_links, checkInternalLinks: check_internal_links, checkAnchorLinks: check_anchor_links, ignorePatterns: ignore_patterns, }); // Check links with concurrency control const linkResults = await checkLinksWithConcurrency(filteredLinks, { timeoutMs: timeout_ms, maxConcurrent: max_concurrent_checks, allowedDomains: allowed_domains, documentationPath: documentation_path, }); // Generate report const report = generateLinkCheckReport(linkResults, { checkExternalLinks: check_external_links, checkInternalLinks: check_internal_links, checkAnchorLinks: check_anchor_links, timeoutMs: timeout_ms, maxConcurrentChecks: max_concurrent_checks, filesScanned: documentationFiles.length, executionTime: Date.now() - startTime, }); // Check if we should fail on broken links if (fail_on_broken_links && report.summary.brokenLinks > 0) { return { success: false, error: { code: "BROKEN_LINKS_FOUND", message: `Found ${report.summary.brokenLinks} broken links`, details: `${report.summary.brokenLinks} out of ${report.summary.totalLinks} links are broken`, resolution: "Fix the broken links or set fail_on_broken_links to false", }, data: report, metadata: { toolVersion: "1.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; } return { success: true, data: report, metadata: { toolVersion: "1.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; } catch (error) { return { success: false, error: { code: "LINK_CHECK_ERROR", message: "Failed to check documentation links", details: error instanceof Error ? error.message : "Unknown error occurred", resolution: "Check the documentation path and ensure files are accessible", }, metadata: { toolVersion: "1.0.0", executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; } }
- Zod input schema defining parameters for the tool, including defaults for documentation path, link check options, timeouts, and output format.const LinkCheckInputSchema = z.object({ documentation_path: z.string().default("./docs"), check_external_links: z.boolean().default(true), check_internal_links: z.boolean().default(true), check_anchor_links: z.boolean().default(true), timeout_ms: z.number().min(1000).max(30000).default(5000), max_concurrent_checks: z.number().min(1).max(20).default(5), allowed_domains: z.array(z.string()).default([]), ignore_patterns: z.array(z.string()).default([]), fail_on_broken_links: z.boolean().default(false), output_format: z.enum(["summary", "detailed", "json"]).default("detailed"), });
- TypeScript interface defining the structure of the tool's output report, including summary statistics, detailed results, recommendations, and configuration.interface LinkCheckReport { summary: { totalLinks: number; validLinks: number; brokenLinks: number; warningLinks: number; skippedLinks: number; executionTime: number; filesScanned: number; }; results: LinkCheckResult[]; recommendations: string[]; configuration: { checkExternalLinks: boolean; checkInternalLinks: boolean; checkAnchorLinks: boolean; timeoutMs: number; maxConcurrentChecks: number; }; }
- Helper function to recursively scan the documentation directory for markdown files.async function scanDocumentationFiles(basePath: string): Promise<string[]> { const files: string[] = []; async function scanDirectory(dirPath: string): Promise<void> { try { const entries = await readdir(dirPath); for (const entry of entries) { const fullPath = join(dirPath, entry); const stats = await stat(fullPath); if (stats.isDirectory()) { // Skip node_modules and hidden directories if (!entry.startsWith(".") && entry !== "node_modules") { await scanDirectory(fullPath); } } else if (stats.isFile()) { const ext = extname(entry).toLowerCase(); if ([".md", ".mdx", ".markdown"].includes(ext)) { files.push(fullPath); } } } } catch (error) { // Skip directories we can't read } } await scanDirectory(basePath); return files; }
- Supporting helper functions that handle link extraction, filtering, concurrent checking for internal/external/anchor links, and report generation.} async function scanDocumentationFiles(basePath: string): Promise<string[]> {