Semantic search and vault management tools for Obsidian, exposed via MCP (Model Context Protocol).
- Obsidian plugin (optional): Chat sidebar for interacting with your vault directly in Obsidian
- Hybrid search: Combines semantic (vector) and keyword search with Reciprocal Rank Fusion
- Vault management: Read, create, move, and append/prepend to files; update frontmatter
- Section editing: Replace or append to specific markdown sections by heading
- Link discovery: Find backlinks and outlinks between notes
- Query by metadata: Search by frontmatter fields, date ranges, or folder
- Interaction logging: Log AI conversations to daily notes
- User preferences: Persistent preferences stored in vault and loaded by the agent
- Audio transcription: Transcribe audio embeds using Whisper API
- Web search: DuckDuckGo integration for web queries
- HTTP API: REST endpoint for programmatic access
The Obsidian plugin provides a chat sidebar that connects to the API server. The API server wraps the LLM agent, which uses MCP tools to search and manage your vault.
- Python 3.11, 3.12, or 3.13 (not 3.14 —
onnxruntimedoesn't have wheels for 3.14 yet)
The install script handles Python detection, virtual environment setup, dependency installation, .env configuration, and background service installation:
# Clone the repository
git clone https://github.com/glibalien/obsidian-tools.git
cd obsidian-tools
# macOS / Linux
./install.sh
# Windows (PowerShell)
.\install.ps1The installer will:
- Find or help you install a compatible Python (resolves the real binary, not pyenv shims)
- Create a virtual environment and install dependencies
- Walk you through
.envconfiguration - Optionally install background services (API server + vault indexer)
- Optionally run the initial vault index
To uninstall services: ./uninstall.sh (macOS/Linux) or .\uninstall.ps1 (Windows).
Click to expand manual installation steps
If you're on macOS and Homebrew has upgraded you to Python 3.14, use pyenv to install a compatible version:
# Install pyenv if you don't have it
brew install pyenv
# Add pyenv to your shell (add these to ~/.zshrc or ~/.bashrc)
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
# Restart your shell or run:
source ~/.zshrc
# Install Python 3.12
pyenv install 3.12.8# Clone the repository
git clone https://github.com/glibalien/obsidian-tools.git
cd obsidian-tools
# If using pyenv, it will automatically use 3.12.8 (from .python-version)
# Verify your Python version:
python --version # Should show Python 3.12.x
# IMPORTANT: If using pyenv, create the venv with the real binary, not the shim:
$(pyenv which python3.12) -m venv .venv
# Otherwise:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCopy .env.example to .env and fill in your values:
cp .env.example .envEdit .env:
VAULT_PATH=~/Documents/your-vault-name
CHROMA_PATH=./.chroma_db
FIREWORKS_API_KEY=your-api-key-here
FIREWORKS_MODEL=accounts/fireworks/models/deepseek-v3p1
API_PORT=8000
INDEX_INTERVAL=60
| Variable | Description |
|---|---|
VAULT_PATH |
Path to your Obsidian vault |
CHROMA_PATH |
Where to store the ChromaDB database (relative or absolute) |
FIREWORKS_API_KEY |
API key from Fireworks AI (required for the chat agent and audio transcription) |
FIREWORKS_MODEL |
Fireworks model ID (default: DeepSeek V3.1) |
API_PORT |
Port for the HTTP API server (default: 8000) |
INDEX_INTERVAL |
How often the vault indexer runs, in minutes (default: 60) |
To use the MCP server with Claude Code or another MCP client, copy .mcp.json.example to .mcp.json and update the paths:
cp .mcp.json.example .mcp.jsonEdit .mcp.json to point to your local installation:
{
"mcpServers": {
"obsidian-tools": {
"command": "/path/to/obsidian-tools/.venv/bin/python",
"args": ["/path/to/obsidian-tools/src/mcp_server.py"]
}
}
}Replace /path/to/obsidian-tools with the actual path where you cloned the repository.
Before searching, index your vault to create embeddings:
python src/index_vault.pyThe indexer is incremental — after the initial run, it only indexes files modified since the last run and prunes entries for deleted files. Use --full for a complete reindex:
python src/index_vault.py --fullTo keep the index up to date automatically, see Running as a Service.
python src/mcp_server.pyOr configure it in your MCP client's settings (see MCP Client Configuration).
For programmatic access or to use the Obsidian plugin:
python src/api_server.pyThe server binds to 127.0.0.1:8000 (localhost only). Send chat messages via POST:
# Start a new conversation
curl -X POST http://127.0.0.1:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Find notes about projects"}'
# Continue a conversation (use session_id from previous response)
curl -X POST http://127.0.0.1:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Tell me more", "session_id": "<uuid>"}'To keep the API server running persistently, see Running as a Service.
The plugin/ directory contains an optional Obsidian plugin with a chat sidebar. You can use the MCP server and HTTP API without installing the plugin.
# Build the plugin
cd plugin
npm install
npm run build
# Install to your vault (adjust path as needed)
mkdir -p ~/Documents/your-vault/.obsidian/plugins/vault-agent
cp manifest.json main.js styles.css ~/Documents/your-vault/.obsidian/plugins/vault-agent/Then in Obsidian:
- Go to Settings → Community Plugins
- Enable "Vault Agent"
- Click the message icon in the ribbon to open the chat sidebar
The API server must be running for the plugin to work.
The install script (./install.sh or .\install.ps1) can set up background services automatically. Service templates are in the services/ directory.
If you prefer to install services manually, expand the section for your platform below.
Linux (systemd) — manual setup
The template files in services/systemd/ use __PROJECT_DIR__ and __VENV_PYTHON__ placeholders. Replace them with absolute paths before installing:
mkdir -p ~/.config/systemd/user
# Replace placeholders and copy (adjust paths to your setup)
for f in services/systemd/*.service services/systemd/*.timer; do
sed -e "s|__PROJECT_DIR__|$PWD|g" \
-e "s|__VENV_PYTHON__|$PWD/.venv/bin/python|g" \
-e "s|__INDEX_INTERVAL__|60|g" \
"$f" > ~/.config/systemd/user/$(basename "$f")
done
systemctl --user daemon-reload
systemctl --user enable --now obsidian-tools-api
systemctl --user enable --now obsidian-tools-indexer-scheduler.timerUseful commands:
# Check status
systemctl --user status obsidian-tools-api
systemctl --user status obsidian-tools-indexer-scheduler.timer
# View logs
journalctl --user -u obsidian-tools-api -f
journalctl --user -u obsidian-tools-indexer
# Restart the API server
systemctl --user restart obsidian-tools-apiNote: User services require an active login session by default. To allow them to run without being logged in:
sudo loginctl enable-linger $USERmacOS (launchd) — manual setup
The template files in services/launchd/ use __USERNAME__, __PROJECT_DIR__, __VENV_PYTHON__, and __INDEX_INTERVAL_SEC__ placeholders. Replace them before installing:
# Replace placeholders and copy (adjust paths to your setup)
for f in services/launchd/*.plist; do
sed -e "s|__VENV_PYTHON__|$PWD/.venv/bin/python|g" \
-e "s|__PROJECT_DIR__|$PWD|g" \
-e "s|__USERNAME__|$(whoami)|g" \
-e "s|__INDEX_INTERVAL_SEC__|3600|g" \
"$f" > ~/Library/LaunchAgents/$(basename "$f")
done
launchctl load ~/Library/LaunchAgents/com.obsidian-tools.api.plist
launchctl load ~/Library/LaunchAgents/com.obsidian-tools.indexer.plistUseful commands:
# Check status
launchctl list | grep obsidian-tools
# View logs
tail -f ~/Library/Logs/obsidian-tools-api.log
tail -f ~/Library/Logs/obsidian-tools-indexer.log
# Unload a service
launchctl unload ~/Library/LaunchAgents/com.obsidian-tools.api.plist
# Reload after editing a plist
launchctl unload ~/Library/LaunchAgents/com.obsidian-tools.api.plist
launchctl load ~/Library/LaunchAgents/com.obsidian-tools.api.plistWindows (Task Scheduler) — manual setup
The template files in services/taskscheduler/ use __VENV_PYTHON__, __PROJECT_DIR__, and __INDEX_INTERVAL__ placeholders. Edit the XML files to replace these with absolute paths, then register them:
$xml = (Get-Content services\taskscheduler\obsidian-tools-api.xml -Raw) `
-replace '__VENV_PYTHON__', "$PWD\.venv\Scripts\python.exe" `
-replace '__PROJECT_DIR__', "$PWD"
Register-ScheduledTask -TaskName "ObsidianToolsAPI" -Xml $xml
$xml = (Get-Content services\taskscheduler\obsidian-tools-indexer.xml -Raw) `
-replace '__VENV_PYTHON__', "$PWD\.venv\Scripts\python.exe" `
-replace '__PROJECT_DIR__', "$PWD" `
-replace '__INDEX_INTERVAL__', '60'
Register-ScheduledTask -TaskName "ObsidianToolsIndexer" -Xml $xmlUseful commands:
# Check status
Get-ScheduledTask | Where-Object TaskName -like 'ObsidianTools*'
# Start/stop
Start-ScheduledTask -TaskName ObsidianToolsAPI
Stop-ScheduledTask -TaskName ObsidianToolsAPI
# Remove
Unregister-ScheduledTask -TaskName ObsidianToolsAPI -Confirm:$falseTo remove background services and optionally the virtual environment:
# macOS / Linux
./uninstall.sh
# Windows (PowerShell)
.\uninstall.ps1Your .env and .chroma_db/ are preserved by the uninstaller.
| Tool | Description |
|---|---|
search_vault |
Hybrid search (semantic + keyword) with configurable mode |
read_file |
Read full content of a vault note |
create_file |
Create a new markdown note with optional frontmatter |
move_file |
Move a file within the vault |
batch_move_files |
Move multiple files at once |
append_to_file |
Append content to end of a file |
prepend_to_file |
Insert content after frontmatter (or at start if none) |
replace_section |
Replace a markdown heading and its content |
append_to_section |
Append content to end of a section |
list_files_by_frontmatter |
Find files by frontmatter field values |
update_frontmatter |
Modify frontmatter (set, remove, or append) |
batch_update_frontmatter |
Update frontmatter on multiple files |
find_backlinks |
Find files that link to a given note |
find_outlinks |
Extract all wikilinks from a file |
search_by_date_range |
Find files by created or modified date |
search_by_folder |
List files in a folder |
log_interaction |
Log an interaction to today's daily note |
save_preference |
Save a user preference to Preferences.md |
list_preferences |
List all saved preferences |
remove_preference |
Remove a preference by line number |
web_search |
Search the web using DuckDuckGo |
get_current_date |
Get current date in YYYY-MM-DD format |
transcribe_audio |
Transcribe audio embeds in a note via Whisper API |
{
"query": "meeting notes about project X",
"n_results": 5,
"mode": "hybrid"
}Modes: "hybrid" (default), "semantic", "keyword"
{
"path": "Projects/my-project.md",
"field": "tags",
"value": "[\"project\", \"active\"]",
"operation": "set"
}Operations: "set", "remove", "append"
{
"path": "Projects/my-project.md",
"heading": "## Status",
"content": "## Status\n\nUpdated to complete."
}Replaces the entire "## Status" section (heading + content through to the next same-level or higher heading).
{
"path": "Projects/my-project.md",
"heading": "## Notes",
"content": "- New note added today"
}Appends content at the end of the "## Notes" section, preserving all existing content.
{
"path": "Meetings/2024-01-15.md"
}Parses audio embeds like ![[recording.m4a]] from the note and transcribes them using Fireworks Whisper API. Supported formats: m4a, webm, mp3, wav, ogg. Audio files are resolved from the Attachments folder.
src/
├── mcp_server.py # FastMCP server - registers tools from submodules
├── api_server.py # FastAPI HTTP wrapper for the agent
├── agent.py # CLI chat agent
├── config.py # Shared configuration
├── hybrid_search.py # Semantic + keyword search with RRF
├── search_vault.py # Search interface
├── index_vault.py # Vault indexer for ChromaDB
├── log_chat.py # Daily note logging
├── services/
│ ├── chroma.py # Shared ChromaDB connection management
│ └── vault.py # Path resolution, response helpers, utilities
└── tools/
├── files.py # read_file, create_file, move_file, append_to_file
├── frontmatter.py # list_files_by_frontmatter, update_frontmatter, etc.
├── links.py # find_backlinks, find_outlinks, search_by_folder
├── preferences.py # save_preference, list_preferences, remove_preference
├── search.py # search_vault, web_search
├── sections.py # prepend_to_file, replace_section, append_to_section
├── utility.py # log_interaction, get_current_date
└── audio.py # transcribe_audio
services/
├── systemd/ # Linux systemd unit files
│ ├── obsidian-tools-api.service
│ ├── obsidian-tools-indexer.service
│ └── obsidian-tools-indexer-scheduler.timer
├── launchd/ # macOS launchd plist files
│ ├── com.obsidian-tools.api.plist
│ └── com.obsidian-tools.indexer.plist
└── taskscheduler/ # Windows Task Scheduler XML files
├── obsidian-tools-api.xml
└── obsidian-tools-indexer.xml
plugin/ # Obsidian chat plugin (optional)
├── src/
│ ├── main.ts # Plugin entry point
│ └── ChatView.ts # Chat sidebar view
├── styles.css # Chat UI styling
└── manifest.json # Plugin metadata
- ChromaDB - Vector database for embeddings
- sentence-transformers - Embedding model
- FastMCP - MCP server framework
- FastAPI - HTTP API framework
- PyYAML - YAML parsing for frontmatter
- OpenAI SDK - Whisper API client (via Fireworks)
- python-dotenv - Environment variable loading
- ddgs - DuckDuckGo search
MIT