obsidian-mcp-server

v0.12.3 pre-1.0

Agent-native TypeScript framework for building MCP servers. Build tools, not infrastructure. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.

public 14 tools 3 resources protocol 2026-07-28
macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922
claude mcp add --transport http obsidian-mcp-server http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922
codex mcp add obsidian-mcp-server --url http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922
{
  "mcpServers": {
    "obsidian-mcp-server": {
      "url": "http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922"
    }
  }
}
gemini mcp add --transport http obsidian-mcp-server http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922
{
  "mcpServers": {
    "obsidian-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922"
      ]
    }
  }
}
{
  "mcpServers": {
    "obsidian-mcp-server": {
      "type": "http",
      "url": "http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922"
    }
  }
}
curl -X POST http://macbook-pro-2.taildb2c94.ts.net/mcp/5601433546c4dbdd46053d6c401724352ff582c07d827922 \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

14

read 4

obsidian_get_note

Read a note from the vault — by path, the active file, or a periodic note. Choose a `format` projection: raw body, full object, structural document map, or a single section.

read
scope read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "obsidian_get_note",
    "arguments": {
      "format": "<format>",
      "target": "<target>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "enum": [
        "content",
        "full",
        "document-map",
        "section"
      ],
      "description": "Which projection to return. `content` — raw markdown body. `full` — content plus parsed frontmatter, tags, and file metadata. `document-map` — catalog of headings, block IDs, and frontmatter field names (use to discover patch targets). `section` — a single heading, block, or frontmatter section (requires `section`); heading sections include the full subtree under that heading and use `Parent::Child` syntax for nesting."
    },
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "section": {
      "description": "Required when `format` is `\"section\"`. Identifies the heading/block/frontmatter to extract.",
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "heading",
            "block",
            "frontmatter"
          ],
          "description": "Heading by name, block by reference, or frontmatter field by key."
        },
        "target": {
          "type": "string",
          "minLength": 1,
          "description": "Heading name — either the full path (`\"Parent::Child\"`) or a bare leaf name that matches exactly one heading — a block reference without the leading caret (e.g. \"2d9b4a\", not \"^2d9b4a\"), or a frontmatter field name."
        }
      },
      "required": [
        "type",
        "target"
      ],
      "additionalProperties": false
    },
    "includeLinks": {
      "default": false,
      "description": "When true with `format: \"full\"`, parses outgoing wiki and markdown link references from the note body. Skipped for other formats.",
      "type": "boolean"
    }
  },
  "required": [
    "format",
    "target",
    "includeLinks"
  ],
  "additionalProperties": false
}

obsidian_list_notes

List notes and subdirectories at a vault path. Defaults to the vault root when `path` is omitted. Tune recursion with `depth`, or filter the walk with `extension` / `nameRegex`. Capped at 1000 entries per call — when reached, walking stops and `excluded` is set; narrow `path` or tighten filters to surface the rest.

read
scope read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "obsidian_list_notes",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "path": {
      "description": "Vault-relative directory path. Omit for the vault root.",
      "type": "string"
    },
    "extension": {
      "description": "Only include files matching this extension, with or without leading dot. Applies to files only — directories are returned regardless.",
      "type": "string"
    },
    "nameRegex": {
      "description": "Optional ECMAScript regex (no flags, ≤256 chars, no nested quantifiers like `(a+)+`) applied to entry names. Matches both files and directories; directories that fail the regex are skipped without recursing into them.",
      "type": "string"
    },
    "depth": {
      "default": 2,
      "description": "How many directory levels to walk. `1` = target directory only (no recursion); `2` = target plus its immediate children — a structural overview; bump higher to drill in. Prefer narrowing `path` to a subdirectory over a high `depth` on the vault root.",
      "type": "integer",
      "minimum": 1,
      "maximum": 20
    }
  },
  "required": [
    "depth"
  ],
  "additionalProperties": false
}

obsidian_list_tags

