Skip to main content
Glama
Obsidian MCP (pgvector + Ollama, self-hosted)

Obsidian MCP (pgvector + Ollama, self-hosted)

by maxkuminov

Edit note

edit_note
Destructive

Modify an existing Obsidian note: replace the whole body, append, find-and-replace text, or rewrite one section, with optional dry-run diff and hash precondition to block stale overwrites.

Instructions

Edit an existing note in the Obsidian vault. Requires write permission (a readwrite API key or OAuth scope).

Arguments: path, content, at most one mode flag — append, find or section (operation is a legacy alias) — plus replace_all, dry_run, replace_frontmatter, and the expected_hash precondition. Every refusal the server raises ends with one machine-readable MCP-REFUSAL {"code":…} line; an undeclared or mistyped argument is rejected by protocol validation before the tool runs and carries no such line. See get_vault_guide for vault conventions.

Modes (mutually exclusive):

  1. Full replace (default): content becomes the note's body; an existing valid line-1 YAML frontmatter block is preserved byte-identically ahead of it. replace_frontmatter=True overwrites the entire file, frontmatter included. A note with no valid block is replaced wholesale. Change frontmatter with set_frontmatter or find=; never write back read_note's frontmatter JSON view. The round-trip guarantee covers a complete, unwindowed whole-note read only — no section, offset=0, truncated false; page a truncated read to the end first, or full replacement replaces the whole body with the fragment. A section response belongs to section mode.

  2. Append: append=True; content is added at the end, after one newline.

  3. Find & replace: find=<exact text> is replaced with content; it must match exactly once unless replace_all=True.

  4. Section: section=<heading> replaces the whole body under the named ATX heading. Parent/Child disambiguates repeated headings; a #N ordinal ("#7", 1-based document order) is the only form that addresses duplicate siblings. A selector resolves to the same section here as in read_note on any write this tool admits — parity is about resolution, not admission.

dry_run=True returns a unified diff and writes nothing.

expected_hash: pass the content_hash a read returned, verbatim, and the call is refused with nothing written if the file changed since — in every mode, dry_run included. It is the whole file's hash, never a hash of the text you received. Optional by default — omitted, a stale edit overwrites — unless the deployment requires it, when an omitted hash is refused precondition_required. Codes: stale_precondition, concurrent_write, no_incumbent, malformed_precondition, precondition_unavailable, precondition_required. Success reports the content_hash of the bytes this call wrote, not what is on disk when you read it; dry_run reports none.

Section mode. content is the section's body: the text beginning on the line immediately after the matched heading line; the heading line is never rewritten. The body is replaced whole — anything content does not resend, a fenced code block under the heading included, is deleted — so a blank line you want after the heading belongs in content. read_note(path, section=...) is the matching read: its heading field carries the heading line and its content field the body, and this tool takes exactly that content. Byte-identity holds for notes whose body newlines are LF: non-LF terminators inside the selected body come back as LF. #N ordinals agree with read_note; a heading inside a fenced code block is not a heading.

Two shapes refuse a section write outright, naming the problem and writing nothing: a malformed frontmatter block, whose refusal names the replace_frontmatter=True repair; and a fence opener indented by one to three spaces that nothing below it closes — close the fence or unindent it to column zero, then reissue. Both are asymmetric with reads: read_note keeps working on such notes. A symlinked final component is refused.

Input Schema

NameRequiredDescriptionDefault
findNo
pathYes
appendNo
contentYes
dry_runNo
sectionNo
operationNo
replace_allNo
expected_hashNo
replace_frontmatterNo

Output Schema

NameRequiredDescriptionDefault
resultYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv0.8.2
    • addedInput schema / additionalProperties
      Added value: +false
  2. Changed1 schema field changedv0.8.1
    • addedInput schema / properties / expected_hash
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "title": "Expected Hash"
      +}
  3. Changed1 schema field changedv0.7.2
    • addedInput schema / properties / replace_frontmatter
      Added value: +{
      +  "default": false,
      +  "title": "Replace Frontmatter",
      +  "type": "boolean"
      +}
  4. Addedv0.7.0
  5. Removedv0.5.4
  6. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Goes far beyond the annotations: it discloses exactly what is destroyed (the section body is replaced whole, including fenced code blocks; a fragment read replaces the whole body), what is preserved (line-1 YAML frontmatter byte-identically), the two shapes that refuse a section write outright, the symlink refusal, and the full refusal-code vocabulary with the distinction that protocol-validation errors carry no `MCP-REFUSAL` line. This is precisely the destructive-mutation context an agent needs and annotations cannot carry.

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

Conciseness4/5

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

Front-loaded and organized: purpose, then arguments, then numbered modes, then per-flag paragraphs in bold. Length is largely earned given ten parameters, four modes, and destructive semantics, but the dedicated "Section mode" paragraph substantially re-covers ground already laid out in mode 4, and the round-trip guarantee passage is dense enough to need a second read.

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

Completeness5/5

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

An output schema exists, so return values need only the one clause provided (success reports the hash of the bytes written; `dry_run` reports none). Given the tool's complexity — destructive whole-body writes, mode dispatch, preconditions, asymmetric read/write failure modes — the description leaves nothing material unaddressed apart from the `path` format.

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

Parameters4/5

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

With 0% schema description coverage, the description carries the full burden and does so for nine of ten parameters: mode flags and their mutual exclusivity, `operation` as a legacy alias, `replace_all`'s exactly-once requirement, `dry_run`'s diff return, `replace_frontmatter`'s whole-file overwrite, and a detailed `expected_hash` contract (whole-file hash, not the returned text's hash). The gap is `path`, whose expected form (vault-relative? extension included?) is never stated.

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?

States a specific verb and resource ("Edit an existing note in the Obsidian vault") and immediately frames the four mutually exclusive modes, which is what actually determines the tool's shape. It also names the sibling to use for a nearby task ("Change frontmatter with `set_frontmatter`"), so an agent can discriminate it from `set_frontmatter`, `write_file`, and `create_note` without opening a schema.

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

Usage Guidelines5/5

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

Explicit when-to-use for each mode, explicit when-not-to (never write back `read_note`'s frontmatter JSON view; use `set_frontmatter` or `find=`), an explicit prerequisite ("Requires write permission (a `readwrite` API key or OAuth scope)"), and explicit pointers to `read_note` and `get_vault_guide`. The `dry_run` and `expected_hash` guidance further tells the agent when to prefer a safer call shape.

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