Skip to main content
Glama

find_broken_links

Identify wikilinks pointing to missing notes in your Obsidian vault to maintain content integrity.

Instructions

Find all wikilinks that point to non-existent notes

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
folderNoLimit scan to a specific folder
maxResultsNoMaximum results to return

Implementation Reference

  • The handler for the 'find_broken_links' tool, which scans notes for wikilinks that do not resolve to an existing note.
      async ({ folder, maxResults }) => {
        try {
          // Get all notes in vault for resolution, but only scan the folder
          const allNotes = await listNotes(vaultPath);
          const scanNotes = folder ? await listNotes(vaultPath, folder) : allNotes;
    
          const brokenBySource = new Map<string, BrokenLink[]>();
    
          for (const notePath of scanNotes) {
            let content: string;
            try {
              content = await readNote(vaultPath, notePath);
            } catch {
              console.error(`Failed to read note for broken link scan: ${notePath}`);
              continue;
            }
    
            const lines = content.split("\n");
            const links = extractWikilinks(content);
    
            for (const link of links) {
              const targetBase = link.target.split("#")[0].trim();
              if (!targetBase) continue;
    
              const resolved = resolveWikilink(targetBase, notePath, allNotes);
              if (!resolved) {
                const lineInfo = findLineWithLink(lines, link.target);
                const broken: BrokenLink = {
                  sourcePath: notePath,
                  targetLink: link.target,
                  line: lineInfo.line,
                };
    
                if (!brokenBySource.has(notePath)) {
                  brokenBySource.set(notePath, []);
                }
                brokenBySource.get(notePath)!.push(broken);
              }
            }
          }
    
          if (brokenBySource.size === 0) {
            const scopeStr = folder ? ` in folder: ${folder}` : "";
            return {
              content: [
                {
                  type: "text" as const,
                  text: `No broken links found${scopeStr}`,
                },
              ],
            };
          }
    
          let totalBroken = 0;
          for (const brokenLinks of brokenBySource.values()) {
            totalBroken += brokenLinks.length;
          }
    
          const lines: string[] = [];
          const scopeStr = folder ? ` (folder: ${folder})` : "";
          lines.push(`Broken links report${scopeStr}\n`);
    
          let shown = 0;
          for (const [sourcePath, brokenLinks] of brokenBySource) {
            if (shown >= maxResults) break;
            lines.push(`${sourcePath}:`);
            for (const bl of brokenLinks) {
              if (shown >= maxResults) break;
              const lineStr = bl.line > 0 ? ` (line ${bl.line})` : "";
              lines.push(`  - [[${bl.targetLink}]]${lineStr}`);
              shown++;
            }
            lines.push("");
          }
    
          if (shown < totalBroken) {
            lines.push(`... and ${totalBroken - shown} more broken link(s) not shown`);
          }
          lines.push(`Total: ${totalBroken} broken link(s) across ${brokenBySource.size} file(s)`);
    
          return { content: [{ type: "text" as const, text: lines.join("\n") }] };
        } catch (err) {
          console.error("find_broken_links error:", err);
          return errorResult(`Error finding broken links: ${err instanceof Error ? err.message : String(err)}`);
        }
      },
    );
  • Registration of the 'find_broken_links' tool in the MCP server, defining its description and input schema.
    // ── find_broken_links ──────────────────────────────────────────
    server.registerTool(
      "find_broken_links",
      {
        description: "Find all wikilinks that point to non-existent notes",
        inputSchema: {
          folder: z
            .string()
            .optional()
            .describe("Limit scan to a specific folder"),
          maxResults: z
            .number()
            .optional()
            .default(200)
            .describe("Maximum results to return"),
        },
      },

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/rps321321/obsidian-mcp-pro'

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