使用说明
mcp-obsidian
A Model Context Protocol (MCP) server for managing Markdown notes in Obsidian vaults with full Zettelkasten workflow support.
This is a Go rewrite inspired by mcp-obsidian using the MCP Go SDK v1.8.0.
Features
- Search Notes: Find notes by filename using case-insensitive matching with regex support
- Read Notes: Read the content of one or more notes with error handling per file
- Create Notes: Create notes with
YYYY-MM-DD_slug.mdnaming convention and YAML frontmatter - Update Notes: Replace or append content to existing notes
- Delete Notes: Safely remove markdown notes from the vault
- Search Content: Full-text search across note bodies with line numbers and snippets
- Get Backlinks: Discover all
[[wikilink]]references to a given note - List Tags: Collect and count tags from YAML frontmatter and inline
#tags - Structured Logging: Uses Go's standard
log/slogwith leveled, key-value output to stderr - Security: Built-in path validation to prevent directory traversal and access to hidden files
- Performance: Native Go static binary with zero CGO dependencies
Installation
From Source
git clone https://github.com/nsega/mcp-obsidian.git
cd mcp-obsidian
make build
The binary is written to build/mcp-obsidian.
Using go install
go install github.com/nsega/mcp-obsidian@latest
Usage
Command Line
Run the server with the path to your vault:
./build/mcp-obsidian /path/to/your/vault
The server communicates over stdin/stdout using the MCP protocol.
Configuration for Claude Desktop
Add the following to your Claude Desktop configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"obsidian": {
"command": "/path/to/mcp-obsidian",
"args": ["/path/to/your/vault"]
}
}
}
Configuration for Claude Code
Add to your ~/.claude/settings.json or project .claude/settings.json:
{
"mcpServers": {
"obsidian": {
"command": "/path/to/mcp-obsidian",
"args": ["/path/to/your/vault"]
}
}
}
Configuration for VS Code
Add to your .vscode/mcp.json:
{
"mcpServers": {
"obsidian": {
"command": "/path/to/mcp-obsidian",
"args": ["/path/to/your/vault"]
}
}
}
Tools
search_notes
Search for notes by filename using case-insensitive matching.
Input:
query(string, required): Search query or regex pattern
Example:
{
"query": "meeting"
}
Returns up to 200 matching file paths.
read_notes
Read the content of one or more notes.
Input:
paths(array of strings, required): File paths to read
Example:
{
"paths": [
"/path/to/vault/note1.md",
"/path/to/vault/note2.md"
]
}
Returns the content of each note with error handling for individual files.
create_note
Create a new note with the Zettelkasten naming convention and YAML frontmatter.
Input:
title(string, required): Title for the note (used in filename slug and heading)content(string, optional): Markdown body contentfolder(string, optional): Subfolder within the vault (e.g.30_Permanent,10_FleetingNote)tags(array of strings, optional): Frontmatter tags
Example:
{
"title": "GTD Zettelkasten Flowchart",
"content": "A note about combining GTD with Zettelkasten.",
"folder": "30_Permanent",
"tags": ["zettelkasten", "gtd", "productivity"]
}
Creates a file like 30_Permanent/2026-02-15_gtd-zettelkasten-flowchart.md with YAML frontmatter containing tags, created, and updated dates.
update_note
Update an existing note's content.
Input:
path(string, required): Full path to the notecontent(string, required): New content to write or appendmode(string, optional):replace(default) orappend
Example:
{
"path": "/path/to/vault/30_Permanent/2026-02-15_my-note.md",
"content": "\n## New Section\nAdditional thoughts.",
"mode": "append"
}
delete_note
Delete a markdown note from the vault.
Input:
path(string, required): Full path to the note to delete
Example:
{
"path": "/path/to/vault/00_Inbox/2026-02-15_scratch.md"
}
Only .md files can be deleted. Directories cannot be deleted.
search_content
Full-text search across note bodies. Returns matching file paths, line numbers, and snippets.
Input:
query(string, required): Search query or regex pattern
Example:
{
"query": "Zettelkasten"
}
Returns up to 200 matches with file path, line number, and the matching line content.
get_backlinks
Find all notes that link to a given note via [[wikilinks]].
Input:
note_name(string, required): Note name without.mdextension or path
Example:
{
"note_name": "my-permanent-note"
}
Returns source file paths and the lines containing the wikilinks. Supports both [[note]] and [[note|alias]] syntax.
list_tags
List all tags found across the vault from YAML frontmatter and inline #tags.
Input:
prefix(string, optional): Filter tags by prefix
Example:
{
"prefix": "project"
}
Returns a sorted, deduplicated list of tags with their occurrence counts.
Security
The server implements several security measures:
- Path Validation: All file operations are restricted to the specified vault directory
- Hidden Files: Access to files and directories starting with
.is denied - Symlink Resolution: Symlinks are resolved and validated to prevent directory escape attacks
- Error Handling: Individual file read failures don't halt operations (logged at
Debuglevel viaslog)
Project Structure
main.go CLI entry point, slog setup, wiring
internal/
├── note/
│ ├── types.go Input/Output structs for all 8 tools
│ ├── util.go Slugify, GenerateFrontmatter, ParseFrontmatterTags
│ └── util_test.go
├── vault/
│ ├── vault.go Vault struct, path validation, symlink resolution
│ └── vault_test.go
├── handler/
│ ├── handler.go 8 MCP tool handler methods (with injected logger)
│ └── handler_test.go
├── server/
│ └── server.go MCP server creation, tool registration, logger passthrough
└── testutil/
└── testutil.go Shared test vault setup/cleanup helpers
Development
Prerequisites
- Go 1.27.3 or later (the
godirective ingo.modis authoritative) - golangci-lint 2.15.0 or later (for linting; CI runs exactly the
GOLANGCI_LINT_VERSIONpin in theMakefileandmake lintchecks you have at least it; the comment above the pin explains why it moves with the Go version)
Build
make build # Build binary to build/mcp-obsidian
make build-all # Cross-compile for linux/darwin/windows (amd64/arm64)
Test
make test # Run tests with race detector (-short: skips the large-frame transport test)
make test-full # Run all tests, including the large-frame transport test (CI runs the full suite too)
make coverage # Run tests with HTML coverage report
Lint
make lint # Run golangci-lint
make check # Run fmt + vet + lint + test
Request size limits
Note content is limited to 10 MiB per call (vault.MaxContentSize) and the JSON-encoded arguments of any tool call to about 61 MiB (server.MaxArgumentsSize); both are answered with a tool error. The stdio transport additionally caps a single inbound JSON-RPC frame at about 65 MiB (server.MaxFrameSize, the arguments bound plus headroom; approximate, since the SDK does not charge read-ahead from a pipelined next frame). A frame beyond that is not decodable, so the server ends the session and exits with server error and max_frame_bytes in the log; the limit exists to bound memory per frame and is a constant in internal/server.
Run
make run VAULT=/path/to/your/vault
Run make help for the full list of targets.
Testing with the MCP Inspector
The easiest way to interactively test the server is with the official MCP Inspector:
npx @modelcontextprotocol/inspector ./build/mcp-obsidian ~/my-vault
This opens a web UI at http://localhost:5173 where you can test all 8 tools and view the JSON-RPC messages.
Manual Testing with JSON-RPC
You can also test by piping JSON-RPC messages via stdin:
(printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}\n'; \
sleep 0.5; \
printf '{"jsonrpc":"2.0","method":"notifications/initialized"}\n'; \
sleep 0.3; \
printf '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}\n'; \
sleep 1) | ./build/mcp-obsidian ~/my-vault 2>/dev/null
Troubleshooting
Server doesn't start:
- Verify the vault path exists and is readable
- Ensure the binary is executable:
chmod +x build/mcp-obsidian
Tools not appearing in Claude Desktop / Claude Code:
- Check the configuration file path and JSON syntax
- Restart the client after configuration changes
- Check logs for error messages:
- Claude Desktop:
~/Library/Logs/Claude/mcp*.log(macOS) - Claude Code: check terminal output
- Claude Desktop:
Permission errors:
- Ensure the vault directory is readable
- Hidden files (starting with
.) are denied by design - Use absolute paths, not relative
No results from search:
- Verify your vault contains
.mdfiles - Search is case-insensitive and supports regex
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