List the vault's tags with usage counts, ordered by count descending and capped at `limit` (default 200) — the response says so when it withheld any. Includes hierarchical parents: `work/tasks` contributes to both `work` and `work/tasks`. Narrow with `nameRegex` for a known prefix or `minCount` to drop the single-use tail. To find notes by tag, use `obsidian_search_notes` in jsonlogic mode (e.g. `{"in": ["work", {"var": "tags"}]}`).

read
scope read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "obsidian_list_tags",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "nameRegex": {
      "description": "Optional ECMAScript regex (no flags, ≤256 chars, no nested quantifiers like `(a+)+`) matched against the bare tag name (no leading `#`). Hierarchical tags like `work/tasks` are matched as the full string. Use character classes (`[Mm]cp`) for case-insensitivity.",
      "type": "string"
    },
    "minCount": {
      "description": "Keep only tags used at least this many times. Omit (or pass 0) for no filter. On a vault with a long single-use tail this drops it in one argument.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 200,
      "description": "Maximum tags to return, applied after `nameRegex` and `minCount` and after ordering by count descending, so the cap keeps the most-used. Max 10000 — high enough to return any real vault whole. When the cap bites, the response reports `truncated`, `shown`, and `cap`.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10000
    }
  },
  "required": [
    "limit"
  ],
  "additionalProperties": false
}

obsidian_search_notes

Search the vault by text substring or JSONLogic predicate. Pick the mode that matches the query shape. Results paginate via opaque cursors: omit `cursor` for the first page, then pass `nextCursor` from the prior response. Text-mode hits additionally clip per file at `maxMatchesPerHit`.

read
scope read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "obsidian_search_notes",
    "arguments": {
      "mode": "<mode>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "mode": {
      "type": "string",
      "enum": [
        "text",
        "jsonlogic"
      ],
      "description": "Which search algorithm to run. `text` matches a substring case-insensitively across filenames and note bodies, returning surrounding context windows. `jsonlogic` evaluates a JSONLogic tree against each note, with `var` paths into `path`, `content`, `frontmatter.<key>`, `tags`, and `stat.{ctime,mtime,size}`, plus `glob` and `regexp` operators — both take their arguments as `[PATTERN, VALUE]`, so the pattern comes first and the `{\"var\": ...}` reference second."
    },
    "query": {
      "description": "The query string. Required for `text` and `omnisearch` modes; ignored in `jsonlogic` mode (use `logic` instead — this field must be a string, so passing a JSONLogic tree here is rejected).",
      "type": "string"
    },
    "logic": {
      "description": "JSONLogic tree. Required for `jsonlogic` mode; ignored in `text` and `omnisearch` modes (use `query` instead — this field must be an object, so passing a string here is rejected). `glob` and `regexp` take `[PATTERN, VALUE]` — pattern first: `{\"glob\": [\"Projects/*.md\", {\"var\": \"path\"}]}`. Backlinks (\"what links here\") have no dedicated tool or upstream endpoint but are expressible this way: `{\"regexp\": [\"\\\\[\\\\[Target Note(\\\\||#|\\\\]\\\\])\", {\"var\": \"content\"}]}` finds every note whose body wikilinks `Target Note`, in plain, aliased, or section form. `obsidian_get_note` with `includeLinks: true` covers the outgoing direction.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "contextLength": {
      "default": 100,
      "description": "Characters of context on each side of the match (text mode only). Sizes the rendered text as well as the structured payload, so a wide window multiplies the response across every match on the page.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "pathPrefix": {
      "description": "Filter returned filenames by prefix (text mode only, applied after matching — does not narrow the search itself).",
      "type": "string"
    },
    "maxMatchesPerHit": {
      "default": 10,
      "description": "Cap on match contexts returned per file in text mode. When clipped, the hit carries `truncated: true` and `totalMatches`.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "cursor": {
      "description": "Opaque cursor from a prior response. Omit for the first page. Page size is server-determined; do not assume a fixed value.",
      "type": "string"
    }
  },
  "required": [
    "mode",
    "contextLength",
    "maxMatchesPerHit"
  ],
  "additionalProperties": false
}

unspecified 1

obsidian_open_in_ui

open-world

Open a file in the Obsidian app UI. By default fails when the path does not exist; the `failIfMissing` flag controls the open-or-create behavior. Opening an existing file needs read access; opening a missing one creates it, so that case needs write access to the path.

