Skip to main content
Glama
obsidian-mcp-server

obsidian-mcp-server

by sonwit
README.md
# obsidian-mcp-server

An MCP server that gives AI agents read and write access to a single Obsidian vault. It works directly on the Markdown files in a folder and speaks Streamable HTTP. It is stateless, so every request gets its own server and transport.

## Tools

| Tool | What it does |
|---|---|
| `list_notes` | Lists notes and folders |
| `read_note` | Reads a note, with a version hash |
| `search_notes` | Text search in file names and contents |
| `create_note` | Creates a new note, fails if it already exists |
| `edit_note` | Replaces one exact piece of text (must be unique) |
| `append_to_note` | Adds text at the end, or under a heading |
| `replace_note` | Overwrites the whole note, requires the version from the latest read |
| `recent_changes` | Lists notes changed in the last N days, newest first |
| `week_progress` | Counts task checkboxes per note and per heading in a folder, with frontmatter |
| `archive_note` | Moves a note to the archive folder |
| `restore_note` | Moves a note from the archive folder to its original location |

Plus the resource `vault://index`, which lists every note.

Notes can be changed from other devices while an agent is working on them. `edit_note` fails if the text is no longer there, and `replace_note` fails if the version is stale. The agent then has to read the note again.

## About the vault

The server tells agents to read `About the vault.md` at the start of a session, if that note exists. Put it in the vault root and use it to describe the structure and conventions you want agents to follow: which folders exist, how tasks and links are written, and what agents may or may not change.

## Safeguards

- Only `.md` files
- No access to hidden files and folders (`.obsidian`, `.trash`)
- No `..` or absolute paths, and symlinks cannot point outside the vault
- No deleting. Notes can only be moved to and from `_arkiv/`
- Atomic writes, at most 1 MB per write
- The log shows which notes were touched, never their content

## Authorization

`/mcp` requires the header `Authorization: Bearer <token>`. The token is never accepted in the URL, and the server will not start without a valid `MCP_TOKEN`. `/healthz` is open and only responds with `{"ok":true}`.

## Configuration

Set as environment variables. See `.env.example` for a template.

| Variable | Default | What it is |
|---|---|---|
| `VAULT_ROOT` | (required) | Path to the vault folder |
| `MCP_TOKEN` | (required) | At least 32 characters, only `A-Z`, `a-z`, `0-9`, `_` and `-`. Generate one with `openssl rand -hex 32` |
| `HOST` | `127.0.0.1` | Address the server listens on |
| `PORT` | `3333` | Port the server listens on |
| `MAX_WRITE_BYTES` | `1000000` | Maximum size of a single write |

## Development

Requires Node 22.18 or newer. The code runs as TypeScript directly, with no build step.

```bash
npm install
cp .env.example .env     # fill in VAULT_ROOT and MCP_TOKEN
npm run dev
npm run typecheck
npm run smoke            # against a running server, creates _smoketest in the vault
npm run inspector        # Streamable HTTP, URL http://127.0.0.1:3333/mcp, header Authorization: Bearer ...
```

## License

MIT, see [LICENSE](LICENSE).