Obsidian MCP Server
# Custom Obsidian MCP Server
A comprehensive Model Context Protocol (MCP) server for Obsidian vault operations, optimized for **Zettelkasten note creation workflows**. Works with any LLM that supports MCP (Claude, Perplexity, etc.).
## Overview
This MCP server connects to the [Obsidian Local REST API plugin](https://github.com/coddingtonbear/obsidian-local-rest-api) and provides 12 powerful tools for:
- **File Operations**: List, read, and manage files in your vault
- **Content Operations**: Create, append, and precisely edit notes
- **Search**: Find related notes across your vault
- **Metadata Management**: Tags, frontmatter, and note properties
Designed specifically for **Zettelkasten practitioners** who want AI assistance in creating atomic notes, finding connections, and maintaining a knowledge graph.
## Features
✅ **Zettelkasten-Optimized**: Tools designed for atomic note creation and linking
✅ **LLM-Agnostic**: Works with any MCP-compatible LLM
✅ **Safe by Default**: CREATE mode won't overwrite existing notes
✅ **Powerful Search**: Find existing notes before creating duplicates
✅ **Precise Editing**: Insert content at specific headings or blocks
✅ **Metadata Rich**: Full frontmatter and tag management
## Prerequisites
1. **Obsidian** with [Local REST API plugin](https://github.com/coddingtonbear/obsidian-local-rest-api) installed and configured
2. **Python 3.10+**
3. **uv** (optional but recommended for managing Python environments)
## Installation
### 1. Install the MCP Server
Using `uv` (recommended):
```bash
cd custom-obsidian-mcp
uv sync
```
Or using pip:
```bash
cd custom-obsidian-mcp
pip install -e .
```
### 2. Set Up Obsidian Local REST API
1. Install the "Local REST API" plugin in Obsidian
2. Enable the plugin in Settings → Community plugins
3. Go to plugin settings and copy your API key
4. Note the port (default: 27124)
### 3. Configure Environment Variables
Create a `.env` file or set these variables:
```bash
export OBSIDIAN_API_KEY="your-api-key-here"
export OBSIDIAN_HOST="127.0.0.1" # Usually localhost
export OBSIDIAN_PORT="27124" # Default port
```
## Configuration for LLM Clients
### Claude Desktop (Desktop App)
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"obsidian": {
"command": "/path/to/uvx",
"args": ["custom-obsidian-mcp"],
"env": {
"OBSIDIAN_API_KEY": "your-api-key-here",
"OBSIDIAN_HOST": "127.0.0.1",
"OBSIDIAN_PORT": "27124"
}
}
}
}
```
Or if using pip installation:
```json
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["-m", "custom_obsidian_mcp.server"],
"env": {
"OBSIDIAN_API_KEY": "your-api-key-here",
"OBSIDIAN_HOST": "127.0.0.1",
"OBSIDIAN_PORT": "27124"
}
}
}
}
```
### Perplexity or Other MCP Clients
Use similar configuration structure adapted to your client's format.
## Available Tools
### File Operations
#### `obsidian_list_files_in_vault`
List all files and directories in vault root.
```python
# No parameters needed
```
#### `obsidian_list_files_in_dir`
List contents of a specific directory.
```python
{
"dirpath": "Zettelkasten" # Empty string for root
}
```
#### `obsidian_get_file_contents`
Read a single file's complete contents.
```python
{
"filepath": "Zettelkasten/202411061234.md"
}
```
#### `obsidian_batch_get_file_contents`
Read multiple files at once (max 20).
```python
{
"filepaths": [
"Zettelkasten/note1.md",
"Zettelkasten/note2.md"
]
}
```
### Search Operations
#### `obsidian_simple_search`
Text search across all vault files with context.
```python
{
"query": "systems thinking",
"context_length": 100 # Optional, default 100
}
```
#### `obsidian_complex_search`
Advanced JsonLogic-based search.
```python
{
"query": {"glob": ["*.md", {"var": "path"}]}
}
```
### Content Operations
#### `obsidian_write_note`
Create or modify notes with optional frontmatter.
```python
{
"filepath": "Zettelkasten/202411061234 Systems Thinking.md",
"content": "# Systems Thinking\n\nContent here...",
"mode": "create", # create|overwrite|append|prepend
"frontmatter": {
"tags": ["zettelkasten", "systems"],
"created": "2024-11-06"
}
}
```
**Modes:**
- `create`: Only create new files (won't overwrite)
- `overwrite`: Replace entire file
- `append`: Add to end
- `prepend`: Add to beginning
#### `obsidian_append_content`
Quick append to file (creates if doesn't exist).
```python
{
"filepath": "Zettelkasten/note.md",
"content": "\n## New Section\n\nNew content..."
}
```
#### `obsidian_patch_content`
Insert content at specific locations within notes.
```python
{
"filepath": "Zettelkasten/note.md",
"target_type": "heading", # heading|block|frontmatter
"target": "Related Concepts/Subsection",
"operation": "append", # append|prepend|replace
"content": "New related concept..."
}
```
**Target Types:**
- `heading`: Navigate to heading path (e.g., "Section/Subsection")
- `block`: Use block reference (e.g., "^block-id")
- `frontmatter`: Update specific frontmatter field
#### `obsidian_delete_file`
Delete file or directory (requires confirmation).
```python
{
"filepath": "Zettelkasten/old-note.md",
"confirm": true # Must be true to proceed
}
```
### Metadata Operations
#### `obsidian_get_frontmatter`
Extract YAML frontmatter from a note.
```python
{
"filepath": "Zettelkasten/note.md"
}
```
#### `obsidian_update_frontmatter`
Update frontmatter without modifying content.
```python
{
"filepath": "Zettelkasten/note.md",
"updates": {
"tags": ["zettelkasten", "new-tag"],
"status": "published"
}
}
```
#### `obsidian_manage_tags`
Add, remove, or list tags.
```python
{
"filepath": "Zettelkasten/note.md",
"action": "add", # add|remove|list
"tags": ["systems-thinking", "mental-models"]
}
```
#### `obsidian_get_notes_info`
Get metadata for multiple notes (max 50).
```python
{
"filepaths": [
"Zettelkasten/note1.md",
"Zettelkasten/note2.md"
]
}
```
Returns: tags, creation date, size, and other metadata.
## Zettelkasten Workflow Examples
### Creating a New Atomic Note
1. **Search for existing related notes:**
```
Use obsidian_simple_search with query "systems thinking"
```
2. **Read related notes:**
```
Use obsidian_batch_get_file_contents with discovered notes
```
3. **Create new atomic note:**
```
Use obsidian_write_note:
- filepath: "Zettelkasten/202411061234 Systems Thinking Core Concept.md"
- content: Your atomic note content
- mode: "create" (safe - won't overwrite)
- frontmatter: {tags: ["zettelkasten", "systems"], created: "2024-11-06"}
```
### Linking Related Notes
1. **Find notes in topic cluster:**
```
Use obsidian_simple_search or obsidian_get_notes_info
```
2. **Add connection to existing note:**
```
Use obsidian_patch_content:
- target_type: "heading"
- target: "Related Concepts"
- operation: "append"
- content: "- [[202411061234 Systems Thinking Core Concept]]"
```
### Organizing with Tags
1. **Check current tags:**
```
Use obsidian_manage_tags with action: "list"
```
2. **Add topic tags:**
```
Use obsidian_manage_tags with action: "add"
```
## Best Practices
### For Zettelkasten
- **Always search first**: Use `obsidian_simple_search` before creating new notes to avoid duplicates
- **Atomic notes**: One idea per note, clearly titled
- **Link liberally**: Use `obsidian_patch_content` to add connections
- **Tag consistently**: Use `obsidian_manage_tags` for topic organization
- **Use CREATE mode**: Default to `mode="create"` to prevent accidental overwrites
### For Safety
- **CREATE mode is default**: Won't overwrite existing notes
- **Confirmation required**: Destructive operations need explicit confirmation
- **Test in dev vault**: Try tools in a test vault before using on your main Zettelkasten
## Troubleshooting
### Connection Errors
**Error:** "Connection error for GET /vault/"
**Solutions:**
1. Verify Obsidian is running
2. Check Local REST API plugin is enabled
3. Verify API key is correct
4. Check port matches plugin settings (default: 27124)
### Authentication Errors
**Error:** "Authentication failed"
**Solutions:**
1. Check `OBSIDIAN_API_KEY` environment variable
2. Regenerate API key in plugin settings
3. Ensure no extra spaces in API key
### File Not Found
**Error:** "Resource not found"
**Solutions:**
1. Verify file path is relative to vault root
2. Check file exists: use `obsidian_list_files_in_dir`
3. Ensure proper file extension (e.g., `.md`)
## Architecture
```
custom-obsidian-mcp/
├── src/
│ └── custom_obsidian_mcp/
│ ├── __init__.py
│ ├── server.py # FastMCP server with all tools
│ └── obsidian_client.py # REST API client
├── pyproject.toml # Project configuration
└── README.md
```
### Key Components
- **FastMCP**: Modern Python MCP framework with Pydantic validation
- **ObsidianClient**: Async HTTP client for REST API communication
- **Pydantic Models**: Type-safe input validation for all tools
- **Error Handling**: Actionable error messages guide correct usage
## Development
### Running Tests
```bash
# Verify Python syntax
python -m py_compile src/custom_obsidian_mcp/server.py
# Test basic import
python -c "from custom_obsidian_mcp.server import mcp; print('OK')"
```
### Adding New Tools
1. Define Pydantic input model
2. Add tool function with `@mcp.tool` decorator
3. Include proper annotations (readOnlyHint, etc.)
4. Add comprehensive docstring
5. Implement error handling
## Contributing
Contributions welcome! Please:
1. Follow existing code style
2. Add Pydantic models for validation
3. Include docstrings with examples
4. Test with Obsidian Local REST API
## License
MIT License - See LICENSE file for details
## Acknowledgments
- Built on [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)
- Inspired by [MarkusPfundstein's mcp-obsidian](https://github.com/MarkusPfundstein/mcp-obsidian)
- Uses [Obsidian Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api)
## Support
For issues or questions:
1. Check Troubleshooting section
2. Verify Obsidian Local REST API is working
3. Test with simple tools first (list_files_in_vault)
4. Open an issue with error messages and configuration
---
**Happy note-taking! 📝✨**
TDQS
Scored across 13 tools
Each tool has a clearly distinct purpose with no significant overlap. For example, obsidian_append_content adds to the end of files, obsidian_patch_content inserts at specific locations, and obsidian_write_note offers multiple write modes. The tools cover different aspects of file operations (read, write, update, delete, search, metadata management) without ambiguity.
All tools follow a consistent 'obsidian_verb_noun' naming pattern throughout. The verbs are descriptive (e.g., append, batch_get, delete, get, list, manage, patch, search, update, write), and the nouns clearly indicate the target resource (e.g., content, file_contents, frontmatter, tags, note). There are no deviations in style or convention.
With 13 tools, the server is well-scoped for managing an Obsidian vault and Zettelkasten workflow. Each tool serves a specific, necessary function, from basic CRUD operations (read, write, update, delete) to advanced features like search, metadata management, and batch operations. The count is neither too sparse nor bloated for the domain.
The tool set provides comprehensive coverage for Obsidian vault management, including full CRUD for files (create, read, update, delete), metadata handling (frontmatter and tags), directory navigation, search, and specialized operations like patching and batch reads. There are no obvious gaps; agents can perform all essential workflows for note-taking and organization.