unspecified
scope write
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "obsidian_open_in_ui",
    "arguments": {
      "path": "<path>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "path": {
      "type": "string",
      "minLength": 1,
      "description": "Vault-relative path of the file to open."
    },
    "failIfMissing": {
      "default": true,
      "description": "When true (default), fails if the file does not exist. When false, allows Obsidian to create the file on open — which requires write access to the path and is rejected in read-only mode.",
      "type": "boolean"
    },
    "newLeaf": {
      "default": false,
      "description": "Open in a new leaf (split pane) instead of the active one.",
      "type": "boolean"
    }
  },
  "required": [
    "path",
    "failIfMissing",
    "newLeaf"
  ],
  "additionalProperties": false
}

disabled 9

obsidian_write_note

Create or overwrite a note. With `section`, replaces just that heading/block/frontmatter section in place — use `obsidian_get_note` with `format: "document-map"` to discover available targets. A nested heading may be named either by its full `Parent::Child` path or by a bare leaf name that matches exactly one heading; a leaf shared by several headings is rejected with `ambiguous_section`. Whole-file writes fail with `file_exists` against an existing note unless `overwrite: true` — for in-place edits, prefer `obsidian_patch_note` (sections), `obsidian_append_to_note` (append), or `obsidian_replace_in_note` (find-and-replace). For heading sections, `content` is the new body; the heading line is preserved automatically.

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "content": {
      "type": "string",
      "description": "Body to write. For heading sections, the new section body — do not repeat the heading line (it stays in place). Markdown unless `contentType` is `json`."
    },
    "section": {
      "description": "Optional sub-document target. When set, only this section is replaced; rest of the note is untouched.",
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "heading",
            "block",
            "frontmatter"
          ],
          "description": "Heading by name, block by reference, or frontmatter field by key."
        },
        "target": {
          "type": "string",
          "minLength": 1,
          "description": "Heading name — either the full path (`\"Parent::Child\"`) or a bare leaf name that matches exactly one heading — a block reference without the leading caret (e.g. \"2d9b4a\", not \"^2d9b4a\"), or a frontmatter field name."
        }
      },
      "required": [
        "type",
        "target"
      ],
      "additionalProperties": false
    },
    "contentType": {
      "default": "markdown",
      "description": "Content body format. Use \"json\" for typed frontmatter values or block-targeted table rows. JSON values must be valid JSON literals — strings need quoting (`\"\\\"draft\\\"\"`, not `\"draft\"`), and numbers/booleans/arrays/objects pass through as-is.",
      "type": "string",
      "enum": [
        "markdown",
        "json"
      ]
    },
    "overwrite": {
      "default": false,
      "description": "Whole-file mode only (ignored when `section` is set). When `false` (default), the call fails with `file_exists` if the target note already exists — read it first and use `obsidian_patch_note` / `obsidian_append_to_note` / `obsidian_replace_in_note` for in-place edits, or retry with `overwrite: true` for a deliberate full replacement.",
      "type": "boolean"
    }
  },
  "required": [
    "target",
    "content",
    "contentType",
    "overwrite"
  ],
  "additionalProperties": false
}

obsidian_append_to_note

