Resolve Alias
resolve_aliasResolve aliases or display names to actual note paths by searching frontmatter aliases and filenames. Translates human-friendly titles into note paths.
Instructions
Find every note whose frontmatter aliases: field contains the given name (case-insensitive). With includeBasename: true, also matches notes whose filename (without .md) equals the name — Obsidian's resolution fallback when no alias matches. Use to translate a human-friendly title like 'My Project' into the actual note path before calling get_note.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Alias or display name to resolve, e.g. 'My Project'. | |
| includeBasename | No | If true (default), also match notes whose filename (without extension) equals `name`. |
Implementation Reference
- src/tools/read.ts:645-687 (handler)The async handler function that executes the resolve_alias tool logic: reads all notes, checks frontmatter aliases and optionally basenames, returns matching note paths.
async ({ name, includeBasename }) => { try { const target = name.trim().toLowerCase(); if (!target) return errorResult("name must not be empty"); const notes = await listNotes(vaultPath); const { contents } = await readAllCached(vaultPath, notes, (note, err) => { log.warn("resolve_alias: note read failed", { note, err }); }); const aliasMatches: string[] = []; const basenameMatches: string[] = []; for (const notePath of notes) { if (includeBasename) { const basename = path.basename(notePath, path.extname(notePath)).toLowerCase(); if (basename === target) basenameMatches.push(notePath); } const content = contents.get(notePath); if (content === undefined) continue; const aliases = extractAliases(content); if (aliases.some((a) => a.toLowerCase() === target)) aliasMatches.push(notePath); } const total = aliasMatches.length + basenameMatches.length; if (total === 0) { return { content: [{ type: "text" as const, text: `No alias or basename match for "${name}"` }] }; } const lines: string[] = [`Matches for "${name}":`, ""]; if (aliasMatches.length > 0) { lines.push(`Alias matches (${aliasMatches.length}):`); for (const p of aliasMatches) lines.push(` - ${p}`); } if (basenameMatches.length > 0) { if (aliasMatches.length > 0) lines.push(""); lines.push(`Basename matches (${basenameMatches.length}):`); for (const p of basenameMatches) lines.push(` - ${p}`); } return { content: [{ type: "text" as const, text: lines.join("\n") }] }; } catch (err) { log.error("resolve_alias failed", { tool: "resolve_alias", err: err as Error }); return errorResult(`Error resolving alias: ${sanitizeError(err)}`); } }, - src/tools/read.ts:633-643 (schema)Input schema for resolve_alias: requires a string `name` (min 1 char) and optional boolean `includeBasename` (default true).
inputSchema: { name: z .string() .min(1) .describe("Alias or display name to resolve, e.g. 'My Project'."), includeBasename: z .boolean() .optional() .default(true) .describe("If true (default), also match notes whose filename (without extension) equals `name`."), }, - src/tools/read.ts:622-688 (registration)Registration of the 'resolve_alias' tool on the MCP server via server.registerTool() within registerReadTools.
server.registerTool( "resolve_alias", { title: "Resolve Alias", description: "Find every note whose frontmatter `aliases:` field contains the given name (case-insensitive). With `includeBasename: true`, also matches notes whose filename (without `.md`) equals the name — Obsidian's resolution fallback when no alias matches. Use to translate a human-friendly title like 'My Project' into the actual note path before calling get_note.", annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false, }, inputSchema: { name: z .string() .min(1) .describe("Alias or display name to resolve, e.g. 'My Project'."), includeBasename: z .boolean() .optional() .default(true) .describe("If true (default), also match notes whose filename (without extension) equals `name`."), }, }, async ({ name, includeBasename }) => { try { const target = name.trim().toLowerCase(); if (!target) return errorResult("name must not be empty"); const notes = await listNotes(vaultPath); const { contents } = await readAllCached(vaultPath, notes, (note, err) => { log.warn("resolve_alias: note read failed", { note, err }); }); const aliasMatches: string[] = []; const basenameMatches: string[] = []; for (const notePath of notes) { if (includeBasename) { const basename = path.basename(notePath, path.extname(notePath)).toLowerCase(); if (basename === target) basenameMatches.push(notePath); } const content = contents.get(notePath); if (content === undefined) continue; const aliases = extractAliases(content); if (aliases.some((a) => a.toLowerCase() === target)) aliasMatches.push(notePath); } const total = aliasMatches.length + basenameMatches.length; if (total === 0) { return { content: [{ type: "text" as const, text: `No alias or basename match for "${name}"` }] }; } const lines: string[] = [`Matches for "${name}":`, ""]; if (aliasMatches.length > 0) { lines.push(`Alias matches (${aliasMatches.length}):`); for (const p of aliasMatches) lines.push(` - ${p}`); } if (basenameMatches.length > 0) { if (aliasMatches.length > 0) lines.push(""); lines.push(`Basename matches (${basenameMatches.length}):`); for (const p of basenameMatches) lines.push(` - ${p}`); } return { content: [{ type: "text" as const, text: lines.join("\n") }] }; } catch (err) { log.error("resolve_alias failed", { tool: "resolve_alias", err: err as Error }); return errorResult(`Error resolving alias: ${sanitizeError(err)}`); } }, ); - src/index.ts:308-308 (registration)Call to registerReadTools which registers resolve_alias along with other read tools on the MCP server.
registerReadTools(server, vaultForTools); - src/lib/markdown.ts:435-450 (helper)The extractAliases helper function that parses frontmatter to extract aliases from various casing variants (aliases, Aliases, ALIASES, alias, Alias). Returns an array of trimmed alias strings.
export function extractAliases(content: string): string[] { const { data } = parseFrontmatter(content); const aliasField = data.aliases ?? data.Aliases ?? data.ALIASES ?? data.alias ?? data.Alias; if (!aliasField) return []; if (Array.isArray(aliasField)) { return aliasField.map((a: unknown) => String(a).trim()).filter(Boolean); } if (typeof aliasField === "string") { return aliasField.split(",").map((a: string) => a.trim()).filter(Boolean); } return []; }