vault-server-MCP

vault-server-MCP

Remote MCP server for Obsidian vault access, giving Claude read/search/archive access to markdown notes via OAuth 2.1 + PKCE auth.

Category
访问服务器

README

MCP server — Obsidian vault access

Remote MCP server (Streamable HTTP transport, OAuth 2.1 + PKCE auth) that gives Claude read/search/archive access to a folder of markdown notes (an Obsidian vault).

Auth is OAuth rather than a plain static token because claude.ai's custom connector UI only has fields for a URL and an OAuth Client ID/Secret — there is no field for a raw bearer token. See "Notes on auth" below for what that means in practice.

Project layout

app/
  config.py          # .env-driven settings (VAULT_PATH, OAUTH_CLIENT_ID/SECRET, HOST, PORT, LOG_LEVEL, LOG_FILE, PUBLIC_HOSTNAME)
  mcp_instance.py     # the shared FastMCP instance, wired with the OAuth provider + DNS-rebinding protection
  oauth_provider.py   # minimal single-tenant OAuth 2.1 authorization server (see below)
  audit_log.py        # file-based audit logger, wraps every tool call
  vault.py            # path-safety + filesystem logic (list/search/read/archive)
  tools.py            # the 5 MCP tools, thin wrappers over vault.py
  server.py           # builds the ASGI app from the FastMCP instance
main.py               # entrypoint: uvicorn.run(app, host=..., port=...)
sample_vault/         # tiny fixture vault for local testing
scripts/manual_test.py  # scripted client: runs the OAuth dance, then exercises all 5 tools
deploy/mcp-obsidian.service  # example systemd (--user) unit

Tools exposed

  1. read_index() — reads the vault's entry point, configured via INDEX_PATH in .env (default README.md). Call this first.
  2. list_notes(folder=None) — lists files/folders, excludes _trash/.
  3. search_notes(query, limit=10) — full-text search across .md files, excludes _trash/.
  4. read_note(path) — returns full file content.
  5. create_note(path, content) — creates a new file; fails if one already exists there.
  6. edit_note(path, content) — overwrites an existing file's full content; fails if it doesn't exist yet.
  7. archive_note(path) — moves a note into _trash/ (never deletes physically).

create_note/edit_note can't write directly into _trash/ — that tree is only ever populated by archive_note.

All path-taking tools reject absolute paths and .. segments (path traversal protection lives in app/vault.py::resolve_safe_path).

Local setup

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env

Edit .env:

  • VAULT_PATH=./sample_vault for local testing (a real vault copy also works).
  • OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET — set real random values, e.g. openssl rand -hex 32 for each.
  • Leave PUBLIC_HOSTNAME empty for local-only use.

Run it:

python main.py

It binds to 127.0.0.1:8000 by default (see .env). The MCP endpoint is at http://127.0.0.1:8000/mcp; OAuth endpoints (/authorize, /token, /.well-known/oauth-authorization-server, …) live at the same host:port.

Testing before connecting to claude.ai

1. Auth check with curl — hitting the MCP endpoint with no token must return 401 with a WWW-Authenticate header pointing at the protected resource metadata (this is what tells claude.ai where to find the OAuth endpoints):

curl -i http://127.0.0.1:8000/mcp
curl -i http://127.0.0.1:8000/.well-known/oauth-authorization-server

2. Scripted smoke test — runs the full OAuth 2.1 + PKCE flow against your own server (no browser needed — app/oauth_provider.py auto-approves, since the real gate is knowing the client secret), then exercises all 5 tools against sample_vault/:

OAUTH_CLIENT_ID=<from .env> OAUTH_CLIENT_SECRET=<from .env> python scripts/manual_test.py

The script creates, edits, and archives a scratch file under sample_vault/_scratch/ as part of the run, so it's safe to re-run repeatedly without resetting sample_vault/.

3. MCP Inspector (interactive, closest to how claude.ai will talk to it):

npx @modelcontextprotocol/inspector

In the UI: Transport = Streamable HTTP, URL = http://127.0.0.1:8000/mcp. Inspector will detect the 401 + metadata and walk you through the OAuth flow itself, prompting for the Client ID/Secret from your .env.

Connecting to claude.ai

Team/Enterprise plan, as an owner: Admin settings → Connectors → Add custom connector →

  • URL: https://<your-public-hostname>/mcp
  • Advanced settings → OAuth Client ID: value of OAUTH_CLIENT_ID
  • Advanced settings → OAuth Client Secret: value of OAUTH_CLIENT_SECRET

Each member then goes to Settings → Connectors, finds the connector, and clicks "Connect" — this runs them through the OAuth consent screen (which auto-approves) and gets them their own access token.

Deploying on the VPS (systemd, user-level service)

This runs as a systemctl --user service under your own account — no dedicated system user or root-owned /opt directory needed. The only root actions required, ever, are creating /vault (owned by your user) and enabling "lingering" so the user service can run without an active login session.

  1. One-time, as root (or via sudo):

    sudo mkdir -p /vault && sudo chown "$USER":"$USER" /vault
    sudo loginctl enable-linger "$USER"
    
  2. Copy the project to ~/mcp-obsidian on the VPS, create a venv there, pip install -r requirements.txt.

  3. Create ~/mcp-obsidian/.env with VAULT_PATH=/vault, strong OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET values, PUBLIC_HOSTNAME set to the hostname your reverse proxy serves (e.g. a nip.io address or your own domain), and LOG_FILE=~/mcp-obsidian/logs/server.log (expand ~ to the real home path — systemd EnvironmentFile doesn't expand ~).

  4. Install the unit file:

    mkdir -p ~/.config/systemd/user
    cp deploy/mcp-obsidian.service ~/.config/systemd/user/
    systemctl --user daemon-reload
    systemctl --user enable --now mcp-obsidian
    systemctl --user status mcp-obsidian
    
  5. Point your reverse proxy (Caddy/nginx) at 127.0.0.1:8000, forwarding the Authorization header through unchanged (this is the default behavior for both — just don't strip it in your config). The proxy also needs to own HTTPS for the exact hostname in PUBLIC_HOSTNAME, since that hostname is baked into the OAuth issuer/resource URLs.

Notes on auth

The server is its own minimal OAuth 2.1 authorization server (app/oauth_provider.py), not just a resource server checking someone else's tokens. It registers exactly one pre-shared client — identified by OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET from .env — and auto-approves every /authorize request without a login screen. This is intentional for a single-company internal tool: the actual security boundary is knowing the client secret (kept by whoever adds the connector in claude.ai), exactly as it was with the plain static bearer token this replaced. PKCE, redirect_uri matching, client-secret verification, and access-token expiry are all enforced by the mcp SDK itself — oauth_provider.py only stores and retrieves codes/tokens (in memory; restarting the service invalidates issued tokens, so anyone connected has to click "Connect" again).

If you ever need real per-user login (rather than one shared credential per company), swap StaticClientOAuthProvider for a provider that redirects to a real identity provider (Google Workspace, Microsoft Entra, etc.) in authorize() — the rest of the server (tools.py, vault.py, the MCP wiring) doesn't need to change.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选