Append content to a note. **Without `section`: appends to the end of the file, or creates the file if it does not exist (your content becomes the full file).** With `section`: appends to the end of that heading/block/frontmatter — use `obsidian_get_note` with `format: "document-map"` to discover available targets. A nested heading may be named either by its full `Parent::Child` path or by a bare leaf name that matches exactly one heading; a leaf shared by several headings is rejected with `ambiguous_section`. For block-reference targets, content is concatenated adjacent to the block line without inserting a separator — include a leading newline in `content` if you want one. Set `createTargetIfMissing` to bring the target section into existence rather than failing when it does not exist.

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "content": {
      "type": "string",
      "description": "Body to append. Markdown unless `contentType` is `json`."
    },
    "section": {
      "description": "Optional sub-document target. When set, content is appended to that section instead of the file.",
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "heading",
            "block",
            "frontmatter"
          ],
          "description": "Heading by name, block by reference, or frontmatter field by key."
        },
        "target": {
          "type": "string",
          "minLength": 1,
          "description": "Heading name — either the full path (`\"Parent::Child\"`) or a bare leaf name that matches exactly one heading — a block reference without the leading caret (e.g. \"2d9b4a\", not \"^2d9b4a\"), or a frontmatter field name."
        }
      },
      "required": [
        "type",
        "target"
      ],
      "additionalProperties": false
    },
    "contentType": {
      "default": "markdown",
      "description": "Content body format. Use \"json\" for typed frontmatter values or block-targeted table rows. JSON values must be valid JSON literals — strings need quoting (`\"\\\"draft\\\"\"`, not `\"draft\"`), and numbers/booleans/arrays/objects pass through as-is.",
      "type": "string",
      "enum": [
        "markdown",
        "json"
      ]
    },
    "createTargetIfMissing": {
      "default": false,
      "description": "When `section` is provided, create the section if it does not already exist (otherwise the call fails when the section is missing).",
      "type": "boolean"
    }
  },
  "required": [
    "target",
    "content",
    "contentType",
    "createTargetIfMissing"
  ],
  "additionalProperties": false
}

obsidian_patch_note

Edit a heading, block reference, or frontmatter field in place — append to, prepend to, or replace the target's body. Use `obsidian_get_note` with `format: "document-map"` to discover available targets first. A nested heading may be named either by its full `Parent::Child` path or by a bare leaf name that matches exactly one heading; a leaf shared by several headings is rejected with `ambiguous_section`.

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "section": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "heading",
            "block",
            "frontmatter"
          ],
          "description": "Heading by name, block by reference, or frontmatter field by key."
        },
        "target": {
          "type": "string",
          "minLength": 1,
          "description": "Heading name — either the full path (`\"Parent::Child\"`) or a bare leaf name that matches exactly one heading — a block reference without the leading caret (e.g. \"2d9b4a\", not \"^2d9b4a\"), or a frontmatter field name."
        }
      },
      "required": [
        "type",
        "target"
      ],
      "additionalProperties": false,
      "description": "Which heading/block/frontmatter field to edit."
    },
    "operation": {
      "type": "string",
      "enum": [
        "append",
        "prepend",
        "replace"
      ],
      "description": "How to apply `content` relative to the targeted section. `append` — at the end of the target's body (for headings, before the next sibling/parent heading; for frontmatter array fields, as a new array item). `prepend` — at the start. `replace` — swaps the target's body."
    },
    "content": {
      "type": "string",
      "description": "Body to insert/replace. Markdown unless `contentType` is `json`."
    },
    "contentType": {
      "default": "markdown",
      "description": "Content body format. Use \"json\" for typed frontmatter values or block-targeted table rows. JSON values must be valid JSON literals — strings need quoting (`\"\\\"draft\\\"\"`, not `\"draft\"`), and numbers/booleans/arrays/objects pass through as-is.",
      "type": "string",
      "enum": [
        "markdown",
        "json"
      ]
    },
    "patchOptions": {
      "description": "Optional flags: createTargetIfMissing, applyIfContentPreexists, trimTargetWhitespace.",
      "type": "object",
      "properties": {
        "createTargetIfMissing": {
          "default": false,
          "description": "Create the target heading/block/frontmatter field if it does not exist.",
          "type": "boolean"
        },
        "applyIfContentPreexists": {
          "default": false,
          "description": "When false (default), the patch is rejected if the supplied content already appears in the target — idempotent against retries. Set to true to force-apply even when it would duplicate. Replace operations are never rejected.",
          "type": "boolean"
        },
        "trimTargetWhitespace": {
          "default": false,
          "description": "Trim whitespace from the target section before applying the operation.",
          "type": "boolean"
        }
      },
      "required": [
        "createTargetIfMissing",
        "applyIfContentPreexists",
        "trimTargetWhitespace"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "target",
    "section",
    "operation",
    "content",
    "contentType"
  ],
  "additionalProperties": false
}

obsidian_replace_in_note

