obsidian-mcp-server
AGENTS.md9 starsChanged 7 months agoMITView on GitHub
Context file for AI coding agents working on this repository. MCP (Model Context Protocol) server built with FastMCP that exposes Obsidian vault operations as tools, resources, and prompts. Acts as a bridge between AI assistants (Claude Desktop, Cursor, Claude Code, Cline, etc.) and a user's Obsidian knowledge base.
- Reads credentials
- Installs packages
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository10 other files it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
- Code QualitySkill
- Documentation UpdaterSkill
- Git WorkflowSkill
- knowledge-extractorSkill
- knowledge-extranctorSkill
- MCP DeveloperSkill
- Python PatternsSkill
- RefactoringSkill
- Test RunnerSkill
The file
We split this AGENTS.md into the rules it gives an agent
10 rules. About 0 are common, 2 are distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 32
Add dev dependencies: uv add --dev package
- Very specific: line 33
Add optional RAG deps: uv sync --extra rag
- Very specific: line 43
All async tests must use anyio fixtures, not asyncio
- Distinctive: line 177
Use f-strings, early returns, descriptive names
- Very specific: line 182
All logs → stderr (stdout is MCP protocol JSON)
- Very specific: line 184
Never: use print() — it breaks MCP communication
- Very specific: line 195
Read these before performing related tasks.
- Very specific: line 206
CHANGELOG.md: Before every commit, add an entry to CHANGELOG.md describing the change.
- Distinctive: line 214
stdout is sacred: Only MCP protocol JSON goes to stdout.
- Very specific: line 215
Path validation is critical: Always validate through utils/security.py to prevent directory traversal.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
Context file for AI coding agents working on this repository.
Essential Commands
# Install dependencies (ALWAYS uv, NEVER pip)
make install # uv sync
# Run MCP server locally
make dev # uv run obsidian-mcp-server
# Testing
make test # uv run pytest tests/
make coverage # uv run pytest --cov=obsidian_mcp --cov-report=term-missing tests/
# Linting & formatting
make lint # uv run ruff check . && uv run pyright .
make format # uv run ruff check --fix . && uv run ruff format .
# Pre-commit hooks (ruff, pyright, pylint, bandit + general checks)
make hooks # uv run pre-commit install
make check # uv run pre-commit run --all-files
Package Management — CRITICAL
- ONLY use
uv, NEVERpiporuv pip install - Add dependencies:
uv add package - Add dev dependencies:
uv add --dev package - Add optional RAG deps:
uv sync --extra rag - Run tools:
uv run tool - FORBIDDEN:
pip install,uv pip install,@latestsyntax
Testing
- Framework:
pytestwithanyiofor async tests (NOT asyncio) - Single test:
uv run pytest tests/test_basic.py::TestClassName::test_name -v - Coverage:
uv run pytest tests/ --cov=obsidian_mcp - CI minimum coverage: 25%
- All async tests must use
anyiofixtures, notasyncio
Architecture
Core Concept
MCP (Model Context Protocol) server built with FastMCP that exposes Obsidian vault operations as tools, resources, and prompts. Acts as a bridge between AI assistants (Claude Desktop, Cursor, Claude Code, Cline, etc.) and a user's Obsidian knowledge base.
Key Principles
- Vault-Agnostic: Auto-detects vault structure. User config lives in
{vault}/.agents/vault.yaml, not here. - Skills System: AI personas loaded from the user's vault at
{vault}/.agents/skills/. Each skill hasSKILL.mdwith YAML frontmatter. - Separation of Concerns: Tool registration (
tools/*.py) → Business logic (tools/*_logic.py) → Utilities (utils/*.py). - Security Model: Path validation via
.forbidden_pathsand vault config. All file ops go throughutils/security.py. - Optional RAG: Semantic search requires extra deps (
uv sync --extra rag). ChromaDB-based vector indexing insemantic/.
Module Organization
obsidian_mcp/
├── server.py # FastMCP instance, module registration, transport config
├── config.py # Pydantic Settings (env: OBSIDIAN_VAULT_PATH, LOG_LEVEL, etc.)
├── vault_config.py # Loads optional .agents/vault.yaml from user's vault
├── constants.py # Centralized magic numbers (SemanticDefaults, SearchLimits, etc.)
├── messages.py # User-facing message templates
├── result.py # Generic Result[T] type for consistent return values
├── tools/ # MCP Tools organized by domain
│ ├── navigation.py # Read, list, search notes
│ ├── navigation_logic.py
│ ├── creation.py # Create, edit, delete, template usage
│ ├── creation_logic.py
│ ├── analysis.py # Vault stats, tag sync, quality checks
│ ├── analysis_logic.py
│ ├── graph.py # Backlinks, orphans, connection analysis
│ ├── graph_logic.py
│ ├── agents.py # Skills loader (from user vault/.agents/skills/)
│ ├── agents_logic.py
│ ├── agents_generator.py # Skill generation, suggestion, and sync tools
│ ├── context.py # Vault structure and metadata
│ ├── context_logic.py
│ ├── semantic.py # RAG/vector search integration
│ ├── semantic_logic.py
│ ├── youtube.py # Transcript extraction
│ └── youtube_logic.py
├── semantic/ # Optional RAG module (ChromaDB + sentence-transformers)
│ ├── indexer.py # Embedding generation
│ ├── retriever.py # Similarity search
│ └── service.py # High-level RAG API
├── utils/
│ ├── logging.py # Centralized logging (stderr only, stdout = MCP protocol)
│ ├── security.py # Path validation, access control, directory traversal prevention
│ ├── vault.py # Vault file operations
│ └── timeout.py # Timeout utilities for long operations
├── resources/ # MCP Resources (read-only data endpoints)
├── prompts/ # MCP Prompts (system prompts for AI)
└── models/ # Pydantic models for data validationTool Registration Pattern
from fastmcp import FastMCP
from ..config import get_vault_path
def register_xxx_tools(mcp: FastMCP) -> None:
@mcp.tool()
def my_tool(param: str) -> str:
"""Docstring becomes tool description in MCP."""
vault_path = get_vault_path()
result = my_tool_logic(param, vault_path)
return result.to_display()Result Pattern
All *_logic.py functions return Result[T] (from result.py):
from obsidian_mcp.result import Result
def my_logic(param: str) -> Result[str]:
if error_condition:
return Result.fail("Error description")
return Result.ok("Success data")
# In tool: result.to_display() for MCP output
# In tests: result.success, result.data, result.errorConfiguration Hierarchy
- Environment Variables (
.env):OBSIDIAN_VAULT_PATH: Absolute path to vault (required)LOG_LEVEL: DEBUG|INFO|WARNING|ERROR (default: INFO)OBSIDIAN_SEARCH_TIMEOUT_SECONDS,OBSIDIAN_MAX_SEARCH_RESULTS,OBSIDIAN_CACHE_TTL_SECONDS
- Vault Config (
{vault}/.agents/vault.yaml):templates_folder,excluded_folders,excluded_patterns,private_paths
- Constants (
constants.py):SemanticDefaults,SearchLimits,FolderSuggestion,FileConstants
- Skills & Rules (in user's vault, NOT this repo):
- Skills:
{vault}/.agents/skills/{name}/SKILL.md - Global rules:
{vault}/.agents/REGLAS_GLOBALES.md
- Skills:
Quality & CI
Pre-commit Hooks
Installed via make hooks. Runs on every commit:
trailing-whitespace,end-of-file-fixer,check-yaml,check-added-large-files,check-merge-conflict,debug-statements- Ruff lint + format
- Pyright type checking
- Pylint code quality (excludes
semantic/) - Bandit security analysis
CI Pipeline (GitHub Actions)
4 parallel jobs on push/PR to main:
| Job | What it checks |
| Lint & Format | ruff check . + ruff format --check . |
| Type Check | pyright |
| Security | bandit -c pyproject.toml -r obsidian_mcp/ |
| Tests | pytest --cov --cov-fail-under=25 |
Code Style
- Type hints required for all functions
- Public APIs must have docstrings
- Line length: 88 chars (Ruff enforced)
- PEP 8 naming:
snake_casefunctions,PascalCaseclasses,UPPER_SNAKE_CASEconstants - Use f-strings, early returns, descriptive names
- Pylint config in
pyproject.toml: max 8 args, 20 locals, 15 branches, 60 statements
Logging
- All logs →
stderr(stdout is MCP protocol JSON) - Use:
from ..utils import get_logger; logger = get_logger(__name__) - Never use
print()— it breaks MCP communication
Dev Environment
.agents/ in This Repo
This repo has its own .agents/ directory with development-specific guidance (separate from the user's vault .agents/):
- Skills (
.agents/skills/):code-quality,docs-updater,git-workflow,mcp-developer,python-patterns,refactoring,test-runner - Workflows (
.agents/workflows/):debug-tests,dev-server,new-tool,quality-check,quick-push
Read these before performing related tasks. Workflows can be invoked via /slash-command syntax.
Docs
Detailed documentation in docs/:
architecture.md,tool-reference.md,configuration.md,agent-folder-setup.md,semantic-search.md,FUTURE.md
Git Workflow
- Conventional commits:
type(scope): description - Types:
feat,fix,docs,style,refactor,test,chore,ci,build - CHANGELOG.md: Before every commit, add an entry to
CHANGELOG.mddescribing the change. Group entries by version under## [Unreleased]until a release is tagged. - For bug fixes:
git commit --trailer "Reported-by:<name>" - For issues:
git commit --trailer "Github-Issue:#<number>" - NEVER mention co-authors, AI tools, or Copilot in commits or PRs
Common Gotchas
- Skills are in the user's vault, not this repo. The
.agents/skills/here are for development guidance, not runtime skills. - stdout is sacred: Only MCP protocol JSON goes to stdout. Everything else → stderr.
- Path validation is critical: Always validate through
utils/security.pyto prevent directory traversal. - RAG is optional: Handle missing
langchaindeps gracefully. Check imports and skip RAG if unavailable. vault_config.yamlvs.env: Vault-specific settings (folders, exclusions) from vault config. Server settings (path, log level) from.env.- Type narrowing: Pyright is strict about
Optional. Always check forNonebefore using Optional values. - Pylint pre-commit: May block commits on pre-existing warnings. Use
--no-verifyonly if warnings are not from your changes. - Duplicate
torch:pyproject.tomlhas a duplicatedtorchentry in[project.optional-dependencies].rag— known issue, harmless.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

