内容
# 一些库的一些MCP
MCP 服务器,给予 AI 模型对 Obsidian 库的读写权限,支持语义搜索、链接图分析、画布操作和增量索引。
构建于 FastMCP + LanceDB 之上。默认使用 fastembed(nomic-embed-text-v1.5-Q,768 维)本地嵌入笔记 - 无需服务器。可选支持 Ollama 或 OpenAI 嵌入。监控库文件系统,变更时重新索引。
警告:这是一个充满激情的编码混乱,请用户自行承担风险
## 功能
- 混合搜索 - 向量相似性(70%)+ 全文关键词(30%),重叠增强
- 语义搜索 - 纯粹基于嵌入的检索
- 精确搜索 - 字面字符串匹配,可选大小写敏感性和正则表达式
- 笔记 CRUD - 创建、读取、追加、前置、移动、删除(默认为软删除到 `.trash`)
- 前置事项解析 - YAML 提取、标签收集(前置事项 + 内联 #hashtags)、属性过滤
- 维基链接图 - 反向链接、出链、孤立检测、损坏链接检测、BFS 邻居遍历(深度 1-5)
- 画布 CRUD - 创建、读取、添加/更新/删除节点和边、网格自动布局、悬空边清理
- 每日笔记 - Moment.js 风格的日期格式化、模板支持、读取 Obsidian 的每日笔记配置
- 增量索引 - 文件系统监视器,2 秒防抖,基于 mtime 的变更检测
- 原子写入 - 临时文件 + POSIX 重命名、按路径 asyncio 锁
- 工具覆盖 - 通过 YAML 配置重命名或禁用任何工具(按代理自定义)
## 要求
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) (用于 `uvx`)
## 快速入门
将以下内容添加到您的 MCP 客户端配置(Cursor、Windsurf、Claude Code 等):
```json
{
"mcpServers": {
"obsidian-vault": {
"command": "uvx",
"args": ["some-vault-some-mcp", "serve", "--transport", "stdio"],
"env": {
"VAULT_PATH": "/path/to/your/obsidian/vault"
}
}
}
}
```
就这样。`uvx`会在首次运行时从 PyPI 安装包并缓存它。
对于替代嵌入提供程序,请添加 `--from` 并使用额外的参数:
```json
{
"command": "uvx",
"args": ["--from", "some-vault-some-mcp[ollama]", "some-vault-some-mcp", "serve", "--transport", "stdio"],
"env": {
"VAULT_PATH": "/path/to/your/obsidian/vault"
}
}
```
根据需要将 `[ollama]` 替换为 `[openai]` 或 `[gpu]`。
### SSE 模式
如果单独运行服务器(Docker、远程等),请使用 SSE URL:
```json
{
"mcpServers": {
"obsidian-vault": {
"url": "http://localhost:3789/sse"
}
}
}
```
### 直接运行
```bash
VAULT_PATH=/path/to/vault uvx some-vault-some-mcp serve
```
首次运行时会对库进行完整索引 - 根据大小需要几分钟。后续启动仅运行增量索引。
### CLI 标志
```
some-vault-some-mcp serve [--transport sse|stdio] [--host 0.0.0.0] [--port 3789]
```
CLI 标志覆盖环境变量。
## 环境变量
| 变量 | 默认值 | 备注 |
|---|---|---|
| `VAULT_PATH` | (必填) | Obsidian 库的绝对路径 |
| `LANCE_DB_PATH` | `./data/vault.lance` | 向量索引存储位置 |
| `MCP_TRANSPORT` | `sse` | `sse` 或 `stdio` |
| `MCP_HOST` | `0.0.0.0` | SSE 绑定地址 |
| `MCP_PORT` | `3789` | SSE 端口 |
| `EMBEDDING_PROVIDER` | `fastembed` | `fastembed`、`ollama`、`openai` 或 `mock` |
| `FASTEMBED_MODEL` | `nomic-ai/nomic-embed-text-v1.5-Q` | 任何 fastembed 支持的模型。在 Apple Silicon 上,自动检测并使用非量化变体(`v1.5` 而不是 `v1.5-Q`),因为量化 ONNX 操作针对 x86 优化 |
| `FASTEMBED_DIMENSIONS` | (自动检测) | 覆盖维度自动检测 |
| `OLLAMA_URL` | `http://localhost:11434` | Ollama API 端点(需要 `--extra ollama`) |
| `OPENAI_API_KEY` | | 如果提供者是 `openai` 则必填(需要 `--extra openai`) |
| `VAULT_API_KEY` | | 在 SSE 传输上启用 Bearer 令牌身份验证 |
| `VAULT_SOFT_DELETE_IS_PERMANENT` | `false` | `true` 使 `delete_note` 成为硬删除 |
| `some_vault_some_mcp_OVERRIDES` | | YAML 覆盖文件路径 |
## Docker
```bash
docker build -t some-vault-some-mcp .
docker run -p 3789:3789 \
-v /path/to/vault:/opt/vault:ro \
-v /data:/opt/data \
some-vault-some-mcp
```
以非 root 用户(vault:vault,UID 1000)运行。索引数据保存在 `/opt/data` 中。库以只读方式挂载。容器是自包含的 - fastembed 在进程中运行,不需要 Ollama sidecar。
要使用 Ollama:`docker run ... -e EMBEDDING_PROVIDER=ollama -e OLLAMA_URL=http://ollama:11434 some-vault-some-mcp`
## 工具(27)
### 搜索
#### `search`
按文本、含义或精确字符串查找笔记。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `query` | 字符串 | (必填) | 搜索查询文本 |
| `mode` | 字符串 | `"hybrid"` | `hybrid`、`semantic` 或 `exact` |
| `top_k` | 整数 | `10` | 返回最大结果数 |
| `tags` | 字符串[] | `null` | 按标签预过滤 |
| `folder` | 字符串 | `null` | 按文件夹路径前缀预过滤 |
| `case_sensitive` | 布尔值 | `false` | 精确模式仅 - 匹配大小写 |
### 读取
#### `get_note`
读取具有解析的前置事项和标签的单个笔记。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库中笔记的相对路径。扩展名不可知 - `todo` 和 `todo.md` 都解析为 `todo.md`。裸文件名解析为 Obsidian 样式,以匹配库中的任意笔记。 |
#### `list_notes`
枚举库笔记,可选元数据过滤器。索引支持的字段(标签、项目、状态、区域)查询 LanceDB 时可用。前置事项属性过滤直接扫描文件。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `folder` | 字符串 | `null` | 按文件夹路径前缀过滤 |
| `tags` | 字符串[] | `null` | 按标签过滤(索引支持) |
| `projects` | 字符串[] | `null` | 按项目过滤(索引支持) |
| `status` | 字符串 | `null` | 按状态字段过滤(索引支持) |
| `area` | 字符串 | `null` | 按区域字段过滤(索引支持) |
| `frontmatter_property` | 字符串 | `null` | 任意前置事项键用于过滤 |
| `frontmatter_value` | 字符串 | `null` | `frontmatter_property` 的匹配值(不区分大小写) |
| `include_content` | 布尔值 | `false` | 在结果中包含笔记内容 |
| `limit` | 整数 | `50` | 返回最大结果数 |
### 写入
#### `create_note`
创建新笔记。如果笔记已存在,则失败。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 新笔记的库相对路径 |
| `content` | 字符串 | (必填) | 笔记正文文本 |
| `frontmatter` | 字符串 | `null` | 前置事项字段的 JSON 字符串,例如 `'{"title":"My Note","tags":["idea"]}'` |
#### `append_to_note`
将文本追加到现有笔记的末尾。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库相对路径 |
| `content` | 字符串 | (必填) | 要追加的文本 |
#### `prepend_to_note`
在前置事项后、笔记正文前插入文本。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库相对路径 |
| `content` | 字符串 | (必填) | 要插入的文本 |
#### `update_frontmatter`
将键值对合并到 YAML 前置事项中。未列出的键保留。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库相对路径 |
| `properties` | 字符串 | (必填) | 键值对的 JSON 字符串,例如 `'{"status":"done","tags":["review"]}'` |
#### `move_note`
移动或重命名笔记。默认情况下,库范围内的维基链接会被重写。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `old_path` | 字符串 | (必填) | 当前库相对路径 |
| `new_path` | 字符串 | (必填) | 目标库相对路径 |
| `update_links` | 布尔值 | `true` | 重写所有引用笔记中的维基链接 |
#### `delete_note`
删除笔记。默认移动到 `.trash`;使用 `permanent` 进行硬删除。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库相对路径 |
| `permanent` | 布尔值 | `false` | `true` 进行硬删除,`false` 软删除到 `.trash` |
### 每日笔记
#### `get_daily_note`
读取今日或指定日期的每日笔记。使用 Obsidian 的 daily-notes 配置文件夹和日期格式。如果请求日期没有笔记,则返回文本消息(不是错误)。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `date` | 字符串 | `null` | 日期字符串(例如 `"2024-03-15"`)。省略表示今日 |
#### `create_daily_note`
创建今日或给定日期的每日笔记。如果存在,则失败。支持 `{{date}}` 模板中的占位符。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `date` | 字符串 | `null` | 日期字符串。省略表示今日 |
| `content` | 字符串 | `null` | 笔记正文文本 |
| `template_path` | 字符串 | `null` | 库相对路径到模板笔记 |
### 标签
#### `get_tags`
列出库中所有唯一标签及其按笔记的使用计数。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `sort_by` | 字符串 | `"count"` | `count`(降序)或 `name`(字母顺序) |
### 链接图
#### `get_backlinks`
查找链接到给定笔记的笔记。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 目标笔记的库相对路径 |
#### `get_outlinks`
列出笔记的所有出站维基链接。显示有效、损坏和嵌入链接。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库相对路径 |
#### `find_orphans`
查找不连接的笔记 - 没有输入链接,没有输出链接,或者两者都没有。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `include_outlinks_check` | 布尔值 | `true` | 还报告没有出站链接的笔记 |
| `max_results` | 整数 | `200` | 每类别上限 |
#### `find_broken_links`
查找指向不存在的笔记的维基链接。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `folder` | 字符串 | `null` | 限制扫描到文件夹 |
| `max_results` | 整数 | `200` | 返回的最大损坏链接数 |
#### `get_graph_neighbors`
通过 BFS 从笔记遍历链接图。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 起始笔记路径 |
| `depth` | 整数 | `1` | BFS 深度,1-5 |
| `direction` | 字符串 | `"both"` | `inbound`、`outbound` 或 `both` |
### 画布
#### `list_canvases`
列出库中的所有 `.canvas` 文件。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `folder` | 字符串 | `null` | 按文件夹路径前缀过滤 |
#### `read_canvas`
读取画布结构(节点 + 边)。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | `.canvas` 文件的库相对路径 |
#### `create_canvas`
创建新的 `.canvas` 文件,可选初始节点/边。失败如果它已经存在。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | (必填) | 库中新的 `.canvas` 文件的库相对路径 |
| `nodes` | 字符串 | `null` | JSON 数组的节点对象,例如 `'[{"type":"text","text":"Hello"}]'` |
| `edges` | 字符串 | `null` | JSON 数组的边对象 |
#### `add_canvas_node`
将节点(文本/文件/链接/组)添加到画布。省略时,位置通过网格布局自动计算。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `canvas_path` | 字符串 | (必填) | 画布路径 |
| `node_type` | 字符串 | (必填) | `text`、`file`、`link` 或 `group` |
| `text` | 字符串 | `null` | 文本内容(用于文本节点) |
| `file` | 字符串 | `null` | 库相对文件路径(用于文件节点)。对于 Markdown 笔记,`.md` 扩展名是可选的,裸文件名按 Obsidian 样式解析;存储完整路径。附件(图像/PDF)必须附带扩展名。 |
| `url` | 字符串 | `null` | URL(用于链接节点) |
| `label` | 字符串 | `null` | 显示标签 |
| `x` | 整数 | `null` | X 位置。如果省略,则自动放置 |
| `y` | 整数 | `null` | Y 位置。如果省略,则自动放置 |
| `width` | 整数 | `250` | 节点宽度(像素) |
| `height` | 整数 | `60` | 节点高度(像素) |
| `color` | 字符串 | `null` | Obsidian 预设 `"1"`-`"6"`(红色、橙色、黄色、绿色、青色、紫色)或十六进制 `"#FF0000"` |
#### `update_canvas_node`
通过 ID 更新现有画布节点的任何属性。仅更改提供的字段。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `canvas_path` | 字符串 | (必填) | 画布路径 |
| `node_id` | 字符串 | (必填) | 节点 ID |
| `x` | 整数 | `null` | 新 X 位置 |
| `y` | 整数 | `null` | 新 Y 位置 |
| `width` | 整数 | `null` | 新宽度 |
| `height` | 整数 | `null` | 新高度 |
| `color` | 字符串 | `null` | 预设 `"1"`-`"6"` 或十六进制 `"#FF0000"` |
| `text` | 字符串 | `null` | 新文本内容 |
| `file` | 字符串 | `null` | 新文件引用 |
| `url` | 字符串 | `null` | 新 URL |
| `label` | 字符串 | `null` | 新标签 |
#### `remove_canvas_nodes`
通过 ID 删除节点。自动删除悬空边。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `canvas_path` | 字符串 | (必填) | 画布路径 |
| `node_ids` | 字符串 | (必填) | 节点 ID 字符串的 JSON 数组,例如 `'["id1","id2"]'` |
#### `add_canvas_edge`
添加具有完整属性支持的画布节点之间的边。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `canvas_path` | 字符串 | (必填) | 画布路径 |
| `from_node` | 字符串 | (必填) | 源节点 ID |
| `to_node` | 字符串 | (必填) | 目标节点 ID |
| `from_side` | 字符串 | `null` | 源侧锚点:`top`、`bottom`、`left`、`right` |
| `to_side` | 字符串 | `null` | 目标侧锚点 |
| `from_end` | 字符串 | `null` | 源侧端点样式(例如 `arrow`、`none`) |
| `to_end` | 字符串 | `null` | 目标侧端点样式 |
| `color` | 字符串 | `null` | 预设 `"1"`-`"6"` 或十六进制 `"#FF0000"` |
| `label` | 字符串 | `null` | 边标签文本 |
#### `update_canvas_edge`
更新现有画布边的属性。仅更改提供的字段。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `canvas_path` | 字符串 | (必填) | 画布路径 |
| `edge_id` | 字符串 | (必填) | 边 ID |
| `from_side` | 字符串 | `null` | 新源锚点侧 |
| `to_side` | 字符串 | `null` | 新目标锚点侧 |
| `from_end` | 字符串 | `null` | 新源端点样式 |
| `to_end` | 字符串 | `null` | 新目标端点样式 |
| `color` | 字符串 | `null` | 预设 `"1"`-`"6"` 或十六进制 `"#FF0000"` |
| `label` | 字符串 | `null` | 新标签 |
#### `remove_canvas_edges`
通过 ID 从画布中删除边。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `canvas_path` | 字符串 | (必填) | 画布路径 |
| `edge_ids` | 字符串 | (必填) | 边 ID 字符串的 JSON 数组 |
### 索引管理
#### `vault_index_status`
检查搜索索引的健康状况和统计信息。无参数。
#### `vault_reindex`
触发单个笔记或整个库的增量重新索引。
| 参数 | 类型 | 默认值 | 备注 |
|---|---|---|---|
| `path` | 字符串 | `null` | 库相对路径以重新索引单个笔记。省略以进行完整库索引 |
## MCP 资源
- `obsidian://note/{path}` - 按路径读取笔记
- `obsidian://tags` - 所有标签
- `obsidian://daily` - 今日每日笔记
## 工具名称覆盖
当您需要更改工具名称和描述时,可以使用 YAML 文件。创建 YAML 文件,并将 `some_vault_some_mcp_OVERRIDES` 设置为其路径:
```yaml
tools:
search:
name: "vault_search"
description: "此代理的自定义描述"
get_note:
name: "read_note"
disabled:
- get_tags
- find_orphans
```
覆盖在注册时应用。对于每个代理的工具名称空间或禁用工具很有用,如果您的代理不支持工具禁用。
## 搜索工作原理
**混合**(默认)- 同时运行语义和 FTS(LanceDB 全文搜索,BM25 样式)并在 2 倍于请求 top_k 的情况下,标准化分数,将合并后的分数按 `semantic * 0.7 + FTS * 0.3` 组合。出现在两者中的结果将获得 1.2 倍提升。按合并后的集合返回 top_k。
**语义** - 嵌入查询,在 LanceDB 上运行向量相似性搜索。
**精确** - 扫描原始库文件以查找字面字符串匹配。非索引支持。可选大小写敏感性。
所有搜索模式都接受标签和文件夹预过滤器。标签使用 SQL LIKE 语句针对索引中的逗号分隔的标签字符串。文件夹使用路径前缀匹配。
## 分段
首先按标题层次结构分割 Markdown,然后按段落分割超大章节。通过分割跟踪标题导航 - `# A / ## B / ### C` 下的段落获取标题字段 `"A > B > C"`。元数据标题附加到嵌入文本中: `[Title: X | Section: A > B | Tags: foo, bar]`.
## 维基链接解析
按照 Obsidian 的行为匹配:
1. 精确相对路径匹配(不区分大小写)
2. 路径后缀匹配(如果链接包含 `/`)
3. 带有邻近性破坏的 basename 匹配(最深的共享路径前缀)
4. 别名匹配(前置事项别名)
第一个匹配步骤获胜。
## 安全边界
- 所有路径都针对库根目录进行验证 - 拒绝 `../`、空字节、符号链接逃逸
- `.obsidian`、`.git`、`.trash` 排除在索引和工具访问之外
- SSE 传输上的可选 Bearer 令牌身份验证
- Docker 容器以非 root 用户运行
- 有界的前置事项解析防止 YAML 炸弹
## 开发
### 设置
```bash
git clone https://github.com/russellsch/some_obsidian_some_mcp.git
cd some_obsidian_some_mcp
uv sync # 默认(fastembed CPU)
uv sync --extra gpu # GPU 加速嵌入
uv sync --extra ollama # Ollama 提供程序
uv sync --extra openai # OpenAI 提供程序
```
### 从源码运行
```bash
VAULT_PATH=/path/to/vault uv run some-vault-some-mcp serve
```
要在您的 MCP 客户端配置中使用本地检出而不是 PyPI 包:
```json
{
"mcpServers": {
"obsidian-vault": {
"command": "uv",
"args": ["run", "--project", "/path/to/some_obsidian_some_mcp", "some-vault-some-mcp", "serve", "--transport", "stdio"],
"env": {
"VAULT_PATH": "/path/to/your/obsidian/vault"
}
}
}
}
```
### 测试
```bash
uv run pytest tests/unit # 单元测试(快速,无外部依赖)
uv run pytest tests/integration # 集成测试(真实 LanceDB,可能会下载 ~130MB 模型)
uv run pytest # 所有测试
```
测试夹具位于 `tests/fixtures/vault/`。
### 发布
参见 [RELEASING.md](RELEASING.md)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器