Search and replace inside a single note, literally or by regex. Replacements run in array order, each over the previous one's output, and cover the note body only unless `scope` says otherwise — the YAML frontmatter block is left byte-identical by default, because a prose edit that lands in a scalar can silently invalidate the whole block. Use for edits that don't fit `obsidian_patch_note`'s structural targets — e.g., body-wide find-and-replace. Prefer `obsidian_manage_frontmatter` for typed edits to a single property.

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "scope": {
      "default": "body",
      "description": "Which part of the note the replacements run over. `body` leaves the YAML frontmatter block byte-identical. `frontmatter` and `both` also rewrite the YAML between the `---` fences — never the fences themselves — and re-parse it afterwards, failing the whole call and writing nothing if the result no longer parses. That check catches YAML that breaks, not YAML that still parses while meaning something else: a replacement that renames a key or strips a scalar's quotes passes it.",
      "type": "string",
      "enum": [
        "body",
        "frontmatter",
        "both"
      ]
    },
    "replacements": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "search": {
            "type": "string",
            "minLength": 1,
            "description": "Substring or regex pattern to match."
          },
          "replace": {
            "type": "string",
            "description": "Replacement text. Empty string deletes matches."
          },
          "useRegex": {
            "default": false,
            "description": "Treat `search` as an ECMAScript regex pattern (≤1024 chars, no nested quantifiers like `(a+)+` — catastrophic-backtracking guard).",
            "type": "boolean"
          },
          "caseSensitive": {
            "default": true,
            "description": "When false, match case-insensitively.",
            "type": "boolean"
          },
          "wholeWord": {
            "default": false,
            "description": "Match only at word boundaries. Applies in both literal and regex modes — the search pattern is wrapped with `\\b…\\b`.",
            "type": "boolean"
          },
          "flexibleWhitespace": {
            "default": false,
            "description": "Treat any run of whitespace in `search` as matching any whitespace in the body. Literal mode only — has no effect when `useRegex: true` (express it directly with `\\s+`).",
            "type": "boolean"
          },
          "replaceAll": {
            "default": true,
            "description": "When false, only the first match is replaced.",
            "type": "boolean"
          }
        },
        "required": [
          "search",
          "replace",
          "useRegex",
          "caseSensitive",
          "wholeWord",
          "flexibleWhitespace",
          "replaceAll"
        ],
        "additionalProperties": false,
        "description": "A single search/replace operation."
      },
      "description": "Replacements to apply in array order over the evolving content."
    }
  },
  "required": [
    "target",
    "scope",
    "replacements"
  ],
  "additionalProperties": false
}

obsidian_manage_frontmatter

Get, set, or delete a single frontmatter key on a note, atomically. `set` requires a JSON-typed `value` (string, number, boolean, array, or object).

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "operation": {
      "type": "string",
      "enum": [
        "get",
        "set",
        "delete"
      ],
      "description": "Operation to perform on `key`. `get` — read the current value. `set` — write `value`, creating the key if absent. `delete` — remove the key from frontmatter."
    },
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "key": {
      "type": "string",
      "minLength": 1,
      "description": "Frontmatter field name."
    },
    "value": {
      "description": "Required when `operation` is `\"set\"`. JSON-typed value to write — strings, numbers, booleans, arrays, and objects all accepted."
    }
  },
  "required": [
    "operation",
    "target",
    "key"
  ],
  "additionalProperties": false
}

obsidian_manage_tags

Add, remove, or list a note's tags. Defaults to the frontmatter `tags:` array — set `location` to `inline` or `both` to mutate the note body. `add` ensures the tag is present in the requested location(s); `remove` strips it; `both` reconciles across both representations. Inline `#tag` occurrences inside fenced code blocks are intentionally left alone, and inline-location additions append the new tag at end-of-file. `list` ignores the input `tags` array.

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Where the note lives."
    },
    "operation": {
      "type": "string",
      "enum": [
        "add",
        "remove",
        "list"
      ],
      "description": "`add` and `remove` mutate the note; `list` reads the current tag set."
    },
    "tags": {
      "description": "Tags to add or remove. Omit the leading `#`. Required for add/remove.",
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "location": {
      "default": "frontmatter",
      "description": "Where to apply the change. Defaults to `frontmatter` (the canonical Obsidian tag location, leaves the body untouched). `inline` mutates the note body — `add` appends `#tag` at end-of-file. `both` is opt-in reconciliation of frontmatter and inline tag locations.",
      "type": "string",
      "enum": [
        "frontmatter",
        "inline",
        "both"
      ]
    }
  },
  "required": [
    "target",
    "operation",
    "location"
  ],
  "additionalProperties": false
}

