get_backlinks
Find all notes linking to a specific note in your Obsidian vault to analyze connections and discover related content.
Instructions
Find all notes that link to a specific note
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | The target note path (relative to vault root) |
Implementation Reference
- src/tools/links.ts:103-212 (handler)The handler function for the get_backlinks tool. It builds a link graph of the vault, resolves the target note, retrieves backlinks from the graph, finds the context of the links within those notes, and formats the output.
// ── get_backlinks ────────────────────────────────────────────── server.registerTool( "get_backlinks", { description: "Find all notes that link to a specific note", inputSchema: { path: z.string().min(1).describe("The target note path (relative to vault root)"), }, }, async ({ path: targetPath }) => { try { const graph = await buildLinkGraph(vaultPath); // Normalize target for comparison const targetNormalized = targetPath.replace(/\.md$/i, "").toLowerCase(); const targetBasename = targetNormalized.split("/").pop() ?? targetNormalized; // Find the actual note path that matches the target let resolvedTarget: string | null = null; for (const notePath of graph.allNotes) { const noteNormalized = notePath.replace(/\.md$/i, "").toLowerCase(); if (noteNormalized === targetNormalized) { resolvedTarget = notePath; break; } } // Also try basename matching if exact match failed if (!resolvedTarget) { for (const notePath of graph.allNotes) { const noteBasename = notePath .replace(/\.md$/i, "") .split("/") .pop() ?.toLowerCase(); if (noteBasename === targetBasename) { resolvedTarget = notePath; break; } } } if (!resolvedTarget) { return errorResult(`No note found matching path: ${targetPath}`); } const backlinkSources = graph.backlinks.get(resolvedTarget); if (!backlinkSources || backlinkSources.size === 0) { return { content: [ { type: "text" as const, text: `No backlinks found for: ${resolvedTarget}`, }, ], }; } const results: { source: string; line: number; context: string }[] = []; for (const sourcePath of backlinkSources) { const lines = graph.noteLines.get(sourcePath) ?? []; // Find the line(s) that contain the link to the target const links = graph.rawLinks.get(sourcePath) ?? []; const relevantLinks = links.filter((l) => { const base = l.target.split("#")[0].trim(); const resolved = resolveWikilink(base, sourcePath, graph.allNotes); return resolved === resolvedTarget; }); if (relevantLinks.length > 0) { for (const link of relevantLinks) { const lineInfo = findLineWithLink(lines, link.target); results.push({ source: sourcePath, line: lineInfo.line, context: lineInfo.content, }); } } else { results.push({ source: sourcePath, line: 0, context: "" }); } } // Deduplicate by source+line const seen = new Set<string>(); const deduped = results.filter((r) => { const key = `${r.source}:${r.line}`; if (seen.has(key)) return false; seen.add(key); return true; }); const output = [ `Backlinks to: ${resolvedTarget}`, `Found: ${deduped.length} backlink(s)\n`, ...deduped.map((r) => { const lineStr = r.line > 0 ? `:${r.line}` : ""; const contextStr = r.context ? ` → ${r.context}` : ""; return `- ${r.source}${lineStr}${contextStr}`; }), ].join("\n"); return { content: [{ type: "text" as const, text: output }] }; } catch (err) { console.error("get_backlinks error:", err); return errorResult(`Error finding backlinks: ${err instanceof Error ? err.message : String(err)}`); } }, );