关于 brainllm
BrainLLM 是一款可自托管的 MCP 服务,将 LLM 的持久记忆存储在 TriliumNext Notes 中。它面向将 LLM 用作长期协作者的用户,由服务端执行笔记结构和会话流程,同时让记忆可在 Trilium 中查看和编辑。
主要功能
- 01将持久记忆存储在 TriliumNext Notes
- 02由服务端执行笔记结构与生命周期规则
- 03类型化知识图谱及 Mermaid 视图
- 04会话导览、日志记录和备份流程
- 05搜索、一致性检查与笔记维护
适用场景
- 01在不同 LLM 会话间延续项目决策
- 02长期维护个人或研究知识
- 03通过带日期的主题和日志跟踪持续工作
- 04在 TriliumNext 中查看和修正存储的记忆
MCP 相关
BrainLLM 是 MCP 服务,向兼容 MCP 的客户端提供存储于 TriliumNext 的持久记忆。它提供会话导览、笔记读写、关系探索和记忆维护等工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y brainllmTRILIUM_BASE_URLBase URL of the TriliumNext instance, e.g. http://localhost:8080TRILIUM_ETAPI_TOKEN密钥ETAPI token. Required unless TRILIUM_PASSWORD is set. Create one in Trilium under Options → ETAPI.TRILIUM_PASSWORD密钥Alternative to TRILIUM_ETAPI_TOKEN — BrainLLM mints a token on first start and caches it beside brainllm.json.BRAINLLM_MODEcore (default) exposes the 45 brain-aware tools; full additionally exposes the 33 raw ETAPI tools.BRAINLLM_TZIANA timezone used to stamp dated notes, e.g. Africa/Johannesburg. Defaults to the host timezone.BRAINLLM_OWNER_PASSWORD密钥Enables the OAuth 2.1 / CIMD endpoints required by claude.ai and other hosted Claude surfaces. This is the password entered on the consent screen when authorizing a client.BRAINLLM_OAUTH_SECRET密钥Optional 32+ character deployment secret for signing OAuth tokens; rotate it to invalidate old tokens.BRAINLLM_PUBLIC_URLOverride for the public origin when a proxy rewrites Host. Must exactly match the URL entered in the client.BRAINLLM_ALLOW_UNAUTHENTICATED_HTTP密钥Explicit trusted-network-only escape hatch for HTTP mode. Never enable on a public interface.BRAINLLM_TRUST_PROXYTrust forwarded host/protocol headers for OAuth discovery only when a trusted reverse proxy sets them.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
BrainLLM
让 Claude 拥有跨会话留存的记忆。
基于 TriliumNext Notes 为大模型提供可自托管的记忆,由服务端进行结构化整理,并由你编辑,通过 Model Context Protocol 提供服务。
网站 · 连接到 Claude · 工作原理 · 文档 · 隐私
BrainLLM 是什么?
大模型会遗忘。每次会话都从零开始:你是谁、你在做什么、昨天做出了什么决定、上周出了什么问题。BrainLLM 解决了这个问题。
BrainLLM 是一款 MCP 服务,为大模型提供真实、持久的记忆——记忆存储在 TriliumNext Notes 中。这是一款成熟的开源知识库,由你自托管并掌控。模型在每次会话开始时载入你的身份信息和当前进展;一有持久信息浮现,就立即写入;以带类型的图谱关联知识;并在每次会话结束时保存日志、日记条目和数据库备份。下一次会话会从上次结束的地方准确接续。
它是一个单独的 Bun/TypeScript 服务,只有两个依赖(MCP SDK 和 Zod),并且只通过 Trilium 的公开 ETAPI 进行通信。你的记忆保存在你自己的 Trilium 实例中——可查看、可编辑、可迁移,绝不会被锁在供应商的黑箱里。
适用人群
适合把大模型作为长期协作者,而非一次性问答工具的人——无论用于编码、研究、个人知识管理,还是日常运营跟踪——希望协作者真正记住你是谁、当前在做什么、已经做出什么决定,而不必每次会话都重新解释。如果你愿意自托管一个小型服务和一个 Trilium 实例,BrainLLM 就能彻底解决“模型又把所有事情都忘了”的问题。它适用于任何支持 MCP 的客户端(Claude Desktop、Claude Code,或任何其他使用 MCP 的客户端),而不仅限于某款产品。
从实验到开源
BrainLLM 于 2026 年 2 月作为一项个人实验启动:大模型能否可靠地操作一个真实的可自托管记忆系统,可靠到可以将其视为自己的记忆——自行梳理、写入、关联并结束会话,而无需人为整理?经过持续的日常生产环境使用,答案依然成立。设计已趋于稳定,故障模式已被发现并修复,项目也从实验发展为高效且稳定到足以与人分享的项目——因此现已开源。它至今仍在每天运行作者自己的会话;你正在阅读的就是同一份代码,并非演示版本。它也仍保留着一个人日常使用的特点——具体含义以及需要更改的内容,请参阅适配你的环境。
核心原则
模型提供内容。服务端掌控形式。
存放位置、命名、标签、去重、关系记录、生命周期老化、归档、日期标记、HTML 清理以及备份——还有结构本身——都由服务端以确定性策略处理,绝不交由大模型决定。模型不会选择父级笔记,不会设置标签,不会检查重复内容,也不会标记日期。这种分工让记忆变得可靠:每一项保障都由工具层强制执行,而非通过提示词提出要求。
设计亮点
- 结构有强制约束,而非仅作要求 — 每种内容都有规范结构,由
template()提供,并在写入时强制执行:新建线程必须包含目标;线程、日记和会话条目以标识行开头(使用哪个大模型、哪个环境、哪种会话类型);每个线程恰好包含一个 Resolution(由resolve()管理);系统会检测重复的章节标题;Last updated时间戳由服务端维护。 - 创建领域时即完整就绪 — 创建知识领域时,会同时创建其手册 和 规范的 Sources 笔记(标记说明、时间戳、分组来源列表、修订表),因此从首次写入起,每条主张都有对应的来源记录位置。
- 可视化关系图 —
graph()会将整个关系图(或任意笔记的邻域)渲染为 Mermaid 流程图,并将其作为原生 Trilium 笔记维护。 - 保留字节的文件 — 文本采用 UTF-8;二进制内容则在工具契约中使用严格标准的 base64,在传输中使用原始字节;读取二进制内容时会返回明确的 MIME/base64 封装,而不是替换字符。
- 一次调用掌握当日情况 —
day()会在一次调用中提供上一场会话、其变更日志以及之后所有有过改动的内容。 - 两种线程形态 — 线程可以是按日期排列的日志(每个活跃日对应一个子项),也可以是带标题的集合(每个项目对应一条持续维护的笔记,例如创意清单);创建时通过
shape=选择一次。 - 无日期的笔记,有日期的记录 — 知识、信息和线程手册不包含状态或历史(只有领域的 Current State 除外);
maintain(deep=true)会标记混入日期信息的文字。除日志外,每条笔记都带有图标,图标按类型设置,并由巡检补齐。 - 可靠的底层机制 — 每次后端调用都有超时限制,幂等读取会重试;常规核心写入具有幂等性或重复保护,而完整模式下的原始覆盖操作仍需显式执行;内容编辑可应对编辑器自身的 HTML 重写;重命名领域会级联应用于其中所有内容;维护巡检会修复发现的偏差。
- 默认支持多 Agent — 所有归类为写入的工具都会在进程级 FIFO 锁后串行执行,因此两个 Agent(例如交互式会话和自动化运行)通过同一个托管实例写入时,会按到达顺序排队,而不是互相竞争;读取则完全并行。每日日志中的删除追补机制可确保笔记在当天收尾后被删除时不会无迹可寻;追补窗口可配置(在生命周期策略中设置
deletionCatchupDays),默认采用 Trilium 的 7 天保留期。 - 用自然语言提问,按表格行操作 —
consistency(subject="…")可查找所有对某个事实作出断言的笔记,无论其措辞如何,无需猜测正则表达式;consistency(pattern, staleAfterDays=N)会呈现仅存在于一条未触及笔记中的数值,也就是因没有内容与之矛盾而悄然过时的数值;它会跳过会话和日记,因为它们是历史记录而非主张;diff(since="today")可在一次调用中审查会话修改过的所有笔记;revise(find="Phase 2", within="tr")可根据表格中的几个字编辑、插入或删除指定行;read(ids=[…])可通过一次往返批量了解多条笔记——预先提供每条笔记的大小,或在这些笔记中统一读取一个section=;split(noteId, sections=[…], into="…")则用于解决笔记过长的问题:将完整章节移到新笔记中,并在原处留下指向新笔记的链接。信息笔记可以带有#mandate标记,使范围限定的会话无需阅读所有笔记的正文,就能找到必须遵守的那一条。 - 独立的连接器身份 — 托管实例会公布 BrainLLM 及其自身来源地址,并提供同源 PNG/SVG 图标、明确的页面 favicon 链接,以及真正的多尺寸 ICO。规范标识在连接器、同意页面和公共资源之间通用。
- 预览变更并衡量覆盖情况 —
revise(noteId, edits=[…], dryRun=true)会显示逐步匹配数量,以及经过清理的变更前后内容,不会写入修订、标签或门控内容。修订失败时会停止内容覆盖。consistency()会报告读取失败和扫描覆盖情况;不区分大小写的比较会保留拼写变体,避免产生错误冲突。扫描不完整时,无法据此认定某内容不存在或达成一致。 - 一次调用即可关闭登记表记录 —
revise(find, within="tr", mode="remove", closure={thread, body})会删除登记表中的一行,并在当天该线程条目中记录其关闭情况;closure={note, table?, row?}会将一行移入明确指定的登记表;目标单元格必须符合表格结构。先写入目标位置可确保内容仍可恢复;moves=[{find, row?}]可批量关闭记录,并在执行前检查、逐行报告进度;append={table, row}可添加无需锚点查找的新行;within=还接受读者看到的行内容("Phase 2 | Claude | Open");写入时以及通过maintain(deep)检查时,都会标记与表头单元格不一致的表格行。 - 可验证的记录 — 会话、日记条目和带日期的线程条目均为只追加内容,现在还能验证其完整性:每次
close()都会将当天的记录封存为摘要,并与前一天的摘要链式关联;maintain(deep)会报告之后被改写的任何封存记录。追加仍然允许;对历史记录的编辑则不会悄然发生。
工作原理
启动时,BrainLLM 会在 Trilium 中构建一棵包含五个区域的树。工具创建的每条笔记都会按服务端策略进行类型标注、添加标签、记录日期并放置到相应位置:
BrainLLM (#brainLlmRoot)
├── 👤 Master Biography · Goals · Preferences (the user — maintained singletons)
├── 🤖 LLM Responsibilities · Protocols · Self-correction · Diary/ (the assistant's self-model + daily diary)
├── 🗂️ Memory Sessions/ · Threads/ (daily session logs + multi-session work)
├── 📚 Knowledge Master/ · Domains/<domain>/{ Sources, info } (learned facts beyond/contra training)
└── 💡 Insights Logs/ · Graph · Claims (the brain's record of itself)
| 笔记类别 | 类型 | 行为 |
|---|---|---|
| 单例 | biography、goals、preferences、responsibilities、protocols、selfcorrection(以及各领域的 Sources 笔记) | 仅维护一条笔记;在原处编辑;保存当前状态事实 |
| 带日期的记录 | diary、session、log | 每个日历日一条;每次写入都会作为带时间戳的附加内容块添加——编年记录才是重点 |
| 集合 | thread、user、information、domain | 带标题的笔记,在各自范围内按规范化标题去重 |
会话遵循强制协议:start() 负责引导(你是谁、正在进行的讨论串、上次以来有哪些变化)→ 模型开展工作,在事实出现时及时写入持久化内容 → session() → addendum() → maintain() → remarks() → diary() → close() 提交日志、重新生成每日变更日志并触发数据库备份。关闭前的关卡由代码强制执行:在每一步都按顺序实际运行之前,close() 都会拒绝执行——仅仅说“我完成了这些步骤”不算,必须调用工具。该关卡具有持久性,会写入会话笔记,而非保存在内存中,因此即使会话中途重启也能保留,并且在 stdio 和 HTTP 下行为一致。没有写入任何内容的会话会进行轻量关闭:无需归档或清理,因此 close() 只要求调用 session()、remarks() 和简短的 diary()。
引导过程刻意保持轻量。start() 会以章节标题的形式返回单例笔记——让模型足以了解知识库中存有什么——并提取最重要的章节。每次会话都提供完整的自我模型,意味着一个问题要花费与一整天工作相同的代价;对于确实需要完整信息的会话,仍可使用 start(depth="full")。
知识是一个类型化图谱:它使用包含 16 种关系的封闭词汇表(extends、contradicts、supports、partOf、supersedes、corrects,……),可通过 connect() 建立关系或在创建时关联,通过 explore() 遍历(链接/反向链接/邻域/最短路径),通过 graph() 呈现,并由 maintain(deep) 审查。审查会修复重复边,并标记笔记应用绝不会主动检查的问题:孤立笔记、过时内容、重复标题、笔记内部的结构偏移、正文超过读取上限、已加标签却从未填写内容的空笔记、标题中带日期(会破坏按标题去重)、长到需要整合的讨论串、从未填写验证表的 Sources 笔记,以及包含双重转义标记的正文。大部分检查依据服务端的笔记属性计算,而不是读取正文,因此报告完整,不会被截断。可以确认检查结果(maintain(ack=[…])),让已判定无误的标记在笔记内容实际变更前不再提示——每次运行都会出现、且每次都被正确忽略的警告,会让你逐渐学会略过整张列表。
快速开始
前置条件
- Bun v1.0+
- 正在运行的 TriliumNext 实例(桌面应用或服务端)
1. 安装并构建
git clone https://github.com/miisodev/BrainLLM
cd BrainLLM
bun install
bun run build
2. 获取 ETAPI 令牌
也可以跳过此步骤。 设置 TRILIUM_PASSWORD,而不是 TRILIUM_ETAPI_TOKEN,BrainLLM 就会在首次启动时自行创建令牌,并将其缓存在 brainllm.json 旁边,以便重启时重复使用,而不是每次都创建新令牌。对于无法在终端中运行设置命令的容器部署,这是最简单的方式。
如果要显式创建令牌,可以让 init 创建一个:
TRILIUM_BASE_URL=http://localhost:8080 TRILIUM_PASSWORD=your-trilium-password bun run init
它会在 brainllm.json 旁边缓存令牌,并仅授予所有者权限,不会打印密钥。如果明确需要副本,请运行 bun run init -- --show-token;此 CLI 命令具备幂等性,会采用现有的 #brainLlmRoot,而不是创建重复树。也可以在 Trilium 中手动创建:选项 → ETAPI → 创建令牌。
3. 配置 MCP 客户端
对于 Claude Desktop,请将配置添加到 claude_desktop_config.json(完整示例,包括远程配置,请参阅 config.example.json):
| 平台 | 路径 |
|---|---|
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"BrainLLM": {
"command": "bun",
"args": ["run", "/absolute/path/to/BrainLLM/dist/index.js"],
"env": {
"TRILIUM_BASE_URL": "http://localhost:8080",
"TRILIUM_ETAPI_TOKEN": "your-etapi-token-here"
}
}
}
}
对于 Claude Code,相同的服务配置块也适用于 .mcp.json。仓库中包含一个符合架构规范的 MCPB 清单(manifest.json),供支持自定义 Bun 命令的宿主使用;但 BrainLLM 仍需要 Bun,且此仓库目前尚未发布 .mcpb 一键安装包。
4. 初始化大脑
重启客户端,然后让 Claude 运行 bootstrap——它会构建包含五个区域的树(每个区域都刻有其用途),写入 brainllm.json,并立即激活。也可以通过 CLI 运行:
TRILIUM_BASE_URL=http://localhost:8080 TRILIUM_ETAPI_TOKEN=your-token bun run init
5. 教会模型如何操作它
安装操作技能——这份文档会教 Claude 将 BrainLLM 视为自己的大脑,而不是文件柜:
# Claude Code / Cowork skills directory (macOS/Linux)
cp -r skills/brainllm ~/.claude/skills/brainllm
# Windows PowerShell
New-Item -ItemType Directory -Force "$env:USERPROFILE\.agents\skills" | Out-Null
Copy-Item skills\brainllm "$env:USERPROFILE\.agents\skills\brainllm" -Recurse -Force
然后开始会话。模型会调用 start(),完成定位,此后便能原生操作大脑。
工具接口
所有工具返回的都是结构化 JSON。用户输入错误会返回提示性错误信息({error, detail, hint}),供模型读取并自行纠正;每个读取、写入和搜索工具都会附带免费的 relations 片段;所有写入操作都可安全重试。
为什么这些工具会采用这样的名称
这种命名是为了提高可读性,而不是一种风格选择。 大多数 MCP 客户端会在每次工具调用发生时显示其名称。BrainLLM 的工具名称采用通俗易懂的英语动词,描述正在对记忆执行什么操作——remember、recall、revise、resolve、forget、connect、maintain——因此,调用日志会直接描述模型正在你的大脑中做什么,你无需了解架构或打开 Trilium 才能弄清楚。
另一种做法是大多数服务采用的方式:根据底层 API 为工具命名,例如 create_note、patch_note、update_attribute。这只能告诉你有一行内容发生了变化,却无法说明模型记录了一项决定、纠正了此前相信的内容,或结束了一项工作——而这些才是你真正希望看到的记录。
这样一来,扫一眼就能看懂会话:
| 你看到的内容 | 含义 |
|---|---|
start → day | 了解当前状况:加载你的身份、尚未完成的事项,以及上次以来的变化 |
remember · diary | 记录某些内容——持久事实,或模型自己的每日记录 |
recall · domain · outline | 回答前先查找信息,而不是依据训练内容作答 |
revise · resolve · forget | 更改、完成或归档已有内容 |
connect · graph | 将模型发现的两个事物关联起来 |
maintain · addendum | 整理维护——内容过时、结构漂移、合并待处理的编辑 |
remarks → close | 收尾:先回顾,再提交会话日志 |
随着工具集不断扩展,以下三条约定能让这些名称始终清晰易懂:
- 动作使用动词,界面使用名词。
remember用于写入;knowledge用于读取。如果工具名称是名词,它就只会读取。 <surface>/<surface>_recall成对出现。 单独的名称表示完整读取某个界面;_recall后缀表示快速浏览。看到memory而不是memory_recall,就能知道模型选择了开销更低的读取方式。- 破坏性操作会明确标示。
forget、detach、delete_*绝不会用较温和的名称来表示,因此不可逆调用不会被伪装成常规操作。
完整模式工具刻意打破了这一约定——它们直接采用 Trilium 的原始基本操作命名(create_note、patch_note、delete_attribute),以便在日志中清楚地区分原始、无保护的操作和具备大脑感知能力的操作。
工具权限
每个工具都会声明自己是读取、写入还是销毁数据,因此客户端可以将它们分组,而你可以按组授权,无需逐个调用授权。每个工具还带有便于理解的标题(“编辑笔记”“搜索大脑”),用于权限提示和目录列表。当前完整模式的分类为 34 个只读 · 31 个写入 · 13 个破坏性操作(核心模式为 23 个只读 · 17 个写入 · 5 个破坏性操作);revise、label 和 connect 被归为破坏性操作,因为它们可能覆盖或移除已有内容。这让读取操作可以在无人值守的情况下运行,而任何会改动你大脑的操作仍会请求授权。
分类信息集中在一个便于审核的表中(src/annotations.ts),而不是散落在各处的注册代码中;默认分类刻意偏向不安全的一侧:表中未列出的工具会被视为写入工具,而非读取工具,并会在启动时明确说明。有三个工具值得留意,因为它们看似只读,实际上并非如此——start() 会创建当天的日记和会话占位记录,session() 会运行维护扫描,而 graph() 会写入生成的图谱笔记。
Core — 通用动词(35)
| 分组 | 工具 |
|---|---|
| 会话生命周期 | start · day · session · remarks · close · backup · health |
| 写入 | remember · diary · revise · split · resolve · withdraw · recover |
| 阅读与搜索 | read · recall · domain · brain · assembly · outline · inspect · diff · template · consistency |
| 图谱 | connect · explore · graph |
| 附件与标签 | attach · detach · label |
| 维护与系统 | addendum · maintain · claim · forget · bootstrap |
Core — 表层读取(10,双模式)
master/master_recall · llm/llm_recall · memory/memory_recall · knowledge/knowledge_recall · insights/insights_recall — 每种表层读取都可完整读取,或以精简存根形式快速浏览。
完整模式(BRAINLLM_MODE=full,+33)
原始 ETAPI,每个 Trilium 原语对应一个工具——笔记、属性、分支、修订、附件、日历、系统。与 Brain 无关且不受保护:用于处理 Core 无法表达的边缘情况,例如精确的原始查询、代码/画布/mermaid 笔记、分支操作和修订恢复。
完整的操作参考见 skills/brainllm/SKILL.md;各主题的深入介绍见 skills/brainllm/references/。此外,架构参考会说明 BrainLLM 的构建方式及其原因——涵盖每个模块、每个工具的行为、sweep 内部机制和幂等性矩阵,并会随每次发布更新,而非发布在此仓库中;如何获取副本,请参阅支持项目。
部署
传输模式
| 模式 | 适用场景 | 选择方式 |
|---|---|---|
| stdio | 本地 — Claude Desktop / Claude Code 将 BrainLLM 作为子进程启动 | 未设置 PORT(默认) |
| HTTP connector | 远程 — 客户端通过网络连接 BrainLLM | 已设置 PORT(Railway 会注入) |
HTTP connector 在 /mcp 提供可流式传输的 HTTP MCP 端点(每个 mcp-session-id 对应一个会话,DELETE 请求会终止会话;启用 CORS,并向浏览器客户端公开 mcp-session-id),在 /sse 提供旧版 SSE 端点(带有 POST /messages),供尚不支持可流式 HTTP 的客户端使用——包括较旧版本的 Cursor、Continue 和 Python-SDK 客户端;还提供 GET /health。对于普通浏览器的 GET 请求,服务根路径会返回一个纯文本页面;发送到 / 的 MCP 格式请求也会被接受,作为兼容性别名,因为某些托管版 Claude connector 会将令牌后的 MCP 路径规范化为源站根路径。服务本身使用普通 HTTP——TLS 应在其前方终止(Railway 会自动为你完成;在裸 VPS 或自有 Docker 主机上,请将其置于 Caddy、nginx 或 Cloudflare Tunnel 等反向代理之后)。现在,若未配置 MCP_AUTH_TOKEN 或 BRAINLLM_OWNER_PASSWORD,HTTP 模式将拒绝启动;BRAINLLM_ALLOW_UNAUTHENTICATED_HTTP=true 是仅适用于隔离可信网络的明确例外。CORS 默认为 *,因此可访问的部署必须配置真正的身份验证关卡。空闲会话会在 1 小时后清除;服务端和 SDK 传输层均将 MCP 请求正文大小限制为 50 MiB。两种传输方式共用同一个身份验证关卡。
Docker / Railway
随附的 Dockerfile 用于构建并运行 HTTP connector(两阶段构建,使用固定摘要的 oven/bun,并通过 entrypoint.sh 降低权限)。在 Railway 上:PORT 会自动注入——将 MCP_AUTH_TOKEN、TRILIUM_BASE_URL、TRILIUM_ETAPI_TOKEN 和 BRAINLLM_TZ 设置为服务变量,部署后,将客户端指向 https://<app>.up.railway.app/mcp 并附上 bearer token。在其他地方部署(VPS、自有 Docker 主机)也同样可行——容器只需设置相同的环境变量,并在前方配置一个终止 TLS 的代理,具体参见上文的传输说明。
品牌形象
在 HTTP 模式下,BrainLLM 会提供自己的图标——/icon.png、/icon.svg、不同尺寸的变体,以及 /favicon.ico——并在 MCP 握手中一并公布这些图标、标题、一行描述和网站,因此客户端会在连接器列表和工具调用旁显示 BrainLLM 标志。握手还包含简短的服务端说明,介绍五个领域和会话形式。所有与品牌形象相关的内容(名称、标语、描述、标志的几何形状)都在 src/brand.ts 中统一定义,单元测试会确保 package.json、manifest.json 和 server.json 使用相同的描述。
这两部分都至关重要。规范要求客户端验证图标 URI 与服务端使用相同的源,因此托管在其他位置的图标会被静默丢弃。而且,如果主机不提供 favicon,客户端会回退到可注册域名的图标——位于 brain.yourdomain.com 的 BrainLLM 会显示 yourdomain.com 的网站标志;这比什么都不显示更糟,因为它看起来像是有意为之。
如需使用自己的图稿,请替换 public/BrainLLM.svg 并运行 bun run icons——此命令会将矢量图栅格化为服务端提供的各种尺寸。请提交生成的文件;Docker 镜像会复制 public/,运行时无需图像处理工具链。
连接远程客户端(OAuth)
Claude Code 和 mcp-remote 会将 MCP_AUTH_TOKEN 作为标头发送,无需进行其他设置。托管版 Claude 界面——claude.ai、Claude 移动版、Cowork——则不支持。它们的自定义连接器界面只能选择 OAuth,否则无法连接;没有填写 bearer token 的输入框。因此,BrainLLM 提供了自己的 OAuth 2.1 授权服务器。它支持两种注册机制,因此所有支持 OAuth 的客户端都能使用:
- 客户端 ID 元数据文档(CIMD)——MCP
2025-11-25规范采用的免注册机制;Claude 会选择此机制。 - 动态客户端注册(RFC 7591)——适用于尚未支持 CIMD 的客户端。opencode(MCP TS SDK ≤1.29)就是一个典型案例:没有
registration_endpoint,它就会拒绝进行身份验证。
设置一个变量即可启用:
BRAINLLM_OWNER_PASSWORD=<a strong password you choose>
此密码就是你在授权同意页面上输入的密码。未设置此变量时,OAuth 端点会完全禁用,其他内容不受影响。
然后在 Claude 中:设置 → 连接器 → 添加自定义连接器,输入 https://<your-host>/mcp,并将 OAuth 客户端字段留空——CIMD 不需要客户端 ID 或密钥。Claude 会通过 401 发现所有信息,你只需在授权同意页面上批准一次,即可保持连接。
启用的功能如下:
| 端点 | 用途 |
|---|---|
/.well-known/oauth-protected-resource(以及 /mcp 变体) | RFC 9728——标识授权服务器 |
/.well-known/oauth-authorization-server(以及 /mcp 变体) | RFC 8414——公布对 CIMD、DCR 和 PKCE S256 的支持 |
/authorize | 验证客户端(CIMD 文档或已注册 ID),并显示授权同意页面 |
/token | 通过 PKCE 验证的代码交换;轮换的刷新令牌仅以 SHA-256 哈希形式存储 |
/register | RFC 7591 动态客户端注册——opencode 和其他仅支持 DCR 的客户端通过此端点连接 |
授权同意页面会显示发起请求的客户端名称及其返回地址,并在该地址指向你自己的计算机时发出警告(例如 Claude Code 使用的环回重定向)。MCP 流量按 bearer token 限流,并设置较宽松的 IP 限额,因此共享同一出口 IP 段的客户端(Claude 的所有托管流量都是如此)不会耗尽彼此的配额。
两种凭据可以同时使用——Claude Code 使用静态令牌,claude.ai 使用 OAuth 令牌,二者连接到同一个 brain。访问令牌是绑定到服务器资源 URI 的签名 JWT,因此为其他 MCP 服务签发的令牌会被拒绝;签名密钥和刷新令牌会与 brainllm.json 一同持久化。在容器部署中,这意味着必须将 BRAINLLM_CONFIG 放在卷上——否则每次重新部署都会使所有令牌失效。
若要在重新部署后保留配置,请在 BrainLLM 服务(而非 Trilium 服务)上挂载卷,并将 BRAINLLM_CONFIG 设置为卷内的文件路径:
railway volume -p <project-id> -s <brainllm-service-id> -e production add --mount-path /vol
railway variables set BRAINLLM_CONFIG=/vol/brainllm.json
如果不使用卷,请不要设置 BRAINLLM_CONFIG——每次冷启动时,自动发现功能都会通过 Trilium 的 #brainLlmRoot 标记重新找到 brain(约 1 秒)。
配置参考
| 变量 | 必需 | 用途 |
|---|---|---|
TRILIUM_BASE_URL | ✅ | TriliumNext 实例的 URL(本地地址,或 HTTPS 反向代理/隧道 URL) |
TRILIUM_ETAPI_TOKEN | ◐ | ETAPI bearer token。除非设置了 TRILIUM_PASSWORD,否则必需 |
TRILIUM_PASSWORD | ◐ | Token 的替代方案:BrainLLM 首次启动时会生成一个 Token,并将其缓存在 brainllm.json 旁边,重启后继续使用。bun run init 也会使用此变量 |
BRAINLLM_MODE | — | core(默认)或 full(添加原始 ETAPI 接口) |
BRAINLLM_TZ | — | IANA 时区(例如 Africa/Johannesburg),使托管部署中的日期按你所在时区标注;未设置时使用主机时钟 |
BRAINLLM_DELETION_CATCHUP_DAYS | — | Trilium 保留已删除笔记的天数(Options → Other → "Erase notes after")。只会报告此时间窗口内的删除记录,且 ETAPI 无法读取该选项。默认为 7,即 Trilium 自身的默认值;会覆盖 policy.deletionCatchupDays |
PORT | — | 设置此变量后会切换到 HTTP 连接器模式 |
MCP_AUTH_TOKEN | — | 在 /mcp 上接受的静态 bearer token,即 Claude Code 和 mcp-remote 发送的 Token |
BRAINLLM_OWNER_PASSWORD | — | 启用 OAuth 2.1/CIMD 端点,claude.ai 必需。你在授权页面输入的密码 |
BRAINLLM_OAUTH_SECRET | — | 可选的 32 个或更多字符的部署签名密钥;轮换此密钥可使旧 OAuth Token 失效 |
BRAINLLM_ALLOW_UNAUTHENTICATED_HTTP | false | 未配置 HTTP 凭据时,仅限受信任网络使用的显式例外;切勿在公共接口上使用 |
BRAINLLM_TRUST_PROXY | false | 仅当受信任的反向代理设置了 x-forwarded-host/x-forwarded-proto 时,才信任它们用于 OAuth 发现;建议使用 BRAINLLM_PUBLIC_URL |
BRAINLLM_PUBLIC_URL | — | 当代理重写 Host 时,用于覆盖推导出的公共源。必须与客户端中输入的 URL 完全一致 |
BRAINLLM_CONFIG | — | 持久卷部署中 brainllm.json 的绝对文件路径 |
生命周期时长配置在 brainllm.json 中,可根据需要调整:
"policy": { "dormantAfterDays": 21, "archiveDormantAfterDays": 45, "staleAfterDays": 7 }
线程会按此时间线从 active → dormant → archived-in-place 逐步变旧;老化不会删除任何内容,recover()/withdraw() 可恢复任何内容。
适配你的环境
此仓库中有少数值反映了作者自己的机器环境。这些都不是机密,但如果你克隆或派生此仓库,请检查并替换为自己的值:
| 项目 | 位置 | 替换为你自己的值 |
|---|---|---|
| 时区 | .env 中的 BRAINLLM_TZ | 你的 IANA 时区;或留空以使用主机时钟 |
| 已删除笔记的保留期限 | .env 中的 BRAINLLM_DELETION_CATCHUP_DAYS | 仅当你的 Trilium 在非 7 天后擦除已删除笔记时才需要调整 |
| 配置路径 | BRAINLLM_CONFIG 环境变量 | 仅持久卷部署需要(见上文) |
| 打包路径 | MCP 客户端配置 | 你机器上 dist/index.js 的真实绝对路径 |
| 作者 · 仓库 · 赞助 | package.json、.github/FUNDING.yml、上方的徽章 | 派生此仓库时替换为你自己的信息;赞助链接支持原作者 |
架构
index.ts ─→ tools.ts ─┬→ trilium.ts ETAPI client: bounded/retrying I/O, graph traversal
├→ router.ts placement policy: kind → parent, label plans, domains
├→ templates.ts canonical structures per kind + the template-tool rules
├→ normalize.ts titles, slugs, markdown→HTML, sanitization, surgery
├→ lifecycle.ts protection, sweep, aging, the start digest
├→ journal.ts daily Insights log generation (regenerate-in-place)
├→ time.ts timezone-correct now/today (BRAINLLM_TZ)
├→ rate-limit.ts bounded public HTTP budgets
├→ brand.ts name, description, mark, icons, server instructions
├→ cap.ts the 140k-character result cap
├→ diffing.ts block and table-cell diffs
├→ network-security.ts CIMD public-network and redirect checks
├→ bootstrap.ts five-area tree builder
└→ tools-*.ts per-area surface reads · full-mode raw ETAPI
主要特性:工具结果不会超过 140,000 个字符(超出部分会被截断,并附上说明,指出更精确的读取方式);每次写入都会经过清理,以兼容 Trilium/CKEditor 5(并将修改报告在 sanitized[] 中);常规内容修改前会创建修订快照;每个请求都会携带用户的本地时间,使 Trilium 按正确日期标注时间;核心写入具备幂等性或重复防护,而 full 模式下的原始覆盖操作则需要明确执行。
开发
bun run dev # hot-reload dev server
bun run build # bundle to dist/index.js
bun run test:unit # all unit tests (Bun discovery; works on Windows and CI)
bun run test:http # local auth/header smoke (no Trilium required)
BRAINLLM_LIVE_TOKEN=... bun run test:live # read-only smoke against a deployed server, over real MCP
bun run test # integration tests (requires a live Trilium instance)
bun run validate:manifest # MCPB schema/icon validation
bun run init # CLI bootstrap
仓库布局:运行时源码位于 src/,操作技能位于 skills/brainllm/,开发脚本位于 scripts/,符合架构验证的 MCPB 清单位于 manifest.json,容器构建文件位于 Dockerfile。
贡献
欢迎贡献,包括问题报告、修复、文档和想法。
我们重视什么
BrainLLM 最初是一个人日常使用的工具,因此代码和文档在一些地方仍沿用了这个人的假设(参见适应你的环境)。最有价值的贡献,是让它适用于更多情境:
| 价值 | 在 PR 中的体现 |
|---|---|
| 通用性 | 将某个工作流中固有的假设替换为适用于任何人的大脑内容和惯例的做法 |
| 跨用户 | src/ 中的任何内容都不应假设某个特定用户的项目、日程或词汇——这些内容应由模型写入内容,绝不能放在工具层 |
| 跨设备 | 在 Windows/macOS/Linux 上、通过本地 stdio 和远程 HTTP、在笔记本电脑和 Railway 容器中,表现都一致 |
| Token 效率 | 更短的响应、更少的往返、更低成本的读取——例如,只需确认是或否时,使用 recall() 返回的排序片段,而不是 inspect(content=true)。这是持续适用的设计取向,而非一次性优化 |
| 延迟 | 有界且可重试的 I/O;并行读取优于顺序读取;不让会话启动或工具调用变得不必要地缓慢 |
如何贡献
- 先提交 issue,说明所有非简单事项——报告 Bug 时提供复现步骤,提出方案时说明具体用例。
- Fork 并创建分支,让改动聚焦,并遵循现有代码风格。
bun run build必须顺利通过,单元测试也必须全部通过(bun run test:unit——CI 会在每次推送和 PR 时运行相同的检查);新增 normalize/lifecycle 逻辑时请添加测试。- 使用约定式提交(
fix:、feat:、docs:、refactor:)。 - 更新改动涉及的文档——
README.md、skills/brainllm/下的技能包,以及相关的config.example.json/.env.example。技能是产品的一部分:改动工具却不更新对应技能,就只完成了一半。
不确定从何开始?带有 good first issue 标签的 issue、文档缺漏和故障排查参考,都是不错的入门途径。
安全
请勿在公开 issue 中报告疑似漏洞。请按照 SECURITY.md 中的私密报告流程操作;报告中切勿包含 ETAPI 令牌、OAuth 凭据或私人脑内容。
隐私与支持
BrainLLM 不收集任何信息:没有分析数据、遥测数据或账户。你的笔记保存在自己的 Trilium 中,登录记录保存在你自己的服务端。完整的隐私政策说明了哪些数据存储在何处。如需帮助,请提交 GitHub issue 或发送邮件至 [email protected]。
支持项目
BrainLLM 由一个人开发和维护。如果它对你有用,赞助将直接用于支持其持续开发:
赞助者可以索取一份 Blueprint——基于完整源代码分析生成的内部架构参考文档,涵盖每个模块、每个工具的确切行为和签名,以及 sweep 和幂等性实现细节;该文档不会发布在此仓库中。请通过 GitHub Sponsors 联系我们索取。
为仓库点星、报告 Bug 和传播消息也能提供帮助。
许可证
致谢
- TriliumNext Notes — 为此服务的后端提供支持的开源、可自托管知识库。没有 TriliumNext 团队的工作,就不会有 BrainLLM。
- Model Context Protocol — 让一份记忆服务于所有支持 MCP 的客户端的开放标准。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。