obsidian_delete_note

Permanently delete a note from the vault. Asks the user to confirm before deleting — the call is answered with a confirmation request and retried with the answer. Recovery requires the local trash in Obsidian — there is no API-level undo.

disabledwould be destructive
scope write

Disabled. Disabled by OBSIDIAN_READ_ONLY=true.

Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "path",
              "description": "Address by vault-relative path."
            },
            "path": {
              "type": "string",
              "minLength": 1,
              "description": "Vault-relative path including extension, e.g. \"Projects/foo.md\"."
            }
          },
          "required": [
            "type",
            "path"
          ],
          "additionalProperties": false,
          "description": "Address a note by its vault-relative path."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "active",
              "description": "Address the file currently open in Obsidian."
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Address whichever file is currently active in the Obsidian UI."
        },
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "const": "periodic",
              "description": "Address a daily/weekly/monthly/etc. periodic note."
            },
            "period": {
              "type": "string",
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "quarterly",
                "yearly"
              ],
              "description": "Periodic note granularity."
            },
            "date": {
              "description": "ISO date YYYY-MM-DD. Omit for the current period.",
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          },
          "required": [
            "type",
            "period"
          ],
          "additionalProperties": false,
          "description": "Address a periodic note (current or dated)."
        }
      ],
      "description": "Which note to delete."
    }
  },
  "required": [
    "target"
  ],
  "additionalProperties": false
}

obsidian_list_commands

List the Obsidian command-palette commands available in the active vault, with their IDs and display names. Filter to a subset with the optional `nameRegex` matched against the display name.

disabledwould be read
scope read

Disabled. Disabled by OBSIDIAN_READ_ONLY=true (commands can mutate).

Unset OBSIDIAN_READ_ONLY to allow commands; OBSIDIAN_ENABLE_COMMANDS=true is also required.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "nameRegex": {
      "description": "Optional ECMAScript regex (no flags, ≤256 chars, no nested quantifiers like `(a+)+`) matched against the command display name (the field agents search by, not the slug-shaped `id`). Use character classes (`[Tt]emplater`) for case-insensitivity.",
      "type": "string"
    }
  },
  "additionalProperties": false
}

obsidian_execute_command

open-world

Execute an Obsidian command by ID (from `obsidian_list_commands`). Behaviour depends on the command — some are destructive (delete file, close vault), some open UI. Commands run with the same authority as a user invoking them from the keyboard.

disabledwould be destructive
scope admin

Disabled. Disabled by OBSIDIAN_READ_ONLY=true (commands can mutate).

Unset OBSIDIAN_READ_ONLY to allow commands; OBSIDIAN_ENABLE_COMMANDS=true is also required.
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "commandId": {
      "type": "string",
      "minLength": 1,
      "description": "Command ID, e.g. \"editor:save-file\". Use obsidian_list_commands to discover."
    }
  },
  "required": [
    "commandId"
  ],
  "additionalProperties": false
}

Resources

3

A note in the Obsidian vault. Returns the parsed note — content, frontmatter, tags, and stat — so clients can attach a specific note to a conversation.

uri obsidian://vault/{+path} mime application/json

All tags found in the Obsidian vault, with usage counts, in upstream order and uncapped — a full snapshot. Includes hierarchical parents (e.g. `work` for `work/tasks`). Use the `obsidian_list_tags` tool for a count-ranked, capped, filterable view.

uri obsidian://tags mime application/json

Server reachability, plugin version, and auth status of the Obsidian Local REST API. Reports the unauthenticated reachability info even when the API key is misconfigured; `authenticated` reflects whether the configured key is accepted by an authenticated probe.

uri obsidian://status mime application/json