内容
# obsidian-multivault-mcp
一个MCP服务器,为**Claude提供对托管在VPS上的Obsidian vaults的读写访问** — 为**多个vault和多个用户**而构建,单次部署即可服务多人。所有用户共享一个服务器;每个用户通过其登录时输入的通行短语(HTTP模式)或Claude Desktop启动时附带的vault路径(stdio模式)被路由到其**专属私有vault**。服务器直接在VPS上运行,对vault具有文件系统访问权限。
## 功能特点
- **多vault,多用户** — 一个服务器为多人服务,每个人映射到其自己的vault。在HTTP模式下,每个人都添加*相同的*连接器URL,而他们各自不同的登录通行短语决定了他们访问的vault;在stdio模式下,每个Claude Desktop都传递其自己的vault路径。
- **两种传输方式** — 通过`ssh`(Claude Desktop)的stdio和受OAuth保护的可流式HTTP端点(Claude移动/网页/桌面远程连接器)。
- **具有严格路径安全性的读写工具** — 仅限于vault相对路径;拒绝`..`、绝对路径、符号链接逃逸和空字节;写入仅限于扩展名允许列表。可选`--read-only`和每用户`readOnly`。
- **自托管OAuth 2.1** — 通行短语登录、PKCE、JWT访问令牌和动态客户端注册,因此不需要第三方身份验证服务。
- **单文件部署** — esbuild将所有内容捆绑到一个`dist/index.js`;部署是单文件复制。
服务器内部没有SSH库;SSH仅是Claude Desktop用于启动服务器和交换JSON-RPC的传输方式。这是“在VPS上集中vault”的设置的一部分:
```
Mac VPS (always-on)
┌ Obsidian app ──(obsidian-remote-ssh plugin)── SSH ─▶ Go daemon ─┐
│ ├─▶ a user's vault
└ Claude Desktop ─(ssh stdio → node index.js)── SSH ─▶ THIS server┘ ▲
Hermes agent (direct fs)
```
## 工具
读取:`list_notes`、`read_note`、`search_notes`。
写入(禁用`--read-only`):`write_note`、`create_note`、`append_to_note`、`edit_note`、`delete_note`、`move_note`。
所有路径都是**相对于vault的**,并被限制在vault根目录下 — 拒绝`..`、绝对路径、符号链接逃逸和空字节;写入/删除/移动操作被限制在允许的扩展名(默认`.md`、`.markdown`、`.canvas`、`.txt`;通过`--ext`覆盖)。
## 在本地构建和测试
```bash
npm install
npm run build # 捆绑到 dist/index.js(单文件)
npm run smoke # 启动服务器针对临时vault并测试每个工具
npm run typecheck # 可选:tsc --noEmit
```
您也可以使用MCP Inspector交互式地驱动它:
```bash
npm run inspect -- /absolute/path/to/a/vault
```
## 部署到VPS
VPS上需要 **Node 20+**。
```bash
VPS=user@your-vps DEST=obsidian-multivault-mcp ./scripts/deploy.sh
# 将 dist/index.js 复制到 ~/obsidian-multivault-mcp/index.js 到 VPS
```
从Mac远程测试复制(应打印stderr上的启动行,然后等待):
```bash
ssh user@your-vps node obsidian-multivault-mcp/index.js /absolute/path/to/vault
# Ctrl-C to stop
```
## 配置Claude Desktop(每台Mac)
编辑 `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"obsidian-multivault": {
"command": "ssh",
"args": [
"youruser@your-vps",
"node",
"/home/youruser/obsidian-multivault-mcp/index.js",
"/home/youruser/alice-vault"
]
}
}
}
```
重启Claude Desktop。第二台Mac(同一VPS上的不同vault)使用相同的块,其vault路径作为最后一个参数。
注意:
- `ssh`必须**非交互式**身份验证(密钥身份验证,无通行短语提示)— 使用`ssh user@your-vps true`进行测试。
- 添加`"--read-only"`作为最后一个参数,仅公开读取工具。
- 配置作为CLI参数传递,而不是`env`,因为`ssh`默认不转发环境变量。
## CLI
```
node index.js <vault-root> [--read-only] [--ext .md,.canvas] [--http [--port 8787] [--host 127.0.0.1]]
```
默认(无`--http`):stdio传输,由Claude Desktop通过ssh启动(上文)。
使用`--http`:为Claude的**远程连接器**(包括**移动应用**)提供长寿命的HTTP服务器(见下文)。
---
# 从Claude移动应用(远程连接器)访问
移动应用无法使用ssh。像claude.ai / Desktop / Cowork一样,它通过**Streamable HTTP**连接到**公共HTTPS URL**上的远程MCP服务器。因此,同一个服务器还运行了一个HTTP传输(`--http`),由自托管的**OAuth 2.1**层提供支持 — 一旦公开,ssh密钥门控就会消失,因此用登录门控取代它。
```
Claude mobile ──HTTPS──▶ Caddy (:443, auto Let's Encrypt) ─▶ node index.js --http (127.0.0.1:8787)
203-0-113-10.sslip.io ├ OAuth 2.1 (passphrase login, PKCE, JWT)
└ /mcp (guarded by bearer JWT) ─▶ vault
```
Claude Desktop的stdio/ssh路径保持不变并独立运行。
## HTTP模式配置(环境变量)
`--http`从环境中读取这些变量:
| 变量 | 必需 | 含义 |
|---|---|---|
| `MCP_PUBLIC_URL` | 是 | 公共源,例如 `https://203-0-113-10.sslip.io`。用作OAuth发行者/受众。 |
| `MCP_JWT_SECRET` | 是 | 用于签名访问/刷新令牌的32+随机字节。旋转它会立即吊销所有令牌。 |
| `MCP_USERS_FILE` | 其中之一 | 每用户 `{id, passphrase, vault}` 映射(见下文)— 用于多人/vault。 |
| `MCP_AUTH_PASSPHRASE` | 其中之一 | 单用户快捷方式:给定CLI参数的vault的登录通行短语。使用此**或** `MCP_USERS_FILE`。 |
| `MCP_CLIENTS_FILE` | 否 | 持久化DCR客户端注册的位置(默认:next to `index.js`)。 |
| `MCP_NO_AUTH` | 否 | 设置为 `1` 以禁用身份验证 — **仅限本地测试**,绝不能公开。 |
### 多人,多vault
将 `MCP_USERS_FILE` 指向一个JSON文件,其中包含每个人的一个条目 — 每个**不同的**通行短语映射到该人的自己的vault。所有人都在自己的Claude账户中添加**相同的**连接器URL;他们登录时输入的通行短语将他们路由到正确的vault。参见 [`users.example.json`](users.example.json)。
```json
{
"users": [
{ "id": "alice", "passphrase": "a-long-passphrase", "vault": "/home/youruser/alice-vault" },
{ "id": "bob", "passphrase": "a-DIFFERENT-long-passphrase", "vault": "/home/youruser/bob-vault", "readOnly": false }
]
}
```
在此模式下,不需要CLI vault参数(`node dist/index.js --http`)。可选的每用户密钥:`readOnly`和`ext`。
## 在本地测试HTTP模式
```bash
npm run build
npm run smoke:http # 启动两个服务器:HTTP工具 + 完整的OAuth握手
# 或手动驱动它(禁用身份验证)使用MCP Inspector:
MCP_NO_AUTH=1 node dist/index.js /path/to/vault --http --port 8787
# → 将Inspector(Streamable HTTP)指向 http://localhost:8787/mcp
```
## 部署到公共HTTPS端点(VPS)
**参见 [`DEPLOY.md`](DEPLOY.md) 了解完整的,可复制的运行手册** — 安装Caddy,`/etc/obsidian-multivault-mcp.env` 秘密,systemd单元,Caddyfile,防火墙,验证和故障排除。它编写得如此详细,以至于具有VPS上shell访问权限的代理可以从头到尾遵循它。
简而言之:`sslip.io` 为裸IP提供HTTPS功能主机名,无需购买域名 (`203-0-113-10.sslip.io` → `203.0.113.10`);Caddy在443上终止TLS并反向代理到 `127.0.0.1:8787` 上的Node服务;服务在systemd下运行,从 `/etc/obsidian-multivault-mcp.env` 读取其秘密。需要 **Node 20+** 和 **开放端口80 + 443**。
## 在Claude移动应用中添加
设置 → **连接器** → **添加自定义连接器** → 粘贴 `https://203-0-113-10.sslip.io/mcp` → 高级设置(客户端ID/密钥)留空(服务器支持动态客户端注册)→ **添加**。当提示时,使用**您自己的通行短语**完成浏览器登录。然后,所有工具都会出现。每个用户都在自己的Claude账户中添加相同的URL,并使用自己的通行短语登录以访问自己的vault。
## 安全注意事项(公开的表面是读写)
- 通行短语和JWT密钥仅存在于mode-600的`EnvironmentFile`中。使用强通行短语。
- 访问令牌是短寿命(1小时)的JWT;旋转 `MCP_JWT_SECRET` 立即吊销所有令牌。
- 如果您不需要移动写入,请在systemd单元中运行 `--read-only` — 它会减小爆炸半径。
- 将Node服务绑定到 `127.0.0.1`;只有Caddy面对互联网,超过HTTPS。