关于 Obsidian Mcp Server
Obsidian MCP Server 通过 MCP 连接,让 AI 助手操作笔记及知识库结构。它提供笔记编辑、元数据和链接工具,并可选用 Postgres、pgvector 与 Ollama 实现本地语义搜索。
主要功能
- 01创建、编辑、移动和读取笔记
- 02管理标签与 YAML frontmatter
- 03搜索笔记并检查链接
- 04编辑章节与目录
- 05可选的本地语义搜索
适用场景
- 01通过 AI 助手创建和整理笔记
- 02按文本、标签或反向链接查找笔记
- 03审查标签及未解析的 wiki 链接
- 04使用本地嵌入搜索相关笔记
MCP 相关
该服务通过 HTTP 和 SSE 将 Obsidian 知识库操作作为 MCP 工具提供,不直接支持 stdio。配置 DATABASE_URL 后,可启用可选的语义搜索工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Obsidian MCP 服务
一个 Model Context Protocol (MCP) 服务,可让 Claude 等 AI 助手与您的 Obsidian 知识库交互。通过自然对话访问笔记、创建内容、管理标签并搜索知识库。
功能
笔记管理
- CRUD 操作:创建、读取、更新和删除笔记(需安全确认)
- 写入模式:覆盖、追加或前置内容
- 批量读取:在单个请求中读取多篇笔记
- 文件信息:无需读取内容即可获取元数据(适用于大型知识库)
- 移动/复制:重新整理知识库结构
- 分节操作:按标题读取、追加或替换特定章节
Frontmatter 与标签
- Frontmatter 解析:将 YAML frontmatter 作为结构化 JSON 获取/设置(由 gray-matter 提供支持)
- 标签管理:添加/移除标签(frontmatter 或行内)
- 标签审计:查找缺少必需标签的笔记
- 标签搜索:列出所有标签及其使用次数
搜索与链接
- 全文搜索:搜索内容和文件名,并显示上下文
- 反向链接:查找所有指向指定笔记的笔记
- 失效链接:检测无法解析的维基链接
- 查找与替换:批量替换文本,支持正则表达式
语义搜索(可选)
- 按含义搜索:在按标题感知方式拆分的文本块上进行混合向量 + 全文检索,由 Postgres + pgvector 和来自 Ollama 的本地嵌入提供支持
- 相似笔记:根据整篇笔记的嵌入,查找与指定笔记最相似的笔记
- 增量索引器:使用同一镜像运行的第二个进程,用于轮询知识库并仅重新嵌入发生变化的内容;适用于无法使用 inotify 的 NFS/SMB
- 数据不会离开您的网络:嵌入在本地计算;除非设置了
DATABASE_URL,否则此功能完全关闭
目录操作
- 创建/删除/重命名:全面的目录管理
- 列出内容:浏览知识库结构
性能
- 令牌优化:可选的精简响应模式(响应缩小 40-60%)
- 高效扫描:无需读取内容即可获取文件信息
- SSE 传输:无需本地安装即可远程访问
快速开始
客户端如何连接:该服务仅通过 HTTP + SSE 使用 MCP 通信(不支持 stdio 模式)。启动服务(使用 npx、Docker 或从源代码运行),然后让客户端连接到 http://HOST:PORT/sse。仅支持 stdio 服务的客户端可通过 mcp-remote 连接,如下所示。服务会监听 PORT 上的所有网络接口,因此请设置 API_KEY(参见身份验证),或将其限制在回环地址上(例如在 Docker 中使用 -p 127.0.0.1:3001:3000),除非您希望整个网络都能读写您的知识库。
npx(最快)
需要 Node.js 20.19 或更高版本。此命令会在前台启动服务(默认端口为 3000):
VAULT_PATH=/path/to/your/vault npx @smith-and-web/obsidian-mcp-server
使用选项:
VAULT_PATH=/path/to/vault PORT=3001 COMPACT_RESPONSES=true npx @smith-and-web/obsidian-mcp-server
Docker(生产环境推荐)
使用 GitHub Container Registry 中的预构建镜像:
docker run -d \
--name obsidian-mcp \
-v /path/to/your/vault:/vault:rw \
-p 127.0.0.1:3001:3000 \
-e VAULT_PATH=/vault \
ghcr.io/smith-and-web/obsidian-mcp-server:latest
latest 会跟随最新版本;生产环境请固定版本标签(例如 :1.6.0)。只有在设置了 API_KEY 且希望其他机器连接时,才去掉 127.0.0.1:。
或使用 Docker Compose:
-
创建
docker-compose.yml:services: obsidian-mcp: image: ghcr.io/smith-and-web/obsidian-mcp-server:latest container_name: obsidian-mcp restart: unless-stopped volumes: - /path/to/your/vault:/vault:rw ports: - "127.0.0.1:3001:3000" environment: - VAULT_PATH=/vault -
启动服务
docker compose up -d -
验证服务是否正在运行
curl http://localhost:3001/health
本地开发
-
安装依赖
npm install -
设置环境变量
export VAULT_PATH=/path/to/your/vault export PORT=3000 -
启动服务
npm start # Or with auto-reload: npm run dev
AI 助手配置
Claude Desktop
将以下内容添加到 Claude Desktop 配置文件中:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
选项 1:本地服务(通过 mcp-remote)
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3001/sse"]
}
}
}
选项 2:使用 HTTPS 的远程服务
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://your-domain.com/sse"]
}
}
}
服务必须已在运行(参见“快速入门”)。Claude Desktop 会启动 mcp-remote,将其 stdio 桥接到服务的 SSE 端点;Claude Desktop 无法直接启动此服务,因为该服务不使用 stdio 通信。
Cursor
将以下内容添加到 Cursor MCP 设置中(Settings → MCP):
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3001/sse"]
}
}
}
其他 MCP 客户端
Claude Code 直接支持 SSE 服务:
claude mcp add --transport sse obsidian http://localhost:3001/sse
# with an API key:
claude mcp add --transport sse obsidian https://your-host/sse --header "Authorization: Bearer your-secret-key"
任何其他兼容 MCP 的客户端都可以使用 mcp-remote 连接:
npx mcp-remote http://localhost:3001/sse
如果客户端原生支持 SSE 服务,也可以直接连接到 SSE 端点 http://localhost:3001/sse。
可用工具
笔记操作
| 工具 | 描述 |
|---|---|
read-note | 读取笔记内容(支持使用 frontmatterOnly 提高效率) |
read-multiple-notes | 批量读取多篇笔记 |
create-note | 创建新笔记 |
edit-note | 替换笔记内容 |
write-note | 以覆盖、追加或前置模式写入 |
delete-note | 删除笔记(需要确认) |
move-note | 移动/重命名笔记 |
duplicate-note | 将笔记复制到新位置 |
get-notes-info | 获取文件元数据,而不读取内容 |
目录操作
| 工具 | 描述 |
|---|---|
list-vault | 列出文件和目录 |
create-directory | 创建新目录 |
delete-directory | 删除目录(可递归) |
rename-directory | 重命名/移动目录 |
Frontmatter 与标签
| 工具 | 描述 |
|---|---|
get-frontmatter | 将 YAML frontmatter 作为 JSON 获取 |
update-frontmatter | 原地更新 frontmatter 字段(未修改的字段会保留原有文本;遇到无效 YAML 时会拒绝操作) |
add-tags | 向 frontmatter 或行内添加标签 |
remove-tags | 从笔记中移除标签 |
list-tags | 列出所有标签及其数量 |
find-notes-by-tag | 查找带有特定标签的笔记 |
search-missing-tag | 查找缺少某个标签的笔记 |
audit-tags | 审查文件夹中是否包含必需标签 |
搜索与链接
| 工具 | 描述 |
|---|---|
search-vault | 全文搜索并显示上下文 |
get-backlinks | 查找链接到某篇笔记的其他笔记 |
find-broken-links | 查找无法解析的 wiki 链接 |
find-replace | 批量查找和替换 |
语义搜索(仅在设置了 DATABASE_URL 时可用)
| 工具 | 描述 |
|---|---|
semantic-search | 混合向量 + 全文搜索;按 directory、tags、frontmatter type 和 status 筛选;`mode: hybrid |
similar-notes | 按质心嵌入查找与某条笔记最相似的笔记 |
index-status | 模型、计数、索引器上次运行时间和延迟 |
reindex-note | 立即重新索引一条笔记 |
search-feedback | 记录搜索评分以及已使用或预期的路径;仅在启用学习日志时可用 |
recommend-notes | 使用 best_score 或 average_vector 查找与正面示例相似、与负面示例不同的笔记;需要语义搜索 |
maintenance-decision | 记录对维护发现的明确接受或拒绝决定;需要语义搜索 |
请参阅语义搜索了解设置方法。
分节操作
| 工具 | 描述 |
|---|---|
read-section | 读取标题下的内容 |
append-to-section | 在某个分节末尾追加内容 |
replace-section | 替换分节内容 |
append-to-file | 在文件末尾追加内容 |
insert-at-marker | 在文本标记处插入内容 |
list-headings | 列出笔记中的所有标题 |
架构
┌─────────────────┐ HTTPS/SSE ┌──────────────────┐
│ Claude Desktop │ ◄────────────────► │ MCP Server │
└─────────────────┘ │ (Express.js) │
└────────┬─────────┘
│
┌────────▼─────────┐
│ VaultManager │
│ (File System) │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Obsidian Vault │
│ (Markdown) │
└──────────────────┘
项目结构
obsidian-mcp-server/
├── src/
│ ├── index.ts # Express server entry point
│ ├── vault/
│ │ ├── VaultManager.ts # Core vault operations
│ │ └── frontmatter.ts # YAML parsing utilities
│ ├── tools/
│ │ ├── definitions.ts # MCP tool schemas
│ │ ├── handlers.ts # Tool execution logic
│ │ └── index.ts # Tool exports
│ ├── server/
│ │ ├── mcp.ts # MCP protocol handlers
│ │ └── middleware.ts # Express middleware
│ ├── index/ # Optional semantic search
│ │ ├── config.ts # DATABASE_URL / OLLAMA_URL / EMBED_MODEL
│ │ ├── exclusions.ts # What the indexer never reads
│ │ ├── chunker.ts # Heading-aware chunking
│ │ ├── embedder.ts # Ollama client (+ a fake for tests)
│ │ ├── ranking.ts # Fusion, weights, per-note cap and env validation
│ │ ├── store.ts # pgvector schema, upserts, hybrid search
│ │ ├── scanner.ts # Vault walk and change detection
│ │ └── SearchService.ts # What the MCP tools call
│ ├── maintain/ # Report configuration, decisions, renderer and worker
│ ├── maintain.ts # Report-only maintenance CLI
│ ├── learn/ # Learn log, redaction, configuration and reports
│ ├── learn-report.ts # Read-only learn-report CLI
│ ├── eval/ # Golden validation, metrics and fixed ranking grid
│ ├── eval.ts # Read-only brain-eval CLI entry point
│ ├── indexer.ts # Indexer entry point (second container)
│ └── types/
│ └── index.ts # TypeScript type definitions
├── sql/001_schema.sql # Index schema (dimension substituted at start)
├── tests/ # Vitest unit tests
├── Dockerfile
├── docker-compose.yml
├── package.json
└── README.md
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
PORT | 3000 | 服务端口 |
VAULT_PATH | /vault | Obsidian 知识库的路径 |
COMPACT_RESPONSES | false | 启用精简响应键,令响应缩小 40%–60% |
API_KEY | (none) | 用于身份验证的 API 密钥。设置后,必须提供 Bearer 令牌(以恒定时间进行比较) |
REQUIRE_TAILSCALE_LOGIN | (none) | 可选。要求 Tailscale-User-Login 标头(由 tailscale serve 设置)与此登录名相同。只有在服务只能通过回环地址上的 tailscale serve 访问时,此设置才可信 |
SSE_KEEPALIVE_SECONDS | 30 | GET /sse 流中 : ping SSE 注释的发送间隔秒数;0 表示禁用。有些客户端会在约五分钟没有收到任何字节后丢弃流并重新连接,且不会再次发送 initialize,导致会话丢失客户端名称 |
MCP_INSTRUCTIONS_FILE | (none) | 可选。文本或 Markdown 文件的路径;该文件会作为 MCP instructions 随 initialize 结果发送给客户端,用于说明此知识库的内容及适用场景。多数客户端会将其加入模型上下文。文件不存在会导致启动错误。请保持内容简短(有些客户端会在约 2 KB 处截断) |
MCP_INSTRUCTIONS | (none) | 相同内容,内联提供。若设置了 MCP_INSTRUCTIONS_FILE,则忽略此项 |
DATABASE_URL | (none) | Postgres + pgvector 连接字符串。启用语义搜索;未设置时,功能关闭 |
OLLAMA_URL | http://localhost:11434 | 用于生成嵌入的 Ollama 服务 |
EMBED_MODEL | nomic-embed-text | Ollama 嵌入模型;维度将从模型中自动获取 |
LEARN_LOG | false | 启用可选的学习日志;配置无效时会禁用日志记录 |
LEARN_REDACT_PATTERNS_FILE | unset | 启用日志记录时必需的、可读取的正则表达式文件 |
LEARN_RETENTION_DAYS | 30 | 清除查询文本、嵌入和反馈备注前保留的天数;整数范围为 1–3650 |
INDEX_COLLECTION | default | 此知识库在共享数据库中的集合名称 |
MAINTAIN_REPORT_DIR | dry 模式除外,必需 | 用于维护报告的现有可写目录 |
MAINTAIN_REPORT_VAULT_DIR | empty | 知识库内的报告目录,用于生成记录中的报告路径 |
MAINTAIN_TZ | UTC | 用于报告时间戳和 ISO 周数的 IANA 时区 |
MAINTAIN_LINK_COSINE | 0.85 | 缺失链接的余弦阈值,必须严格介于 0 和 1 之间 |
MAINTAIN_DUP_COSINE | 0.92 | 近似重复项的余弦阈值,必须严格介于 0 和 1 之间 |
MAINTAIN_DUP_TITLE_SIM | 0.6 | 近似重复项的标题相似度,必须严格介于 0 和 1 之间 |
MAINTAIN_HUB_DAYS | 14 | 用于判定过时中心笔记的关联笔记更新间隔;整数范围为 1–365 |
MAINTAIN_SUMMARY_DROP | 0.05 | 摘要与正文的余弦相似度下降阈值,必须严格介于 0 和 1 之间 |
MAINTAIN_MAX_PER_CLASS | 10 | 每次运行中每类新增发现的最大数量;整数范围为 1–100 |
SEARCH_FUSION | rrf | 融合方式:rrf(加权倒数排名)或 dbsf(基于分布的分数融合) |
SEARCH_RRF_K | 60 | RRF 排名常数,整数范围为 1–1000;在 DBSF 模式下会验证此值,但不会使用 |
SEARCH_W_VECTOR | 1 | 向量分支权重,有限数值,范围为 0–10 |
SEARCH_W_TEXT | 1 | 全文搜索分支权重,有限数值,范围为 0–10;两项权重不能同时为零 |
SEARCH_PER_NOTE_CAP | 0 | 每篇笔记的最大分块数,整数范围为 0–8;0 表示不设上限 |
INDEX_INTERVAL_SECONDS | 60 | 仅用于索引器:知识库扫描间隔秒数 |
INDEX_VERBOSE | false | 仅用于索引器:记录每个已索引或已移除文件的日志 |
EMBED_BATCH_SIZE | 16 | 每次 Ollama 请求处理的文本数量 |
EMBED_PROVIDER | ollama | ollama 或 fake(用于测试和冒烟运行的确定性哈希嵌入;不可用于实际搜索) |
EMBED_DIM | (auto) | fake 提供程序的嵌入维度(默认值为 64);使用 Ollama 时会忽略此项,因为维度会从模型中自动获取 |
学习日志
可选的度量层需要语义搜索,默认关闭。它会记录三种搜索工具(semantic-search、similar-notes、search-vault)、成功的读取操作(read-note、read-multiple-notes、read-section)、明确反馈,以及包含客户端名称的会话开始和关闭事件。会话包括旧版 SSE 流(GET /sse + /message),以及自 1.10.1 起的 POST /sse 客户端:initialize 会返回一个 Mcp-Session-Id 标头,后续请求回传该标头时会记录在对应会话下,带该标头的 DELETE /sse 会关闭会话。不带该标头的 POST /sse 请求会以客户端名称 post 记录,且不关联会话。搜索排序不变;搜索绝不会读取这些日志。
设置 LEARN_LOG=true,并将 LEARN_REDACT_PATTERNS_FILE 指向可读取的 UTF-8 文件。空行和注释会被忽略,其余各行会去除首尾空白,并作为 JavaScript 正则表达式源代码进行编译,匹配时不区分大小写。示例文件如下:
# comment
\bexample-term\b
八种内置模式还会检测私钥标头、访问密钥格式、JWT、托管式 Git 令牌、密钥赋值、Bearer 凭据、URL 令牌参数和 64 位十六进制字符串。敏感查询(包括相似笔记路径)及其嵌入向量会在存储前清除。敏感字符串参数会替换为 [redacted];敏感反馈备注会清除,敏感反馈路径会丢弃,敏感读取路径绝不会记录。针对特定站点的模式应放在外部文件中。配置无效时会禁用日志记录,但不会禁用搜索,并会在启动时报告。
已记录的搜索会返回附加的 requestId。将其传递给读取操作的可选 requestId 参数,然后调用 search-feedback,并提供 requestId、rating(positive、negative、neutral)和 usedPaths。expectedPaths 和简短的 note 均为可选项。source 默认为 agent;只有在收到明确的人工反馈时才使用 user。未知 ID 会返回 { recorded: false, reason: 'unknown requestId' }。带有未知 ID 的读取操作会被记录,但不会关联搜索。
搜索、读取和会话写入均采用即发即弃方式:不会延迟工具响应,失败会以 learn-log: 记录。反馈操作会等待完成。索引器循环至多每天应用一次保留策略,--once 除外。超过 LEARN_RETENTION_DAYS(默认值为 30)后,查询文本、查询嵌入向量和反馈备注会被清除。路径、排名、时间戳、已检查的参数、客户端名称和反馈路径会保留。即使日志记录处于关闭状态,sql/002_learning.sql 也会创建四张 learn 表;维度重置后会重新创建嵌入向量列。如果需要保留查询数据,请勿将 learn 表数据纳入数据库转储,并在备份中排除原始数据库文件。
针对现有数据库运行只读报告:
node dist/learn-report.js --days 30 --out /existing-directory/learn-report.json
--days 默认为 30(范围为 1–365);输出目录必须已存在。报告使用只读连接,不受排序和日志记录环境设置影响,并输出计数和比率,不包含查询文本。标准输出会按报告顺序列出点分隔的键,最后显示 VOLUME GATE: pass|fail。
报告涵盖所选集合和时间范围。会话包括按客户端统计的数量,以及以分钟为单位的已关闭时长中位数。搜索包括按工具统计的数量和已脱敏内容占比。关联的语义读取采用读取路径中的最低排名:usedAt1、usedAt3、usedAt8 分别表示关联读取的占比,missRate 表示未出现在结果中的占比。linkedSearchRate 表示有相关读取的语义搜索占比,linkedSemanticPerDay 表示其每日数量。改写率和回退率统计会话关联的语义搜索后 120 秒内又进行语义搜索或文本搜索的情况。反馈覆盖率表示收到反馈的语义搜索占比;反馈来源数量和负面反馈率涵盖所有反馈。分母为零时的比率和空中位数均为 null;数字四舍五入到小数点后四位。每日关联语义搜索数量四舍五入后达到 30,即通过量级门槛。来自 Agent 的反馈用于衡量遵循情况;用户更正则提供独立标签。
语义搜索
默认关闭。设置 DATABASE_URL 后,服务器(增加四个工具)和索引器(作为第二个进程运行,以保持索引最新)都会启用语义搜索。所有组件均在本地运行:启用了 pgvector 扩展的 Postgres,以及用于生成嵌入的 Ollama。
工作原理
- 知识库仍是真实数据的唯一来源。 索引是派生数据,可以删除后重建(
--reindex)。 - 分块会识别标题层级。 笔记按 H1–H3 标题拆分;超过约 800 个 token 的章节会进一步拆分,并保留重叠部分;每个块都带有其面包屑路径(
Title › H2 › H3)。第 0 块包含标题和 frontmatter 中的summary,因此,一行摘要是判断“哪篇笔记能回答这个问题”的最强信号。 - 检索采用混合方式。 基于 HNSW 索引的余弦相似度检索与 Postgres 全文搜索并行运行,再通过可配置的加权倒数排名融合(默认方式)或基于分布的分数融合(DBSF)进行合并,因此改写表达和精确标识符(主机名、工单编号)都能奏效。
- 变更检测采用轮询,而非文件监视器,因为 inotify 不会通过 NFS 或 SMB 传播。索引器每个间隔都会对每个文件执行
stat,仅对修改时间或大小发生变化的文件计算哈希,并且只对内容发生变化的文件重新生成嵌入。 - 排除规则会在读取前按路径强制执行:
.obsidian/、.trash/、.git/、Synology 的#recycle/@eaDir/@tmp/、AppleDouble 文件._*、点文件、非.md文件、包含.noindex文件的任何目录,以及 frontmatter 中设置index: false的任何笔记。 - 链接解析方式与 Obsidian 一致。
[[Areas/note]]按路径解析;如果知识库中恰好只有一篇笔记具有该基本名称,则[[note]]可解析;如果有两篇,则保持未解析状态(会记录,目标为NULL)。删除两篇重名笔记中的一篇后,另一篇会在下次编入索引时重新解析。 - 模型是架构事实的一部分。 首次运行时,嵌入模型及其维度会记录在数据库中;若使用不同模型,则在执行
--reindex之前不会编入索引。
评估搜索质量(brain-eval)
使用 npm run build 构建,然后使用与索引相同的数据库、集合和嵌入配置运行:
node dist/eval.js --golden /private/golden.json --grid --out /private/runs/grid.json
# Or evaluate one configuration alongside the baseline:
node dist/eval.js --golden /private/golden.json --config '{"wText":1.5,"perNoteCap":2}' --out /private/runs/candidate.json
# npm run eval -- <the same arguments> also works.
输出目录必须已存在。CLI 以只读模式打开数据库:不会应用架构变更、重新索引或写入索引。嵌入模型和维度必须与存储的元数据匹配,并且所有预期笔记都必须已存在于集合中。每个查询只嵌入一次,其向量会在各项配置间复用。固定网格包含 40 项配置,基线为 v1.8.0 的 rrf-k60-v1-t1-cap0。CLI 会忽略环境中已有的 SEARCH_* 排名设置;--config 仅将提供的字段合并到基线上。无效的排名环境设置会导致服务启动失败。
黄金文件是私有评估数据,且必须恰好包含以下键:
{
"version": 1,
"created": "2026-01-01",
"notes": "Synthetic example only",
"queries": [
{
"id": "example-1",
"category": "exact-id",
"query": "synthetic search phrase",
"expected": ["Example.md"]
}
]
}
查询 ID 必须唯一,查询字符串不得为空,预期列表不得为空且其中的 .md 路径必须唯一。类别依次为 exact-id、paraphrase、decision-why、multi-hop 和 recent,并按此顺序报告。未知键会被拒绝。请将黄金数据集和评估报告保存在公共代码仓库之外。
指标按结果顺序统计不同的笔记路径,且最多请求八个片段:
mrr:第一个预期笔记排名的倒数均值;未命中时为零。recall5:前 5 项命中率,表示前五个笔记中是否存在任意一个预期笔记。ndcg8:前八个笔记的二元相关性归一化折损累计增益。distinctNotes:返回的不同笔记数量的均值。指标也会按类别细分,JSON 报告会将其四舍五入到小数点后四位。
候选项必须将 MRR 至少提高 0.0300,保持每个类别的命中率不变,并保留基线命中数在前五位以内的每个查询。比较时会将指标四舍五入为万分之一的整数。通过门槛的候选项按 MRR、命中率、NDCG 从高到低排序;平局时优先选择变更的排名字段较少者,其次选择 ID 按字典序较小者。若没有候选项通过,则采用基线,且 changed: false。JSON 包含黄金文件的 SHA-256、每个查询的笔记列表、门槛原因以及获胜配置的环境变量;标准输出会打印排名表、类别对比和最终的 WINNER 行。
退出代码:0 表示报告已完成(包括排名未变);2 表示输入问题(参数、黄金文件验证、模型不匹配或缺少预期路径);1 表示其他运行时故障(数据库、嵌入服务或输出写入)。
搜索要求使用 pgvector 0.8.0 或更高版本:每个事务都会使用严格顺序的迭代式 HNSW 扫描,经过筛选的搜索会将 hnsw.ef_search 提高到 100。默认 RRF 分数与 v1.8.0 相同;完全相同的分数按片段位置、再按 ID 排序。DBSF 分数范围从零到两路权重之和。
索引可识别的 Frontmatter
---
title: ubuntubox — NFS mount gate # else first H1, else file name
type: area # filterable: semantic-search { type }
status: active # filterable: semantic-search { status }
tags: [infra, nas] # filterable: semantic-search { tags }
summary: One line that answers "what is this note about".
index: true # false = never indexed
---
无效的 Frontmatter 会被报告,而不会被隐藏。 若笔记的 --- 区块不是有效的 YAML(常见原因是未加引号的值中含有 : ,例如 summary: Finished it: all done;应加引号:summary: "Finished it: all done"),该笔记仍会根据正文建立索引,但不会包含其元数据。索引器在索引此类笔记时以及每次启动时都会记录 WARN invalid frontmatter in <path>: <YAML error>;每次处理的日志行都会包含 invalid_frontmatter=<count>,而 index-status 会返回 invalidFrontmatter: { count, notes: [{ path, error }] }(最多 20 条)。reindex-note 会返回一个 warning。修复笔记后,该标记会在下一次处理时清除。
设置
- 数据库:任何支持 pgvector 的 Postgres 16+。首次启动时会根据
sql/001_schema.sql创建架构;无需手动执行任何操作。构建 HNSW 索引需要共享内存:为 Postgres 容器设置shm_size: 1g。 - 嵌入:运行
ollama pull nomic-embed-text(768 维,8k 上下文)或bge-m3(1024 维)。 - 服务:在服务的环境中添加
DATABASE_URL、OLLAMA_URL、EMBED_MODEL(以及可选的INDEX_COLLECTION)。/health端点会报告semanticSearch: enabled。 - 索引器:使用相同镜像再次运行,并设置
command: ["node", "dist/indexer.js"]、相同的环境变量,以及以只读方式挂载的库。node dist/indexer.js --once --verbose会执行单次遍历后退出;如果有任何文件处理失败,则以非零状态码退出。
services:
obsidian-mcp:
image: ghcr.io/smith-and-web/obsidian-mcp-server:latest
environment:
VAULT_PATH: /vault
API_KEY: ${API_KEY}
DATABASE_URL: postgres://brain:${DB_PASSWORD}@brain-db:5432/brain
OLLAMA_URL: http://ollama:11434
EMBED_MODEL: nomic-embed-text
INDEX_COLLECTION: secondbrain
volumes:
- /path/to/vault:/vault:rw
ports: ["127.0.0.1:3001:3000"]
obsidian-indexer:
image: ghcr.io/smith-and-web/obsidian-mcp-server:latest
command: ["node", "dist/indexer.js"]
environment:
VAULT_PATH: /vault
DATABASE_URL: postgres://brain:${DB_PASSWORD}@brain-db:5432/brain
OLLAMA_URL: http://ollama:11434
EMBED_MODEL: nomic-embed-text
INDEX_COLLECTION: secondbrain
INDEX_INTERVAL_SECONDS: "60"
volumes:
- /path/to/vault:/vault:ro
brain-db:
image: pgvector/pgvector:0.8.2-pg16
shm_size: 1g
environment:
POSTGRES_DB: brain
POSTGRES_USER: brain
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ./brain-db:/var/lib/postgresql/data
ollama:
image: ollama/ollama:latest
volumes:
- ./ollama:/root/.ollama
索引器 CLI
| 命令 | 作用 |
|---|---|
node dist/indexer.js | 持续运行,每隔 INDEX_INTERVAL_SECONDS 执行一次遍历;收到 SIGTERM 时正常停止 |
node dist/indexer.js --once | 执行一次遍历;如果有任何文件出错,则以状态码 1 退出 |
node dist/indexer.js --reindex | 删除此集合并重新构建(更改 EMBED_MODEL 后必须执行) |
npx -p @smith-and-web/obsidian-mcp-server obsidian-mcp-indexer | 使用 npm 包运行相同的索引器(也接受 --once、--reindex、--verbose) |
--verbose / INDEX_VERBOSE=true | 记录每个已索引或已移除的文件 |
安全说明
- 索引器只需要对库的读取权限;请以
:ro方式挂载。 - 分块文本以明文形式存储在 Postgres 中。请像对待库一样保护数据库:使用相同的网络边界和备份策略,并且不要索引那些不应与库中其他内容共用访问密钥的材料(使用
index: false或.noindex目录)。 - 嵌入数据不会离开 Ollama 主机。此功能不涉及任何外部 API。
API 端点
| 端点 | 方法 | 描述 |
|---|---|---|
/health | GET | 健康检查 |
/sse | GET | MCP 的 SSE 端点 |
/sse | POST | 直接调用 MCP 协议 |
/message | POST | SSE 传输消息 |
身份验证
服务支持可选的 API 密钥身份验证。设置 API_KEY 后,所有 /sse 和 /message 端点都需要身份验证。/health 端点仍可公开访问。
启用身份验证
设置 API_KEY 环境变量:
# Docker
docker run -d \
--name obsidian-mcp \
-v /path/to/vault:/vault:rw \
-p 3001:3000 \
-e VAULT_PATH=/vault \
-e API_KEY=your-secret-key \
ghcr.io/smith-and-web/obsidian-mcp-server:latest
# npx
API_KEY=your-secret-key VAULT_PATH=/path/to/vault npx @smith-and-web/obsidian-mcp-server
身份验证方式
客户端在 Authorization 标头中发送密钥:
Authorization: Bearer your-secret-key
?api_key= 查询参数已在 1.6.0 中移除(它会出现在访问日志和客户端历史记录中)。使用该参数的客户端必须改用标头,例如在 mcp-remote 中使用 --header "Authorization: Bearer your-secret-key"。
Tailscale 登录(可选的第二重验证)
在 loopback 上通过 tailscale serve 提供服务时,设置 [email protected]。之后,请求还需要包含 Tailscale-User-Login 标头;serve 会为 tailnet 用户添加此标头。其他请求都会收到 403。如果端口还能通过其他方式访问,请勿依赖此功能,因为直接连接的客户端可以伪造该标头。
使用 API 密钥配置客户端
Claude Desktop / Cursor(通过 mcp-remote):
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://your-domain.com/sse",
"--header", "Authorization: Bearer your-secret-key"
]
}
}
}
Claude Code:
claude mcp add --transport sse obsidian https://your-domain.com/sse --header "Authorization: Bearer your-secret-key"
安全注意事项
- 生成强密钥:使用至少 32 个字符的随机字符串
- 必须使用 HTTPS:远程公开服务时,始终使用 HTTPS,防止密钥被拦截
- 文件访问:服务对挂载的保险库具有完整的读写权限,但所有路径都限制在保险库内(拒绝逃出保险库的路径,例如
../../etc/passwd;已在 1.4.1 中修复) - 绑定地址:服务会监听所有网络接口。在 Docker 中,除非需要远程访问,否则请将端口发布到回环地址(
127.0.0.1:3001:3000) - CORS:目前允许所有来源。如有需要,请在生产环境中进行限制
- 日志中的密钥:仅通过
Authorization请求头发送密钥;旧的?api_key=查询参数已移除,因为 URL 最终会出现在访问日志中 - 网络安全:对于敏感保险库,请考虑添加 VPN 或防火墙规则等额外防护层
使用 NGINX Proxy Manager 部署
如需通过 SSL 远程访问:
-
添加代理主机:
- 域名:
obsidian.yourdomain.com - 转发主机名:
obsidian-mcp(容器名称) - 转发端口:
3000 - 无需启用 WebSocket(SSE 使用纯 HTTP);下方的 Advanced 配置才是关键
- 域名:
-
SSL 选项卡:申请 Let's Encrypt 证书
-
Advanced 选项卡(用于 SSE):
proxy_buffering off; proxy_cache off; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off;
使用 Tailscale 进行私有访问
如果客户端位于 Tailscale tailnet 中,可以完全跳过公共反向代理。让容器继续绑定到回环地址,并使用 tailscale serve 通过仅限 tailnet 使用的 HTTPS 证书公开服务:
docker run -d --name obsidian-mcp -v /path/to/vault:/vault:rw -p 127.0.0.1:3001:3000 \
-e API_KEY=your-secret-key -e [email protected] \
ghcr.io/smith-and-web/obsidian-mcp-server:latest
sudo tailscale serve --bg --https=443 http://127.0.0.1:3001
然后,客户端使用 https://<machine>.<tailnet>.ts.net/sse,并在 Authorization: Bearer 请求头中提供密钥。切勿为此服务启用 Tailscale Funnel。有关 REQUIRE_TAILSCALE_LOGIN 的作用及其无法防护的内容,请参阅 Tailscale 登录。
故障排除
容器问题
# View logs
docker-compose logs -f obsidian-mcp
# Restart
docker-compose restart
# Rebuild
docker-compose up -d --build
保险库访问
# Check host mount
ls -la /path/to/your/vault
# Check container access
docker exec obsidian-mcp ls -la /vault
连接和身份验证错误
401 Unauthorized:缺少或错误的Authorization: Bearer <key>请求头(不再接受?api_key=查询参数)。403 Forbidden:已设置REQUIRE_TAILSCALE_LOGIN,但请求并非以该用户身份通过tailscale serve到达。Path escapes vault:请求的路径解析到VAULT_PATH之外;请使用相对于保险库的路径。- 搜索结果缺少笔记的
summary、标签或类型筛选结果:检查index-status中的invalidFrontmatter(或索引器日志中的WARN invalid frontmatter);笔记的 YAML 无效,因此索引时未包含其元数据。 - SSE 连接在反向代理后中断:关闭代理缓冲(见上方的 NGINX 配置块),检查证书,并确认防火墙允许该端口。
- Claude Desktop 已启动,但未显示任何工具:服务必须已在运行,且配置必须使用
mcp-remote(服务没有 stdio 模式)。
开发
快速开始
# Clone and install
git clone https://github.com/smith-and-web/obsidian-mcp-server.git
cd obsidian-mcp-server
make install
# Run with example vault
make dev
# Test connection
make test-connection
可用命令
运行 make help 查看所有可用命令:
Development:
make install Install dependencies
make dev Run server with hot-reload
make start Run server in production mode
make test-connection Test server connectivity
Docker:
make docker-build Build Docker image
make docker-up Start Docker container
make docker-down Stop Docker container
make docker-logs View container logs
make docker-restart Restart container
make docker-shell Open shell in container
使用 Bruno 测试 API
仓库中包含用于测试核心工具的 Bruno 集合(四个语义搜索工具需要 DATABASE_URL 和正在运行的索引)。
- 安装 Bruno(免费、开源的 API 客户端)
- 从
./bruno/obsidian-mcp打开集合 - 选择
local或remote环境 - 运行请求,测试各个工具
该集合按类别组织:
health/- 服务健康状态和工具列表notes/- 笔记 CRUD 操作directories/- 目录操作frontmatter/- Frontmatter 操作tags/- 标签管理search/- 搜索和查找替换links/- 反向链接和失效链接sections/- 基于章节的操作
示例保险库
开发和测试用的示例保险库位于 ./examples/test-vault/。其中包含带有标签、链接和章节的示例笔记,可用于测试所有功能。
贡献
请参阅 CONTRIBUTING.md 了解相关指南。
许可证
MIT License——详见 LICENSE。
相关项目
- Model Context Protocol - 协议规范
- Obsidian - 知识库应用
- Claude - Anthropic 的 AI 助手
维护工作进程
在与服务相同的镜像中运行 node dist/maintain.js(或 npm run maintain),并配置 DATABASE_URL、INDEX_COLLECTION、VAULT_PATH 和 MAINTAIN_REPORT_DIR。使用主机的调度程序每周运行一次。--dry 会输出候选项数量和维护指标,不会记录运行信息或写入报告。索引必须已存在。
七项确定性检查用于查找缺失链接、未链接的提及、可唯一修复的未解析链接、缺失的 index 或 aliases frontmatter、过时的摘要、近似重复项,以及过时的项目或领域枢纽。类型为 journal 或 inbox 的笔记不会作为目标。Journal/ 下的笔记不会收到任何方向的链接建议。未链接的提及仅统计笔记正文中的内容(不包括 frontmatter、第一个 H1、代码、wikilinks 或 markdown 链接),并且仅在其作为完整单词出现时才计入,不区分大小写,连字符视为单词的一部分;工作进程会从 VAULT_PATH 读取源笔记进行检查。近似重复项配对会跳过已互相链接的笔记,以及类型为 plan 的笔记配对。索引器会在内容变化时记录摘要/正文余弦历史,维护流程会为较早的笔记补建基线。搜索排名保持不变,维护流程绝不会读取学习表。
每个 ISO 周都会在配置的输出目录中生成 Brain maintenance YYYY-Www.md。报告是草稿笔记,包含 index: false、稳定的发现 ID、接受/拒绝复选框、趋势和接受计数。它们不会出现在搜索结果或链接图中。重新运行时会先解析勾选项,然后以原子方式仅替换该周的报告;移动后的报告仍会通过其 maintenance_report frontmatter 被发现。
对每条发现勾选一个复选框,或者明确要求助手调用 maintenance-decision,并提供 finding: "f17"、decision: "accept" 或 "reject",以及可选原因。若勾选项相互冲突,则不会记录决定。复选框不会覆盖先前的决定;显式工具调用可以更改决定。已拒绝的发现不会再次被提出。每类新增发现的数量设有上限,每份报告最多列出 50 条未处理的发现。
此阶段仅生成报告:接受一条发现只会记录决定,不会更改任何笔记。请自行应用已接受的发现。近似重复项合并和枢纽审查永久保持为仅生成报告。工作进程绝不会编辑现有笔记;它唯一写入知识库的内容是自己的报告。
recommend-notes 接受 1–10 条 positive 路径,最多 10 条 negative 路径,以及可选的整数 k(1–50)、type 和 directory。它会排除示例。best_score 会比较最近的正向项和负向项;average_vector 会使用正向项的均值,并在存在负向项时远离负向项的均值。两种工具都不要求记录学习数据;敏感的决定原因会依据当前启用的脱敏模式或内置规则存储为 null。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。