mcp-obsidian
# MCP Server for Obsidian
An MCP server for interacting with a local Obsidian vault via natural language, exposing read and write operations on the vault's notes (`.md`) to MCP clients (Claude Desktop, Claude Code, VS Code Copilot, Cursor, and compatible hosts).
Direct filesystem access to the vault — does not depend on the Obsidian app being open or on any installed plugin (e.g. Local REST API).
## Security guardrails
Every tool goes through `resolveNotePath()` before touching the filesystem. That function rejects:
- absolute paths, drive letters (`C:\...`) and UNC paths (`\\...`);
- traversal (`..`) in any path segment, even disguised inside subfolders;
- hidden segments (`.obsidian`, `.git`, `.trash`, etc.) — protects Obsidian's internal config and version control;
- any extension other than `.md`.
In addition:
- `create_note` does not overwrite by default — requires explicit `overwrite: true`;
- `delete_note` requires explicit `confirm: true`, even with a valid `path`;
- listing, search, and backlinks ignore hidden folders and `node_modules`.
These validations limit the model's blast radius to `.md` files inside the configured vault, even in the face of a malicious prompt or a model hallucination trying to escape scope.
## Available Tools
| Tool | Description |
|---|---|
| `list_notes` | Lists `.md` notes in the vault, optionally restricted to a subfolder |
| `read_note` | Reads a note: frontmatter, body, and wikilinks found |
| `search_notes` | Searches text across all notes, returning file/line/snippet |
| `get_backlinks` | Lists notes that reference the given note via `[[wikilink]]` |
| `create_note` | Creates a new note (fails if it already exists, unless `overwrite: true`) |
| `append_to_note` | Appends text to the end of an existing note |
| `delete_note` | Deletes a note (requires `confirm: true`) |
## Requirements
- Node.js 18+
- A local Obsidian vault (any folder of `.md` files — the Obsidian app does not need to be open)
## Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
| `OBSIDIAN_VAULT_PATH` | Yes | — | Absolute path to the local Obsidian vault |
| `MCP_TRANSPORT` | No | `stdio` | Transport mode: `stdio` (default, for `npx`/Claude Desktop/VS Code) or `http` (Streamable HTTP, for Docker/remote clients) |
| `MCP_HTTP_PORT` | No | `3004` | HTTP server port (only used when `MCP_TRANSPORT=http`) |
| `MCP_HTTP_HOST` | No | `0.0.0.0` | HTTP server bind address (only used when `MCP_TRANSPORT=http`) |
## Usage
### Run directly from GitHub
```bash
npx github:ferronicardoso/mcp-obsidian
```
### Claude Code (CLI)
**PowerShell:**
```powershell
claude mcp add obsidian --scope user `
--env OBSIDIAN_VAULT_PATH="/my/obsidian/vault/path/" `
-- npx -y github:ferronicardoso/mcp-obsidian
```
`--scope` controls where the server registration is stored:
| Scope | Stored in | Visible to |
|---|---|---|
| `local` (default) | project-local, untracked | only you, only in this project |
| `project` | `.mcp.json` at the project root | anyone who clones the repository (commit it to share) |
| `user` | global Claude Code config | you, across all projects |
### Claude Desktop configuration
`%APPDATA%\Claude\claude_desktop_config.json`:
```json
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": ["github:ferronicardoso/mcp-obsidian"],
"env": {
"OBSIDIAN_VAULT_PATH": "/my/obsidian/vault/path/"
}
}
}
}
```
### VS Code MCP configuration
`.vscode/mcp.json`:
```json
{
"servers": {
"obsidian": {
"command": "npx",
"args": ["github:ferronicardoso/mcp-obsidian"],
"env": {
"OBSIDIAN_VAULT_PATH": "/my/obsidian/vault/path/"
}
}
}
}
```
### Run with Docker (HTTP transport)
The vault needs to be mounted as a volume inside the container; `OBSIDIAN_VAULT_PATH` must point to the path *inside* the container.
```powershell
docker run -d --name mcp-obsidian `
-p 3004:3004 `
-v "/my/obsidian/vault/path/:/vault:rw" `
-e OBSIDIAN_VAULT_PATH=/vault `
ghcr.io/ferronicardoso/mcp-obsidian:latest
```
The MCP endpoint is then available at `http://localhost:3004/mcp`.
## Local Development
```bash
git clone https://github.com/ferronicardoso/mcp-obsidian
cd mcp-obsidian
npm install
npm run build
```
Run the compiled server:
```powershell
$env:OBSIDIAN_VAULT_PATH = "/my/obsidian/vault/path/"
npm start
```
Run the tests:
```bash
npm test
```
## Build and Commit Workflow
This repository keeps `dist/` tracked to support `npx github:user/repo`.
A Husky `pre-commit` hook:
1. builds TypeScript (`npm run build`)
2. stages the generated artifacts (`git add dist`)
Manual fallback:
```bash
npm run build
git add dist
```
## Security Notes
- `OBSIDIAN_VAULT_PATH` must point to a real user vault — the server refuses to start if the path does not exist or is not a directory.
- All write tools (`create_note`, `append_to_note`, `delete_note`) operate exclusively inside the vault, validated by `resolveNotePath()`.
- There is no audit trail (who/when changed what) and no automatic versioning of changes. Before pointing it at a production vault, consider versioning the vault with Git or keeping backups.
## License
[MIT](LICENSE) © Raphael Augusto Ferroni Cardoso
TDQS
Scored across 7 tools
Each tool targets a clearly distinct operation: listing, reading, searching, backlink lookup, creating, appending, and deleting. The read operations are differentiated by what they return, and the write operations are cleanly separated by semantics.
All tool names follow a consistent verb_noun snake_case pattern (list_notes, read_note, search_notes, create_note, etc.). The naming style is uniform and predictable.
Seven tools is well-scoped for an Obsidian vault server. Each tool covers a necessary note operation without redundancy or bloat.
Core note lifecycle operations are covered: list, read, search, create, append, delete, and backlink discovery. Minor gaps like rename/move or direct edit-by-section exist, but they are workable via create with overwrite and append.