Skip to main content
Glama
jagoff

obsidian-mcp-complete

by jagoff

obsidian_move_note

Move or rename a note in an Obsidian vault, optionally updating all incoming wiki and Markdown links to maintain cross-references.

Instructions

Move or rename a note. Optionally rewrites incoming wiki-links and Markdown links.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
vaultNoOptional configured vault name. Defaults to the server default vault.
fromYesVault-relative path. Absolute paths and traversal are rejected.
toYes
overwriteNo
updateLinksNo

Implementation Reference

  • src/tools.ts:566-583 (registration)
    Tool registration for 'obsidian_move_note' including its schema (vault, from, to, overwrite, updateLinks) and the inline handler function. The handler resolves source and destination paths, calls vaults.move() to perform the file rename, and optionally calls rewriteIncomingLinks() to update wiki and Markdown links across the vault.
    tool(
      "obsidian_move_note",
      "Move or rename a note. Optionally rewrites incoming wiki-links and Markdown links.",
      {
        vault: vaultArg,
        from: pathArg,
        to: pathArg,
        overwrite: z.boolean().optional().default(false),
        updateLinks: z.boolean().optional().default(true),
      },
      async (args) => {
        const from = vaults.notePath(args.from);
        const to = vaults.notePath(args.to);
        const moved = await vaults.move(from, to, args.vault, { overwrite: args.overwrite });
        const rewrites = args.updateLinks ? await rewriteIncomingLinks(vaults, args.vault, moved.from, moved.to) : [];
        return { ...moved, rewrites };
      },
    );
  • The rewriteIncomingLinks() helper function, called by the move_note handler. It scans all markdown files in the vault and rewrites any wiki-links ([[...]]) and Markdown links ([...](...)) from the old path to the new path.
    async function rewriteIncomingLinks(vaults: VaultManager, vault: string | undefined, fromPath: string, toPath: string): Promise<Array<{ path: string; changed: number }>> {
      const files = await vaults.markdownFiles(vault);
      const rewrites: Array<{ path: string; changed: number }> = [];
      const fromNoExt = fromPath.replace(/\.md$/i, "");
      const fromBase = path.posix.basename(fromNoExt);
      const toNoExt = toPath.replace(/\.md$/i, "");
      for (const file of files) {
        const read = await vaults.readText(file, vault);
        let changed = 0;
        let next = read.text.replace(/(!?)\[\[([^\]\n]+)\]\]/g, (full, embed: string, inner: string) => {
          const [targetAndSection, display] = inner.split("|", 2);
          const sectionMatch = /([#^].*)$/.exec(targetAndSection);
          const section = sectionMatch?.[1] ?? "";
          const target = targetAndSection.replace(/[#^].*$/, "").trim().replace(/\.md$/i, "");
          if (target !== fromNoExt && target !== fromBase && target !== fromPath) return full;
          changed += 1;
          return `${embed}[[${toNoExt}${section}${display ? `|${display}` : ""}]]`;
        });
        next = next.replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (full, label: string, target: string) => {
          const clean = decodeURIComponent(target).replace(/^\.\//, "");
          if (clean !== fromPath && clean !== fromNoExt && clean !== `${fromNoExt}.md`) return full;
          changed += 1;
          return `[${label}](${encodeURI(toPath)})`;
        });
        if (changed > 0) {
          await vaults.writeText(read.path, next, vault, { overwrite: true });
          rewrites.push({ path: read.path, changed });
        }
      }
      return rewrites;
    }
  • VaultManager.move() - the underlying filesystem operation for moving/renaming a note. Used directly by the obsidian_move_note handler. It resolves both paths, checks existence, creates parent directories, and performs fs.rename.
    async move(
      fromPath: string,
      toPath: string,
      vaultName?: string | null,
      options: { overwrite?: boolean } = {},
    ): Promise<{ from: string; to: string }> {
      this.assertWritable();
      const from = this.resolvePath(fromPath, vaultName);
      const to = this.resolvePath(toPath, vaultName);
      if (!fssync.existsSync(from.absolute)) throw new Error(`Source does not exist: ${from.relative}`);
      if (!options.overwrite && fssync.existsSync(to.absolute)) throw new Error(`Destination exists: ${to.relative}`);
      await fs.mkdir(path.dirname(to.absolute), { recursive: true });
      await fs.rename(from.absolute, to.absolute);
      this.onInvalidate?.(from.vault.name);
      return { from: from.relative, to: to.relative };
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate the tool is not read-only and not destructive, but the description does not elaborate on side effects beyond link rewriting. Missing details on behaviors like overwrite (deletion of destination) or permission requirements. Given minimal annotations, the description adds some value but is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence of 14 words succinctly conveys the core purpose and key option. No wasted words; front-loaded with the main action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description provides the essential purpose and link rewriting detail, but lacks explanation of overwrite behavior, path validation, or return values. For a tool with 5 parameters and no output schema, this is adequate but not fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description clarifies the updateLinks parameter by stating 'Optionally rewrites incoming wiki-links and Markdown links,' but does not add meaning to other parameters like overwrite or vault. With schema coverage at 40%, the description partially compensates but remains limited.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (move or rename) and the resource (note), and includes the optional link rewriting feature, which distinguishes it from sibling tools like obsidian_create_note or obsidian_delete_note.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description mentions optional link rewriting but does not provide explicit guidance on when to use this tool versus alternatives like obsidian_batch_rename or when not to use it. Usage context is implied but not explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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

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