Bulk rename a tag
obsidian_rename_tagReplace all instances of an existing tag with a new tag across your entire Obsidian vault.
Instructions
Renames a tag across every note in the vault.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| vault | No | Vault name to target. Optional — defaults to the most recently focused vault. | |
| old | Yes | Existing tag name (e.g. '#old'). | |
| new | Yes | New tag name (e.g. '#new'). | |
| confirm | No | Set to true to skip the interactive confirmation prompt. Use only when the caller has already confirmed with the user. |
Implementation Reference
- src/tools.ts:593-598 (schema)Input schema for obsidian_rename_tag: requires 'old' (existing tag) and 'new' (new tag) strings, both with min length 1, plus optional VaultArg and ConfirmArg.
inputSchema: { ...VaultArg, old: z.string().min(1).describe("Existing tag name (e.g. '#old')."), new: z.string().min(1).describe("New tag name (e.g. '#new')."), ...ConfirmArg, }, - src/tools.ts:604-605 (handler)Handler calls runText with command 'tags:rename' passing vault, old tag, and new tag.
handler: async ({ vault, old, new: newTag }) => runText("tags:rename", { vault, params: { old, new: newTag } }), - src/tools.ts:589-606 (registration)Full tool registration with name 'obsidian_rename_tag', destructivelyHinted, with confirmation prompt config.
{ name: "obsidian_rename_tag", title: "Bulk rename a tag", description: "Renames a tag across every note in the vault.", inputSchema: { ...VaultArg, old: z.string().min(1).describe("Existing tag name (e.g. '#old')."), new: z.string().min(1).describe("New tag name (e.g. '#new')."), ...ConfirmArg, }, annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false }, confirm: { action: () => "Rename a tag across the entire vault", detail: ({ old, new: newTag }) => `${old} → ${newTag}`, }, handler: async ({ vault, old, new: newTag }) => runText("tags:rename", { vault, params: { old, new: newTag } }), }, - src/tools.ts:99-110 (helper)Helper runText executes the Obsidian CLI command and returns trimmed output as text result.
async function runText( command: string, opts: Parameters<typeof runObsidian>[1] = {}, ): Promise<McpToolResult> { try { const result = await runObsidian(command, opts); const text = result.stdout.trim() || result.stderr.trim() || "(no output)"; return textResult(text); } catch (err) { return errorResult(err); } } - src/exec.ts:64-101 (helper)runObsidian executes the Obsidian CLI binary with built arguments, used by runText to invoke 'tags:rename'.
export async function runObsidian( command: string, opts: RunOptions = {}, ): Promise<RunResult> { const bin = process.env.OBSIDIAN_CLI ?? "obsidian"; const args = buildArgs(command, opts); const cmdline = [bin, ...args].map(shellQuote).join(" "); try { const { stdout, stderr } = await exec(cmdline, { maxBuffer: 64 * 1024 * 1024, windowsHide: true, }); return { stdout, stderr, exitCode: 0, command: cmdline }; } catch (err: unknown) { const e = err as NodeJS.ErrnoException & { stdout?: string; stderr?: string; code?: number | string; }; const result: RunResult = { stdout: e.stdout ?? "", stderr: e.stderr ?? e.message ?? "", exitCode: typeof e.code === "number" ? e.code : 1, command: cmdline, }; if (e.code === "ENOENT") { throw new ObsidianCliError( `Obsidian CLI binary not found ('${bin}'). ` + `Make sure Obsidian is running and the CLI is registered ` + `(Settings → General → Command line interface → Register CLI). ` + `Override with the OBSIDIAN_CLI env var if the binary lives elsewhere.`, result, ); } throw new ObsidianCliError( `obsidian CLI exited with code ${result.exitCode}: ${result.stderr.trim() || result.stdout.trim()}`, result,