find_broken_links
Detect and fix broken links in Obsidian vaults to maintain note connectivity and improve content organization.
Instructions
壊れたリンクの検出と修復支援を行います
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/obsidian-handler.ts:320-422 (handler)Primary tool handler that implements the logic to find broken links in Obsidian vault by scanning all MD files for wiki and markdown links, verifying targets, and providing similarity-based suggestions.async findBrokenLinks(): Promise<{ brokenLinks: Array<{ sourceFile: string; linkText: string; targetPath: string; lineNumber: number; suggestions: string[]; }>; totalCount: number; }> { const vaultPath = this.config.vaultPath; const brokenLinks: Array<{ sourceFile: string; linkText: string; targetPath: string; lineNumber: number; suggestions: string[]; }> = []; // すべてのMarkdownファイルを取得 const allMdFiles = await this.getAllMdFiles(vaultPath); const allFileBasenames = allMdFiles.map(file => path.basename(file, '.md')); const processFile = async (filePath: string) => { try { const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); const relativeFilePath = path.relative(vaultPath, filePath); for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { const line = lines[lineIndex]; // ウィキリンク [[]] の検出 const wikiLinkMatches = line.matchAll(/\[\[([^\]|]+)(\|[^\]]+)?\]\]/g); for (const match of wikiLinkMatches) { const targetPath = match[1]; const linkText = match[2] ? match[2].substring(1) : targetPath; // ターゲットファイルの存在確認 const possiblePaths = [ path.join(vaultPath, `${targetPath}.md`), path.join(vaultPath, targetPath), ]; let exists = false; for (const possiblePath of possiblePaths) { if (await FileUtils.fileExists(possiblePath)) { exists = true; break; } } if (!exists) { // 修復候補を検索 const suggestions = this.findSimilarFiles(targetPath, allFileBasenames); brokenLinks.push({ sourceFile: relativeFilePath, linkText, targetPath, lineNumber: lineIndex + 1, suggestions, }); } } // Markdownリンク []() の検出 const mdLinkMatches = line.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g); for (const match of mdLinkMatches) { const linkText = match[1]; const targetPath = match[2]; // 内部リンク(.mdファイル)のみチェック if (targetPath.endsWith('.md') || !targetPath.includes('://')) { const fullTargetPath = path.resolve(path.dirname(filePath), targetPath); if (!(await FileUtils.fileExists(fullTargetPath))) { const suggestions = this.findSimilarFiles(path.basename(targetPath, '.md'), allFileBasenames); brokenLinks.push({ sourceFile: relativeFilePath, linkText, targetPath, lineNumber: lineIndex + 1, suggestions, }); } } } } } catch (error) { // ファイル読み込みエラーは無視 } }; // すべてのMarkdownファイルを処理 for (const filePath of allMdFiles) { await processFile(filePath); } return { brokenLinks, totalCount: brokenLinks.length, }; }
- src/server.ts:192-199 (registration)MCP tool registration defining the name, description, and input schema for find_broken_links.{ name: 'find_broken_links', description: '壊れたリンクの検出と修復支援を行います', inputSchema: { type: 'object', properties: {}, }, },
- src/server.ts:341-350 (handler)Server-side dispatch handler that invokes the ObsidianHandler's findBrokenLinks method and formats the response as MCP content.case 'find_broken_links': const brokenLinksResult = await this.obsidianHandler.findBrokenLinks(); return { content: [ { type: 'text', text: JSON.stringify(brokenLinksResult, null, 2), }, ], };
- src/obsidian-handler.ts:449-464 (helper)Helper utility to generate file name suggestions for broken links using similarity scoring.private findSimilarFiles(targetName: string, allFileBasenames: string[]): string[] { const target = targetName.toLowerCase(); const suggestions: { name: string; score: number }[] = []; for (const filename of allFileBasenames) { const score = this.calculateSimilarity(target, filename.toLowerCase()); if (score > 0.3) { // 30%以上の類似度 suggestions.push({ name: filename, score }); } } return suggestions .sort((a, b) => b.score - a.score) .slice(0, 5) .map(s => s.name); }
- src/obsidian-handler.ts:424-447 (helper)Helper to recursively collect all Markdown files in the vault.private async getAllMdFiles(dirPath: string): Promise<string[]> { const files: string[] = []; const processDirectory = async (currentPath: string) => { try { const entries = await fs.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); if (entry.isDirectory()) { await processDirectory(fullPath); } else if (entry.isFile() && entry.name.endsWith('.md')) { files.push(fullPath); } } } catch (error) { // ディレクトリ読み込みエラーは無視 } }; await processDirectory(dirPath); return files; }