Skip to main content
Glama
README.md
# obsidian-mcp

An MCP server that gives an AI agent read and write access to a folder of
Markdown notes. Point it at an Obsidian vault, a Logseq graph, or any notes
directory.

Four tools, no dependencies beyond the MCP SDK, one file.

```
search_notes   full-text search with surrounding context
read_note      read one note by relative path
list_notes     enumerate notes, optionally under a subfolder
write_note     create a note, or append to an existing one
```

## Setup

```bash
pip install -r requirements.txt
```

Register it with Claude Code by adding a `.mcp.json` at the root of your
project:

```json
{
  "mcpServers": {
    "vault": {
      "command": "python",
      "args": ["/path/to/server.py"],
      "env": { "VAULT_ROOT": "/path/to/your/vault" }
    }
  }
}
```

Restart your session. You'll be asked to approve the server on first use —
it can write files, so that prompt is doing real work.

## Configuration

| variable | default | meaning |
|---|---|---|
| `VAULT_ROOT` | current directory | the folder to expose |
| `VAULT_READONLY` | unset | set to `1` to drop `write_note` entirely |
| `VAULT_MAX_RESULTS` | 20 | search result cap |
| `VAULT_MAX_BYTES` | 100000 | truncation limit for `read_note` |

`VAULT_READONLY=1` removes the write tool from the advertised list rather than
just refusing calls, so an agent never sees a capability it can't use.

## Two things worth knowing

**Path traversal is checked properly.** Every caller-supplied path is resolved
and compared against the vault root:

```python
p = (VAULT_ROOT / rel).resolve()
if not p.is_relative_to(VAULT_ROOT):
    raise ValueError(...)
```

Resolve-then-compare, not a string prefix test — `..` and symlinks both defeat
string comparison, and without this `../../.ssh/id_rsa` is a valid note path.

**Errors are returned, not raised.** A tool that throws takes the server down,
and a dead MCP server is a *silent* one — the agent simply stops having the
capability and carries on without mentioning it. Returning the error text means
the agent can see what went wrong and tell you.

## Scope

Deliberately small. No embeddings, no vector index, no frontmatter parsing, no
graph traversal. Full-text search over Markdown covers most of what an agent
actually needs, and it has no index to rebuild or fall out of date.

Search returns one hit per note, which keeps results readable when a term
appears forty times in one file.

## Licence

MIT