Hermes → Trilium MCP 安装配置

架构总览

🧠 用户 Telegram → #笔记 搜 Docker
🤖 Hermes Gateway → Agent Loop → mcp_tool.py
MCP stdio: stdin→JSON-RPC · stdout←响应
🔌 trilium-mcp v1.0.1 7 tools · Node.js · Authorization: <裸 token> (源码硬编码,无 Bearer 前缀)
HTTPS ETAPI: axios → REST · 环境变量来源:config.yaml env → dotenv(.env)
🗄️ Trilium Server trilium.atibm.com · 1133 笔记

安装路径

/opt/data/
├── home/
│   └── 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                        # Hermes 主环境变量

源码细节

入口 (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;

环境变量加载优先级

  1. config.yaml mcp_servers.trilium.env — Hermes 启动 MCP 子进程时注入环境变量
  2. MCP 自带 .env — dotenv 加载(不会覆盖已存在的环境变量)
  3. 两者都配了同一个 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/home/trilium-mcp,用
https://github.com/h30190/Trilium_MCP 这个仓库。装完在 config.yaml
的 mcp_servers 里配上,env 里引 .env 的 TRILIUM_ETAPI_TOKEN 就行。
最后 /restart。

注:安装后记得检查 MCP 自带的 .env 文件中的 token 是否与 /opt/data/.env 一致。


使用方式

🔍 搜索笔记

触发 search_notes(query)

你说Hermes 执行
搜索我的笔记里关于 Docker 的内容search_notes("Docker")
搜一下 gbrain 相关的笔记search_notes("gbrain")

📖 读取笔记

触发 read_note(noteId) — 返回基础元数据(noteId/title/type/mime/date)+ 正文

你说Hermes 执行
读一下笔记 ludkdAFlaQi5 的内容read_note("ludkdAFlaQi5")
打开刚才搜到的那篇笔记将上一步的 noteId 传给 read_note

📋 获取元数据

触发 get_note_metadata(noteId) — 完整属性/标签/关系/子笔记树

你说Hermes 执行
查看这篇笔记的标签和属性get_note_metadata(id) → attributes/labels/relations/childNoteIds

✏️ 创建笔记

触发 create_note(parentNoteId, title, content, type, mime)

你说Hermes 执行
创建一篇新笔记,标题叫"测试"create_note(parentNoteId="...", title="测试", type="text")
创建一篇 Mermaid 图表笔记create_note(..., type="mermaid")

🔄 更新笔记

触发 update_note(noteId, title?, content?, type?, mime?)

你说Hermes 执行
把标题改成"已修改"update_note(id, title="已修改")
在末尾追加一段话read_note → 拼接 → update_note

📂 移动笔记

触发 move_note(noteId, parentNoteId)⚠️ 会替换所有父节点,克隆笔记会丢失其他父位置

你说Hermes 执行
移到 Hermes 配置目录下get_note_metadata 检查是否克隆 → move_note

🏷️ 管理属性

触发 manage_attributes(action, noteId, type, name, value, isInheritable)

你说Hermes 执行
加标签 status:donemanage_attributes("create", noteId, "label", "status", "done")
改为 status:review先查 attributeId → manage_attributes("update", attributeId, value="review")
关联到另一篇笔记manage_attributes("create", noteId, "relation", "relatedNote", targetId)

故障排查

现象原因排查
curl 裸 token 401,带 Bearer 也 401Token 本身无效在 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 重启