{"id":46733672,"url":"https://github.com/pvliesdonk/markdown-vault-mcp","last_synced_at":"2026-05-03T12:02:31.891Z","repository":{"id":342873065,"uuid":"1175222959","full_name":"pvliesdonk/markdown-vault-mcp","owner":"pvliesdonk","description":"Generic markdown collection MCP server with FTS5 + semantic search, frontmatter-aware indexing, and incremental reindexing","archived":false,"fork":false,"pushed_at":"2026-04-26T13:54:57.000Z","size":3691,"stargazers_count":5,"open_issues_count":25,"forks_count":3,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T15:04:49.805Z","etag":null,"topics":["claude","embeddings","fastmcp","fts5","knowledge-base","markdown","mcp","mcp-server","model-context-protocol","obsidian","python","search","semantic-search","sqlite","vault"],"latest_commit_sha":null,"homepage":"https://pvliesdonk.github.io/markdown-vault-mcp/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pvliesdonk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-07T12:08:22.000Z","updated_at":"2026-04-24T16:24:54.000Z","dependencies_parsed_at":"2026-03-15T18:02:07.874Z","dependency_job_id":"0ab71044-6154-44a0-871b-d42cd5ce2d27","html_url":"https://github.com/pvliesdonk/markdown-vault-mcp","commit_stats":null,"previous_names":["pvliesdonk/markdown-mcp","pvliesdonk/markdown-vault-mcp"],"tags_count":46,"template":false,"template_full_name":null,"purl":"pkg:github/pvliesdonk/markdown-vault-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pvliesdonk%2Fmarkdown-vault-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pvliesdonk%2Fmarkdown-vault-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pvliesdonk%2Fmarkdown-vault-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pvliesdonk%2Fmarkdown-vault-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pvliesdonk","download_url":"https://codeload.github.com/pvliesdonk/markdown-vault-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pvliesdonk%2Fmarkdown-vault-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32568036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude","embeddings","fastmcp","fts5","knowledge-base","markdown","mcp","mcp-server","model-context-protocol","obsidian","python","search","semantic-search","sqlite","vault"],"created_at":"2026-03-09T16:28:35.936Z","updated_at":"2026-05-03T12:02:31.876Z","avatar_url":"https://github.com/pvliesdonk.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- mcp-name: io.github.pvliesdonk/markdown-vault-mcp --\u003e\n# markdown-vault-mcp\n\n[![CI](https://github.com/pvliesdonk/markdown-vault-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/pvliesdonk/markdown-vault-mcp/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/pvliesdonk/markdown-vault-mcp/graph/badge.svg)](https://codecov.io/gh/pvliesdonk/markdown-vault-mcp) [![PyPI](https://img.shields.io/pypi/v/markdown-vault-mcp)](https://pypi.org/project/markdown-vault-mcp/) [![Python](https://img.shields.io/pypi/pyversions/markdown-vault-mcp)](https://pypi.org/project/markdown-vault-mcp/) [![License](https://img.shields.io/github/license/pvliesdonk/markdown-vault-mcp)](LICENSE) [![Docker](https://img.shields.io/github/v/release/pvliesdonk/markdown-vault-mcp?label=ghcr.io\u0026logo=docker)](https://github.com/pvliesdonk/markdown-vault-mcp/pkgs/container/markdown-vault-mcp) [![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://pvliesdonk.github.io/markdown-vault-mcp/) [![llms.txt](https://img.shields.io/badge/llms.txt-available-brightgreen)](https://pvliesdonk.github.io/markdown-vault-mcp/llms.txt) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/pvliesdonk/markdown-vault-mcp)\n\n\u003c!-- DOMAIN-START --\u003e\nA generic markdown collection [MCP](https://modelcontextprotocol.io/) server with FTS5 full-text search, semantic vector search, frontmatter-aware indexing, incremental reindexing, and non-markdown attachment support.\n\n**[Documentation](https://pvliesdonk.github.io/markdown-vault-mcp/)** | **[PyPI](https://pypi.org/project/markdown-vault-mcp/)** | **[Docker](https://github.com/pvliesdonk/markdown-vault-mcp/pkgs/container/markdown-vault-mcp)**\n\nPoint it at a directory of Markdown files (an Obsidian vault, a docs folder, a Zettelkasten, a PARA vault) and it exposes search, read, write, and edit tools over the Model Context Protocol.\n\u003c!-- DOMAIN-END --\u003e\n\n## Features\n\n\u003c!-- DOMAIN-START --\u003e\n- **Full-text search** — SQLite FTS5 with BM25 scoring, porter stemming\n- **Semantic search** — cosine similarity over embedding vectors (FastEmbed, Ollama, or OpenAI)\n- **Hybrid search** — Reciprocal Rank Fusion combining FTS5 and vector results\n- **Diversity-aware ranking** — each search result list caps a single document at 2 chunks (configurable), downweights chunks of long documents, and returns sentence-scale snippets — bounded LLM context cost per query, with full chunk recovery via `read(path, section=heading)`\n- **Adaptive heading-level chunking** — long sections are recursively re-split at deeper heading levels (H1 → H6) until each chunk fits a configurable word budget, improving retrieval precision on synthesising essays without manual restructuring\n\n\u003e **Upgrading.** As of this release, `search` returns query-relevant snippets in the `content` field by default (approximately 200 words). Pass `snippet_words=0` to recover the prior full-chunk behaviour, or use `read(path, section=heading)` to fetch a specific chunk after seeing a snippet. Documents are also re-chunked on next `reindex` to honour the adaptive `MARKDOWN_VAULT_MCP_MAX_CHUNK_WORDS` threshold (default 400).\n- **Frontmatter-aware** — indexes YAML frontmatter fields, supports required field enforcement\n- **Incremental reindexing** — hash-based change detection, only re-processes modified files\n- **Write operations** — create, edit, delete, rename documents with automatic index updates\n- **Attachment support** — read, write, delete, and list non-markdown files (PDFs, images, etc.)\n- **Git integration** — optional auto-commit and push on every write via `GIT_ASKPASS`\n- **OIDC authentication** — optional token-based auth for HTTP deployments (Authelia, Keycloak, etc.)\n- **MCP tools** — 28 LLM-visible tools including search, read, write, edit, delete, rename, git history, and admin operations; plus 6 app-only tools for MCP Apps clients\n- **MCP resources** — 9 resources exposing vault configuration, statistics, tags, folders, document outlines, similar notes, recent notes, and an interactive SPA\n- **MCP prompts** — 6 prompt templates including template-driven note creation\n\u003c!-- DOMAIN-END --\u003e\n\n## What you can do with it\n\n\u003c!-- DOMAIN-START --\u003e\nWith this server mounted in Claude, you can:\n\n- **Capture a URL as a note.** \"Fetch \u003curl\u003e, summarize as a Resource note under `3-Resources/`, and link any existing notes on the topic.\" — Claude composes `fetch` + `search` + `write`.\n- **Research a topic into your vault.** \"Research product security regulations, compare them, and create a set of interlinked notes — one per regulation, plus a map-of-content.\" — Claude composes web-search tools (client-side) + `write` with wikilinks. See the [Research workflows guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/research-workflows/) for the full loop.\n- **Distill today's thinking.** \"Summarize today's conversations into Inbox notes.\" — Claude.ai only; uses `conversation_search` + `recent_chats` + `write`. The [`para-capture-chats`](examples/para/prompts/para-capture-chats.md) prompt is the one-click version.\n- **Find missing links.** Fire the [`propose-links`](https://pvliesdonk.github.io/markdown-vault-mcp/prompts/#propose-links) prompt from the `+` menu — it scans recently-modified notes, proposes meaningful connections, and writes them on confirmation.\n- **Split or merge captures.** \"Split this Inbox note into two.\" / \"Merge this into `\u003cexisting note\u003e` instead of duplicating.\" — Claude composes `read` + `write` + `delete`.\n\nNo external scheduler, no separate capture app — the vault sits behind your conversations and absorbs their output.\n\u003c!-- DOMAIN-END --\u003e\n\n\u003c!-- ===== TEMPLATE-OWNED SECTIONS BELOW — DO NOT EDIT; CHANGES WILL BE OVERWRITTEN ON COPIER UPDATE ===== --\u003e\n\n## Installation\n\n### From PyPI\n\n```bash\npip install markdown-vault-mcp\n```\n\n\u003c!-- DOMAIN-START --\u003e\nWith optional dependencies:\n\n```bash\npip install markdown-vault-mcp[mcp]            # FastMCP server\npip install markdown-vault-mcp[embeddings-api]  # Ollama/OpenAI embeddings via HTTP\npip install markdown-vault-mcp[embeddings]      # FastEmbed local embeddings\npip install markdown-vault-mcp[all]             # MCP + FastEmbed + API embeddings\n```\n\u003c!-- DOMAIN-END --\u003e\n\n### From source\n\n```bash\ngit clone https://github.com/pvliesdonk/markdown-vault-mcp.git\ncd markdown-vault-mcp\nuv sync --all-extras --dev\n```\n\n### Docker\n\n```bash\ndocker pull ghcr.io/pvliesdonk/markdown-vault-mcp:latest\n```\n\n\u003c!-- DOMAIN-START --\u003e\nThe Docker image uses `[all]` (MCP + FastEmbed + API embeddings). By default, semantic search works locally with FastEmbed and can switch to Ollama/OpenAI when configured. A `compose.yml` ships at the repo root as a starting point — copy `.env.example` to `.env`, edit, and `docker compose up -d`.\n\n### Linux packages (.deb / .rpm)\n\nDownload `.deb` or `.rpm` packages from the [GitHub Releases](https://github.com/pvliesdonk/markdown-vault-mcp/releases) page. Both install a hardened systemd unit; env configuration is sourced from `/etc/markdown-vault-mcp/env` (copy from the shipped `/etc/markdown-vault-mcp/env.example`). See the [systemd deployment guide](https://pvliesdonk.github.io/markdown-vault-mcp/deployment/systemd/) for details.\n\n### Claude Desktop (.mcpb bundle)\n\nDownload the `.mcpb` bundle from the [GitHub Releases](https://github.com/pvliesdonk/markdown-vault-mcp/releases) page. Double-click to install, or run:\n\u003c!-- DOMAIN-END --\u003e\n\n```bash\nmcpb install markdown-vault-mcp-\u003cversion\u003e.mcpb\n```\n\n\u003c!-- DOMAIN-START --\u003e\nClaude Desktop opens a GUI wizard that prompts for required env vars — no manual JSON editing needed. See [Step 0 of the Claude Desktop guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/claude-desktop/#step-0-install-via-mcpb-bundle-easiest) for details.\n\n### Claude Code plugin\n\n```\n/plugin marketplace add pvliesdonk/claude-plugins\n/plugin install markdown-vault-mcp@pvliesdonk\n```\n\nInstalls the MCP server and the `vault-workflow` skill. See the [Claude Code plugin guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/claude-code-plugin/) for details.\n\n## Quick Start\n\n### As a library\n\n```python\nfrom pathlib import Path\nfrom markdown_vault_mcp import Collection\n\ncollection = Collection(source_dir=Path(\"/path/to/vault\"))\nresults = collection.search(\"query text\", limit=10)\n```\n\n### As an MCP server\n\n```bash\nexport MARKDOWN_VAULT_MCP_SOURCE_DIR=/path/to/vault\nmarkdown-vault-mcp serve\n```\n\n### With Docker Compose\n\n1. Copy an example env file:\n\n   ```bash\n   cp examples/obsidian-readonly.env .env\n   ```\n\n2. Edit `.env` to set `MARKDOWN_VAULT_MCP_SOURCE_DIR` to the absolute path of your vault on the host.\n\n3. Start the service:\n\n   ```bash\n   docker compose up -d\n   ```\n\n4. Check the logs:\n\n   ```bash\n   docker compose logs -f markdown-vault-mcp\n   ```\n\n### Example env files\n\n| File | Description |\n|------|-------------|\n| `examples/obsidian-readonly.env` | Obsidian vault, read-only, Ollama embeddings |\n| `examples/obsidian-readwrite.env` | Obsidian vault, read-write with git auto-commit |\n| `examples/obsidian-oidc.env` | Obsidian vault, read-only, OIDC authentication (Authelia) |\n| `examples/ifcraftcorpus.env` | Strict frontmatter enforcement, read-only corpus |\n\nFor reverse proxy (Traefik) and deployment setup, see [`docs/deployment.md`](docs/deployment.md).\n\n## Configuration\n\nAll configuration is via environment variables with the `MARKDOWN_VAULT_MCP_` prefix (except embedding provider settings, which use their own conventions).\n\n### Core\n\n| Variable | Default | Required | Description |\n|----------|---------|----------|-------------|\n| `MARKDOWN_VAULT_MCP_SOURCE_DIR` | — | **Yes** | Path to the markdown vault directory |\n| `MARKDOWN_VAULT_MCP_READ_ONLY` | `true` | No | Set to `false` to enable write operations |\n| `MARKDOWN_VAULT_MCP_INDEX_PATH` | in-memory | No | Path to the SQLite FTS5 index file; set for persistence across restarts |\n| `MARKDOWN_VAULT_MCP_EMBEDDINGS_PATH` | disabled | No | Path to the numpy embeddings file; required to enable semantic search |\n| `MARKDOWN_VAULT_MCP_STATE_PATH` | `{SOURCE_DIR}/.markdown_vault_mcp/state.json` | No | Path to the change-tracking state file |\n| `MARKDOWN_VAULT_MCP_INDEXED_FIELDS` | — | No | Comma-separated frontmatter fields to promote to the tag index for structured filtering |\n| `MARKDOWN_VAULT_MCP_REQUIRED_FIELDS` | — | No | Comma-separated frontmatter fields required on every document; documents missing any are excluded from the index |\n| `MARKDOWN_VAULT_MCP_EXCLUDE` | — | No | Comma-separated glob patterns to exclude from scanning (e.g. `.obsidian/**,.trash/**`) |\n| `MARKDOWN_VAULT_MCP_TEMPLATES_FOLDER` | `_templates` | No | Relative folder path where note templates live (used by the `create_from_template` prompt) |\n| `MARKDOWN_VAULT_MCP_PROMPTS_FOLDER` | — | No | Path to a directory of `.md` prompt files that extend or override built-in prompts (see [User-defined prompts](#user-defined-prompts)) |\n\n### Server identity\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MARKDOWN_VAULT_MCP_SERVER_NAME` | `markdown-vault-mcp` | MCP server name shown to clients; useful for multi-instance setups |\n| `MARKDOWN_VAULT_MCP_INSTRUCTIONS` | (auto) | System-level instructions injected into LLM context; defaults to a description that reflects read-only vs read-write state |\n| `MARKDOWN_VAULT_MCP_HTTP_PATH` | `/mcp` | HTTP endpoint path for streamable HTTP transport (used by `serve --transport http`) |\n| `MARKDOWN_VAULT_MCP_EVENT_STORE_URL` | `file:///data/state/events` | Event store backend for HTTP session persistence. `file:///path` (default) survives restarts; `memory://` for dev (lost on restart). |\n| `MARKDOWN_VAULT_MCP_APP_DOMAIN` | (auto) | Override the Claude app domain used for MCP Apps iframe sandboxing. Auto-computed from `BASE_URL` when not set. |\n| `FASTMCP_LOG_LEVEL` | `INFO` | Log level for FastMCP internals (`DEBUG`, `INFO`, `WARNING`, `ERROR`). App loggers default to `INFO`. `-v` overrides both to `DEBUG`. |\n| `FASTMCP_ENABLE_RICH_LOGGING` | `true` | Set to `false` for plain/structured JSON log output instead of Rich-formatted output. |\n\n### Search and embeddings\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MARKDOWN_VAULT_MCP_EMBEDDING_PROVIDER` | auto-detect | Embedding provider: `openai`, `ollama`, or `fastembed` |\n| `OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL (**not** `MARKDOWN_VAULT_MCP_`-prefixed) |\n| `OPENAI_API_KEY` | — | OpenAI API key for the OpenAI embedding provider (**not** `MARKDOWN_VAULT_MCP_`-prefixed) |\n| `MARKDOWN_VAULT_MCP_OLLAMA_MODEL` | `nomic-embed-text` | Ollama embedding model name |\n| `MARKDOWN_VAULT_MCP_OLLAMA_CPU_ONLY` | `false` | Force Ollama to use CPU only |\n| `MARKDOWN_VAULT_MCP_FASTEMBED_MODEL` | `BAAI/bge-small-en-v1.5` | FastEmbed model name |\n| `MARKDOWN_VAULT_MCP_FASTEMBED_CACHE_DIR` | FastEmbed default | FastEmbed model cache directory (in Docker, stored under `/data/state/fastembed`) |\n\n### Git integration\n\nGit integration has three modes:\n\n- **Managed mode** (`MARKDOWN_VAULT_MCP_GIT_REPO_URL` set): server owns repo setup.\n  On startup it clones into `SOURCE_DIR` when empty, or validates existing `origin`.\n  Pull loop + auto-commit + deferred push are enabled.\n- **Unmanaged / commit-only mode** (no `GIT_REPO_URL`): writes are committed to a local git repo if `SOURCE_DIR` is already a git checkout. No pull, no push.\n- **No-git mode**: if `SOURCE_DIR` is not a git repo, git callbacks are no-ops.\n\nWhen token auth is used (`MARKDOWN_VAULT_MCP_GIT_TOKEN`), remotes must be HTTPS.\nSSH remotes (for example `git@github.com:owner/repo.git`) are rejected with a startup error.\nFix with: `git -C /path/to/vault remote set-url origin https://github.com/owner/repo.git`\n\nBackward compatibility: `MARKDOWN_VAULT_MCP_GIT_TOKEN` without `GIT_REPO_URL` still works (legacy mode) but logs a deprecation warning.\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MARKDOWN_VAULT_MCP_GIT_REPO_URL` | — | HTTPS remote URL for managed mode; enables clone/remote validation on startup |\n| `MARKDOWN_VAULT_MCP_GIT_USERNAME` | `x-access-token` | Username for HTTPS auth prompts (`x-access-token` for GitHub, `oauth2` for GitLab, account name for Bitbucket) |\n| `MARKDOWN_VAULT_MCP_GIT_TOKEN` | — | Token/password for HTTPS auth (`GIT_ASKPASS`) |\n| `MARKDOWN_VAULT_MCP_GIT_PULL_INTERVAL_S` | `600` | Seconds between `git fetch` + ff-only update attempts; `0` disables periodic pull |\n| `MARKDOWN_VAULT_MCP_GIT_PUSH_DELAY_S` | `30` | Seconds of write-idle time before pushing; `0` = push only on shutdown |\n| `MARKDOWN_VAULT_MCP_GIT_COMMIT_NAME` | `markdown-vault-mcp` | Git committer name for auto-commits; **set this in Docker** where `git config user.name` is empty |\n| `MARKDOWN_VAULT_MCP_GIT_COMMIT_EMAIL` | `noreply@markdown-vault-mcp` | Git committer email for auto-commits |\n| `MARKDOWN_VAULT_MCP_GIT_LFS` | `true` | Enable Git LFS — runs `git lfs pull` on startup to fetch LFS-tracked attachments (PDFs, images). Set to `false` for repos without LFS. |\n\n### Attachments\n\nNon-markdown file support. See [Attachments](#attachments) for details.\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MARKDOWN_VAULT_MCP_ATTACHMENT_EXTENSIONS` | (built-in list) | Comma-separated allowed extensions without dot (e.g. `pdf,png,jpg`); use `*` to allow all non-`.md` files |\n| `MARKDOWN_VAULT_MCP_MAX_ATTACHMENT_SIZE_MB` | `10.0` | Maximum attachment size in MB for reads and writes; `0` disables the limit |\n\n### Bearer token authentication\n\nSimple static token auth for HTTP deployments. Set a single env var — clients must send `Authorization: Bearer \u003ctoken\u003e`.\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `MARKDOWN_VAULT_MCP_BEARER_TOKEN` | Yes | Static bearer token; any non-empty string enables auth |\n\n### OIDC authentication\n\nFull OAuth 2.1 authentication for HTTP deployments. OIDC activates when all four required variables are set. See [Authentication](#authentication) for setup details.\n\n\u003e **Multi-auth:** If both `BEARER_TOKEN` and all OIDC variables are set, the server accepts **either** credential — a valid bearer token or a valid OIDC session. This is useful when different clients use different auth flows (e.g. Claude web via OIDC and Claude Code via bearer token).\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `MARKDOWN_VAULT_MCP_BASE_URL` | Yes | Public base URL of the server (e.g. `https://mcp.example.com`; include prefix if mounted under subpath, e.g. `https://mcp.example.com/vault`). Also required for `create_download_link` and used to auto-compute the MCP Apps domain. |\n| `MARKDOWN_VAULT_MCP_OIDC_CONFIG_URL` | Yes | OIDC discovery endpoint (e.g. `https://auth.example.com/.well-known/openid-configuration`) |\n| `MARKDOWN_VAULT_MCP_OIDC_CLIENT_ID` | Yes | OIDC client ID registered with your provider |\n| `MARKDOWN_VAULT_MCP_OIDC_CLIENT_SECRET` | Yes | OIDC client secret |\n| `MARKDOWN_VAULT_MCP_OIDC_JWT_SIGNING_KEY` | No | JWT signing key; **required on Linux/Docker** — the default is ephemeral and invalidates tokens on restart. Generate with `openssl rand -hex 32` |\n| `MARKDOWN_VAULT_MCP_OIDC_AUDIENCE` | No | Expected JWT audience claim; leave unset if your provider does not set one |\n| `MARKDOWN_VAULT_MCP_OIDC_REQUIRED_SCOPES` | No | Comma-separated required scopes; default `openid` |\n| `MARKDOWN_VAULT_MCP_OIDC_VERIFY_ACCESS_TOKEN` | No | Set `true` to verify the upstream access token as a JWT instead of the id token. Only needed when your provider issues JWT access tokens and you require audience-claim validation on that token. Default: verify the id token (works with all providers, including opaque-token issuers like Authelia) |\n\n## CLI Reference\n\n```\nmarkdown-vault-mcp \u003ccommand\u003e [options]\n```\n\n### `serve`\n\nStart the MCP server.\n\n```bash\nmarkdown-vault-mcp serve [--transport {stdio|sse|http}] [--host HOST] [--port PORT] [--http-path PATH]\n```\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--transport` | `stdio` | MCP transport: `stdio` (stdin/stdout, default), `sse` (Server-Sent Events), `http` (streamable-HTTP). Use `http` for Docker with a reverse proxy or when OIDC is enabled. |\n| `--host` | `127.0.0.1` | Bind host for the `http` transport (ignored for `stdio` and `sse`); pass `0.0.0.0` to bind all interfaces inside Docker |\n| `--port` | `8000` | Port for the `http` transport (ignored for `stdio` and `sse`) |\n| `--http-path` (alias `--path`) | env `MARKDOWN_VAULT_MCP_HTTP_PATH` or `/mcp` | MCP HTTP path for `http` transport; useful for reverse-proxy subpath mounting (e.g. `/vault/mcp`). The legacy `--path` spelling is still accepted. |\n\n### Reverse Proxy Subpath Mounts\n\nBy default, HTTP transport serves MCP on `/mcp`. You can run it under a subpath:\n\n```bash\nmarkdown-vault-mcp serve --transport http --http-path /vault/mcp\n```\n\nEquivalent env-based config:\n\n```bash\nMARKDOWN_VAULT_MCP_HTTP_PATH=/vault/mcp\n```\n\nFor reverse proxies, you can either:\n\n- Keep app path at `/mcp` and use proxy rewrite/strip-prefix middleware.\n- Set app path directly to the public path (`/vault/mcp`) and route without rewrite.\n\nWhen OIDC is enabled under a subpath, the configuration is different: the subpath goes in `BASE_URL` only, and `HTTP_PATH` stays at `/mcp`. See [OIDC subpath deployments](https://pvliesdonk.github.io/markdown-vault-mcp/deployment/oidc/#subpath-deployments).\n\nThen your redirect URI is:\n\n```text\nhttps://mcp.example.com/vault/auth/callback\n```\n\n### `index`\n\nBuild the full-text search index.\n\n```bash\nmarkdown-vault-mcp index [--source-dir PATH] [--index-path PATH] [--force]\n```\n\n### `search`\n\nSearch the collection from the CLI.\n\n```bash\nmarkdown-vault-mcp search \u003cquery\u003e [-n LIMIT] [-m {keyword|semantic|hybrid}] [--folder PATH] [--json]\n```\n\n### `reindex`\n\nIncrementally reindex the vault (only processes changed files).\n\n```bash\nmarkdown-vault-mcp reindex [--source-dir PATH] [--index-path PATH]\n```\n\n## MCP Tools\n\n| Tool | Description |\n|------|-------------|\n| `search` | Hybrid full-text + semantic search with optional frontmatter filters |\n| `read` | Read a document or attachment by relative path |\n| `write` | Create or overwrite a document or attachment |\n| `edit` | Replace text in a document — exact match, line-range, or scoped match with normalized fallback |\n| `delete` | Delete a document or attachment and its index entries |\n| `rename` | Rename/move a document or attachment, updating all index entries; pass `update_links=true` to also rewrite backlinks in other notes |\n| `list_documents` | List indexed documents; pass `include_attachments=true` to also list non-markdown files |\n| `list_folders` | List all folder paths in the vault |\n| `list_tags` | List all unique frontmatter tag values |\n| `reindex` | Force a full reindex of the vault |\n| `stats` | Get collection statistics (document count, chunk count, link health metrics, etc.) |\n| `build_embeddings` | Build or rebuild vector embeddings for semantic search |\n| `embeddings_status` | Check embedding provider and index status |\n| `get_backlinks` | Find all documents that link to a given document |\n| `get_outlinks` | Find all links from a document, with existence check |\n| `get_broken_links` | Find all links pointing to non-existent documents |\n| `get_similar` | Find semantically similar notes by document path |\n| `get_recent` | Get the most recently modified notes |\n| `get_context` | Get a consolidated context dossier for a note (backlinks, outlinks, similar, folder peers, tags, modified time) |\n| `get_orphan_notes` | Find all notes with no inbound or outbound links |\n| `get_most_linked` | Find the most-linked-to notes ranked by backlink count |\n| `get_connection_path` | Find the shortest path between two notes via BFS on the undirected link graph (max 10 hops) |\n| `get_history` | List commits that touched a note or the whole vault (git-backed vaults only) |\n| `get_diff` | Return a unified diff of a note between a reference commit/timestamp and HEAD (git-backed vaults only) |\n| `fetch` | Download a file from a URL and save it to the vault as a note or attachment (MCP-to-MCP transfer) |\n| `create_download_link` | Generate a one-time download URL for a vault file — enables MCP-to-MCP file transfer (HTTP/SSE transport only; requires `BASE_URL`) |\n| `browse_vault` | Open the vault explorer SPA in a supporting MCP Apps client |\n| `show_context` | Open the Context Card for a specific note in a supporting MCP Apps client |\n\nWrite tools (`write`, `edit`, `delete`, `rename`, `fetch`) are only available when `MARKDOWN_VAULT_MCP_READ_ONLY=false`.\n\n`browse_vault` and `show_context` are LLM-visible in all clients; when called in an MCP Apps-capable client they open the interactive SPA. Six additional internal tools (`vault_context`, `vault_list`, `vault_read`, `vault_search`, `vault_graph_neighborhood`, `vault_graph_hubs`) use `visibility=\"app\"` and are used by the SPA only — they are never visible to the LLM.\n\n### Resources\n\nMCP resources expose vault metadata as structured JSON that clients can read directly without invoking tools.\n\n| URI | Description |\n|-----|-------------|\n| `config://vault` | Current collection configuration (source dir, indexed fields, read-only state, etc.) |\n| `stats://vault` | Collection statistics (document count, chunk count, embedding count, etc.) |\n| `tags://vault` | All frontmatter tag values grouped by indexed field |\n| `tags://vault/{field}` | Tag values for a specific indexed frontmatter field (template) |\n| `folders://vault` | All folder paths in the vault |\n| `toc://vault/{path}` | Table of contents (heading outline) for a specific document (template) |\n| `similar://vault/{path}` | Top 10 semantically similar notes for a document (template) |\n| `recent://vault` | 20 most recently modified notes with ISO timestamps |\n| `ui://vault/app.html` | Interactive vault explorer SPA for MCP Apps clients |\n\n### Prompts\n\nPrompt templates guide the LLM through multi-step workflows using the vault tools.\n\n| Prompt | Parameters | Description |\n|--------|------------|-------------|\n| `summarize` | `path` | Read a document and produce a structured summary with key themes and takeaways |\n| `research` | `topic` | Search for a topic, synthesize findings, and create a new note at `research/{topic}.md` |\n| `discuss` | `path` | Analyze a document and suggest improvements using `edit` (not `write`) |\n| `create_from_template` | `template_name` (optional) | Discover templates (if needed), read a template, gather user values, and write a new note |\n| `related` | `path` | Find related notes via search and suggest cross-references as markdown links |\n| `compare` | `path1`, `path2` | Read two documents and produce a side-by-side comparison |\n\nWrite prompts (`research`, `discuss`, `create_from_template`) are only available when `MARKDOWN_VAULT_MCP_READ_ONLY=false`.\n\nTemplates are regular markdown files. If placeholder template text pollutes search results, add your templates folder to `MARKDOWN_VAULT_MCP_EXCLUDE` (for example `_templates/**`).\n\n### User-defined prompts\n\nMount a directory of `.md` prompt files to override or extend the built-in prompts. Set `MARKDOWN_VAULT_MCP_PROMPTS_FOLDER` to the path. Each file's frontmatter defines `description`, `arguments` (a list of objects, each with `name`, `description`, and `required` fields), and optional `tags`. A user prompt with the same name as a built-in replaces it.\n\nFor a complete example — including Zettelkasten capture, development, and review prompts — see the [Zettelkasten guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/zettelkasten/).\nFor an alternative action-oriented workflow — Projects, Areas, Resources, Archive with triage, kickoff, and weekly review prompts — see the [PARA guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/para/).\n\n## MCP Apps\n\nThe server ships four browser-based views that MCP clients supporting the MCP Apps protocol can render inline or in fullscreen. They are delivered as a single HTML resource at `ui://vault/app.html` and registered using `visibility=\"app\"` so they appear only in supporting clients and do not clutter the standard tool list. See the [MCP Apps guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/mcp-apps/) for details.\n\n| View | Description |\n|------|-------------|\n| **Context Card** | Displays a note dossier (backlinks, outlinks, similar notes, tags) for the note currently in focus |\n| **Graph Explorer** | Interactive force-directed link graph of the vault, powered by vis-network |\n| **Vault Browser** | Searchable, filterable file tree for navigating the vault without issuing tool calls |\n| **Note Preview** | Full-width markdown preview with frontmatter table and \"Send to Claude\" button |\n\nThe two primary tools exposed to MCP Apps clients are:\n\n| Tool | Description |\n|------|-------------|\n| `browse_vault` | Returns the vault tree structure for the Vault Browser view |\n| `show_context` | Returns the full context dossier for a given note path (used by the Context Card view) |\n\n**Domain configuration:** MCP Apps iframes are sandboxed to a specific Claude app domain. The domain is auto-computed from `MARKDOWN_VAULT_MCP_BASE_URL`. Override with `MARKDOWN_VAULT_MCP_APP_DOMAIN` if your deployment is hosted on a custom domain or behind a proxy that changes the apparent hostname.\n\nVendored dependencies (bundled at build time, no runtime CDN): vis-network (graph rendering), marked.js (markdown rendering), DOMPurify (XSS sanitization), ext-apps SDK (MCP Apps lifecycle).\n\n## Attachments\n\nIn addition to Markdown notes, the server can read, write, delete, rename, and list non-markdown files (PDFs, images, spreadsheets, etc.). All existing tools are overloaded — no new tool names.\n\n### How it works\n\nPath dispatch is extension-based: a path ending in `.md` is treated as a note; any other path is treated as an attachment if the extension is in the allowlist. The `kind` field on returned objects distinguishes the two: `\"note\"` or `\"attachment\"`.\n\n### Reading attachments\n\n`read` returns base64-encoded content for binary attachments:\n\n```json\n{\n  \"path\": \"assets/diagram.pdf\",\n  \"mime_type\": \"application/pdf\",\n  \"size_bytes\": 12345,\n  \"content_base64\": \"\u003cbase64 string\u003e\",\n  \"modified_at\": 1741564800.0\n}\n```\n\n### Writing attachments\n\n`write` accepts a `content_base64` parameter for binary content:\n\n```json\n{ \"path\": \"assets/diagram.pdf\", \"content_base64\": \"\u003cbase64 string\u003e\" }\n```\n\n### Listing attachments\n\n`list_documents` with `include_attachments=true` returns both notes and attachments:\n\n```json\n[\n  { \"path\": \"notes/intro.md\", \"kind\": \"note\", \"title\": \"Intro\", \"folder\": \"notes\", \"frontmatter\": {}, \"modified_at\": 1741564800.0 },\n  { \"path\": \"assets/diagram.pdf\", \"kind\": \"attachment\", \"folder\": \"assets\", \"mime_type\": \"application/pdf\", \"size_bytes\": 12345, \"modified_at\": 1741564800.0 }\n]\n```\n\n### Default allowed extensions\n\n`pdf`, `docx`, `xlsx`, `pptx`, `odt`, `ods`, `odp`, `png`, `jpg`, `jpeg`, `gif`, `webp`, `svg`, `bmp`, `tiff`, `zip`, `tar`, `gz`, `mp3`, `mp4`, `wav`, `ogg`, `txt`, `csv`, `tsv`, `json`, `yaml`, `toml`, `xml`, `html`, `css`, `js`, `ts`\n\nOverride with `MARKDOWN_VAULT_MCP_ATTACHMENT_EXTENSIONS`. Use `*` to allow all non-`.md` files.\n\n\u003e **Hidden directories:** Attachments inside hidden directories (`.git/`, `.obsidian/`, `.markdown_vault_mcp/`, etc.) are never listed, regardless of extension settings. `MARKDOWN_VAULT_MCP_EXCLUDE` patterns are also applied to attachments.\n\n## Authentication\n\nThe server supports four auth modes:\n\n1. **Multi-auth** — both bearer token and OIDC configured; either credential accepted (e.g. Claude web via OIDC + Claude Code via bearer token on the same instance)\n2. **Bearer token** — set `MARKDOWN_VAULT_MCP_BEARER_TOKEN` to a secret string\n3. **OIDC** — full OAuth 2.1 flow via `OIDC_CONFIG_URL`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, and `BASE_URL`\n4. **No auth** — server accepts all connections (default)\n\n**Auth requires `--transport http` (or `sse`).** It has no effect with `--transport stdio`.\n\nFor setup instructions, troubleshooting, and provider-specific guides, see the [Authentication guide](https://pvliesdonk.github.io/markdown-vault-mcp/guides/authentication/).\n\n## Development\n\n```bash\ngit clone https://github.com/pvliesdonk/markdown-vault-mcp.git\ncd markdown-vault-mcp\nuv sync --all-extras --dev\n\n# Run tests\nuv run python -m pytest tests/ -x -q\n\n# Lint and format\nuv run ruff check src/ tests/\nuv run ruff format src/ tests/\n\n# Type check\nuv run mypy src/ tests/\n```\n\u003c!-- DOMAIN-END --\u003e\n\n## GitHub secrets\n\nCI workflows reference three repository secrets. Configure them via **Settings → Secrets and variables → Actions** or with `gh secret set`:\n\n| Secret | Used by | How to generate |\n|---|---|---|\n| `RELEASE_TOKEN` | `release.yml`, `copier-update.yml` | Fine-grained PAT at \u003chttps://github.com/settings/personal-access-tokens/new\u003e with `contents: write` and `pull_requests: write` (the `copier-update` cron opens PRs). Scoped to this repo. |\n| `CODECOV_TOKEN` | `ci.yml` | \u003chttps://codecov.io\u003e — sign in with GitHub, add the repo, copy the upload token from the repo settings page. |\n| `CLAUDE_CODE_OAUTH_TOKEN` | `claude.yml`, `claude-code-review.yml` | Run `claude setup-token` locally and paste the result. |\n\n`GITHUB_TOKEN` is auto-provided — no action needed.\n\n## Troubleshooting\n\n### Moving a scaffolded project\n\n`uv sync` creates `.venv/bin/*` scripts with absolute shebangs pointing at the venv Python. If you move the repo (`mv /old/path /new/path`), `uv run pytest` fails with `ModuleNotFoundError` because the stale shebang resolves to a different interpreter than the venv's site-packages.\n\n**Fix:**\n\n```bash\nrm -rf .venv\nuv sync --all-extras --dev\n```\n\n`uv run python -m pytest` also works as a one-shot workaround.\n\n### `uv.lock` refresh after `copier update`\n\nWhen `copier update` introduces new dependencies, CI runs `uv sync --frozen` which fails against a stale lockfile. Run `uv lock` locally and commit the refreshed `uv.lock` alongside accepting the copier-update PR.\n\n\u003c!-- ===== TEMPLATE-OWNED SECTIONS END ===== --\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpvliesdonk%2Fmarkdown-vault-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpvliesdonk%2Fmarkdown-vault-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpvliesdonk%2Fmarkdown-vault-mcp/lists"}