架构总览
#笔记 搜 Dockermcp_trilium_search_notes(query=...)安装路径(标准布局)
/opt/data/
├── mcp/
│ └── Trilium-MCP/ # MCP 服务器(从 github.com/h30190/Trilium_MCP 克隆)
│ ├── dist/index.js # 编译后入口
│ ├── src/ # TypeScript 源码
│ │ ├── index.ts # 主入口,dotenv 加载 ../.env
│ │ └── trilium-client.ts # axios 客户端,第 11 行硬编码 Authorization
│ ├── .env # ETAPI Token(dotenv 自动读取)
│ ├── node_modules/
│ ├── package.json
│ └── tsconfig.json
├── config.yaml # mcp_servers.trilium 配置(含 env 注入)
└── .env # Hermes 主环境变量
⚠️ 注意:MCP 仓库名是 Trilium_MCP(下划线),但标准安装目录名是 Trilium-MCP(连字符),config.yaml 的 args 中引用的是目录名。安装后请确认路径一致。
源码细节
入口 (dist/index.js)
// dist/index.js 第 13 行 — dotenv 自动加载 ../.env
dotenv.config({ path: path.resolve(__dirname, "../.env") });
const TRILIUM_ETAPI_TOKEN = process.env.TRILIUM_ETAPI_TOKEN;
环境变量加载优先级
- config.yaml mcp_servers.trilium.env — Hermes 启动 MCP 子进程时注入环境变量
- MCP 自带 .env — dotenv 加载(不会覆盖已存在的环境变量)
- 两者都配了同一个 token。如果某天不一致,config.yaml 的注入优先级更高
Authorization 头格式(⚠️ 踩坑点)
// dist/trilium-client.js 第 8 行 — 直接传 token,无 Bearer 前缀
headers: {
'Authorization': token, // ← 裸 token,没有 "Bearer "
'Content-Type': 'application/json',
},
已验证:atibm.com 这个 Trilium 实例只接受裸 token 格式。
- ✅
Authorization: TaCxTq...5Yo=→ 200 OK - ❌
Authorization: Bearer TaCxTq...5Yo=→ 401 NOT_AUTHENTICATED
验证测试(对齐 MCP 实际行为)
# 必须先用 source 加载 .env(终端子进程不会自动读取 .env 文件)
source /opt/data/.env
# ✅ 正确:裸 token,与 MCP 源码一致
curl -s -X GET "https://trilium.atibm.com/etapi/notes/root" \
-H "Authorization: ${TRILIUM_ETAPI_TOKEN}"
# ❌ 错误:Trilium 实例会返回 401
# curl -H "Authorization: Bearer ${TRILIUM_ETAPI_TOKEN}" ...
一次对话安装
帮我装一下 Trilium MCP,安装到 /opt/data/mcp/Trilium-MCP,用
https://github.com/h30190/Trilium_MCP 这个仓库。装完在 /opt/data/config.yaml
的 mcp_servers 里配上,env 里引 .env 的 TRILIUM_ETAPI_TOKEN 就行。
最后 /restart。
注:安装后记得检查 MCP 自带的 .env 文件中的 token 是否与 /opt/data/.env 一致。
使用方式
⚠️ 重要:不要手搓 JSON-RPC
MCP 工具的底层通信协议是 JSON-RPC 2.0(stdin/stdout 传输),但 Hermes 的 MCP Client 层已经帮你处理了。你不要手写 echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"update_note",...}}'——这是 MCP 服务器子进程的内部线缆协议。正确做法是直接调用 Hermes 暴露的 native tools,命名格式为 mcp_trilium_<tool_name>。
🔍 搜索笔记
Hermes agent 直接调用 mcp_trilium_search_notes(query="Docker")
底层自动转为 JSON-RPC: {"method":"tools/call","params":{"name":"search_notes","arguments":{"query":"Docker"}}} → 你无需关心
| 你说 | Hermes agent 实际执行 |
| 搜索我的笔记里关于 Docker 的内容 | mcp_trilium_search_notes(query="Docker") |
| 搜一下 gbrain 相关的笔记 | mcp_trilium_search_notes(query="gbrain") |
📖 读取笔记
Hermes agent 直接调用 mcp_trilium_read_note(noteId="ludkdAFlaQi5") — 返回基础元数据(noteId/title/type/mime/date)+ 正文
| 你说 | Hermes agent 实际执行 |
| 读一下笔记 ludkdAFlaQi5 的内容 | mcp_trilium_read_note(noteId="ludkdAFlaQi5") |
| 打开刚才搜到的那篇笔记 | 将上一步的 noteId 传给 mcp_trilium_read_note |
📋 获取元数据
Hermes agent 直接调用 mcp_trilium_get_note_metadata(noteId="...") — 完整属性/标签/关系/子笔记树
| 你说 | Hermes agent 实际执行 |
| 查看这篇笔记的标签和属性 | mcp_trilium_get_note_metadata(noteId="...") → attributes/labels/relations/childNoteIds |
✏️ 创建笔记
Hermes agent 直接调用 mcp_trilium_create_note(parentNoteId="...", title="测试", type="text")
| 你说 | Hermes agent 实际执行 |
| 创建一篇新笔记,标题叫"测试" | mcp_trilium_create_note(parentNoteId="...", title="测试", type="text") |
| 创建一篇 Mermaid 图表笔记 | mcp_trilium_create_note(..., type="mermaid") |
🔄 更新笔记
Hermes agent 直接调用 mcp_trilium_update_note(noteId="...", content="...") — 更新标题/内容/类型/MIME
| 你说 | Hermes agent 实际执行 |
| 把标题改成"已修改" | mcp_trilium_update_note(noteId="...", title="已修改") |
| 在末尾追加一段话 | mcp_trilium_read_note → 拼接 → mcp_trilium_update_note |
📂 移动笔记
Hermes agent 直接调用 mcp_trilium_move_note(noteId="...", parentNoteId="...") — ⚠️ 会替换所有父节点,克隆笔记会丢失其他父位置
| 你说 | Hermes agent 实际执行 |
| 移到 Hermes 配置目录下 | mcp_trilium_get_note_metadata 检查是否克隆 → mcp_trilium_move_note |
🏷️ 管理属性
Hermes agent 直接调用 mcp_trilium_manage_attributes(action="create", noteId="...", type="label", name="status", value="done")
| 你说 | Hermes agent 实际执行 |
| 加标签 status:done | mcp_trilium_manage_attributes("create", noteId="...", type="label", name="status", value="done") |
| 改为 status:review | 先查 attributeId → mcp_trilium_manage_attributes("update", attributeId="...", value="review") |
| 关联到另一篇笔记 | mcp_trilium_manage_attributes("create", noteId="...", type="relation", name="relatedNote", value=targetId) |
故障排查
| 现象 | 原因 | 排查 |
| curl 裸 token 401,带 Bearer 也 401 | Token 本身无效 | 在 Trilium 设置页面重新生成,同步更新 MCP 的 .env 和 /opt/data/.env |
| curl 裸 token 通,MCP 不通 | MCP 子进程没读到正确的 token | 检查 MCP 自带 .env 的 token 是否正确;检查 config.yaml env 段是否正确引用 \${TRILIUM_ETAPI_TOKEN};重启 MCP(/restart) |
| curl 带 Bearer 通,MCP 不通 | Trilium 要求 Bearer 前缀但 MCP 发裸 token | 修改 trilium-client.ts 第 11 行或在 token 值前硬编码 "Bearer " |
| 工具不存在或 tools 列表为空 | MCP 未注册或启动失败 | ps aux | grep trilium-mcp · 检查 config.yaml mcp_servers 段 · /restart 重启 |
| agent 手搓 raw JSON-RPC | 笔记教了裸协议 + 工具在 config.yaml 中未正确注册 | 确保 mcp_servers.trilium 在 config.yaml 中正确配置 → agent 自动看到 mcp_trilium_* 工具,无需手写 JSON-RPC |