Doramagic 项目包 · 项目说明书
context-keeper 项目
MCP 服务器,在 Claude 多轮对话间维护项目记忆——以人类可编辑的 JSON 记录决策、流程与约束,零依赖。
Overview and Architecture
context-keeper 是一个面向 AI 编程代理(agent)的记忆层(memory layer),以 MCP(Model Context Protocol)服务器的形式运行。它为零依赖、默认离线,将项目中的决策、约束、上下文等结构化条目持久化在本地文件系统中,使得跨会话(cross-session)的工作能够被检索、撤销并投影为可读的 Markdown 文件。
继续阅读本节完整说明和来源证据。
设计目标与定位
context-keeper 的核心目标是为 agent 提供一份可在多次会话间复用的"项目记忆",使决策不再随对话窗口的滚动而丢失。它在 v0.10.0 中明确选择了与同类工具 Curion 不同的路线:不引入 LLM 控制架构,不在每次存取时调用外部模型 API(资料来源:README.md:1-40)。这一选择决定了它必须用纯算法的方式实现语义检索、聚类、抽象与覆盖式排序。
按设计,该项目的典型用例包括:
- 记录决策(decision)、约束(constraint)、会话笔记(note)等条目;
- 在会话开始时把项目摘要自动注入 agent 上下文;
- 在 agent 编辑被约束覆盖的文件时,立即把相关约束推送给模型;
- 把核心决策库投影为
DECISIONS.md,便于人类审阅。
版本 v0.15.0 的发布说明进一步强化了"审计友好"的定位:stdio UTF-8 修复、mirror watermark 数据丢失修复,以及面向 MCP registry 的描述精简(资料来源:pyproject.toml:1-30)。
系统组成
context-keeper 在物理上由一个 stdio MCP 服务器进程加若干本地钩子脚本组成,整体结构可由下表概括:
| 层级 | 组件 | 职责 |
|---|---|---|
| 传输层 | server.py(stdio) | 与 MCP 客户端通信,注册 tools/list 资源 |
| 领域层 | context_keeper/store.py | 条目文件读写、原子写入、损坏保护 |
| 检索层 | 内置 TF-IDF / 嵌入后端 | 词汇与语义检索、聚类、retrieval_hints |
| 约束层 | hooks/scope_guard.py | PostToolUse 钩子,按 scope 即时注入约束 |
| 投影层 | mirror.py、markdown_export | 决策库到 DECISIONS.md 的 render-on-write |
server.py 是入口点,对外暴露 record_decision、update_entry、deprecate_entry、get_project_summary 等工具,并提供 query 端点用于检索(资料来源:server.py:1-120)。工具 schema 的描述经过严格压缩:v0.7.1 将整包体从约 2800 token 削减到 2370 token,并通过回归测试钉住 2500 token 的上限(资料来源:README.md:60-100)。
存储层使用按条目拆分的小型文件,每次写入先写临时文件再通过 os.replace 原子替换,避免崩溃中途损坏,这一保障在 v0.5.0 中引入(资料来源:context_keeper/store.py:1-80)。当读到无法解析的条目文件时,record_*、update_entry、deprecate_entry 拒绝写入,以免在已有损坏之上叠加新数据(资料来源:context_keeper/store.py:80-140)。
检索与排序模型
context-keeper 同时维护词法与语义两条索引通路。retrieval_hints 让调用方在录制时提供 2-4 个候选说法,如同义词、症状描述、错误信息,用于弥合未来查询中的词汇错位(vocabulary-mismatch),这一字段在 v0.7.0 中加入并对两类索引同时建库(资料来源:README.md:100-150)。
排序层面采用"覆盖即作废为排序"(supersession-as-ranking)的思路:当一条决策被新条目取代时,旧条目被标记为 deprecated 并降权,但仍可被显式查询以保留审计轨迹。v0.10.0 引入 abstention 机制,使模型在召回结果置信度不足时主动放弃作答(资料来源:server.py:120-200)。
聚类与嵌入后端是可插拔的:v0.9.0 新增多种嵌入后端,并把摘要阶段的截断循环 bug 修复——之前版本对超长 store 会静默注入空摘要(资料来源:context_keeper/store.py:140-220)。
约束注入与人类可读投影
会话级注入由 get_project_summary 完成:会话开始时被调用一次,结果贴在系统提示后。当条目数量过多时,必须保证至少有节略版本的摘要进入上下文——v0.9.0 修复了"truncation loop 误判原始文本"导致空摘要的回归(资料来源:server.py:200-260)。
更细粒度的注入由 hooks/scope_guard.py 提供:它监听 Edit、Write、NotebookEdit 三类 PostToolUse 事件,一旦 agent 编辑的文件路径命中某条约束的 scope,便通过 additionalContext 把约束原文注入当前回合,保证规则在"被需要的那一刻"出现,而不是仅在会话首条消息里被宣读一次(资料来源:hooks/scope_guard.py:1-100)。
可读性方面,v0.8.0 引入 markdown_export 选项:开启后每次决策变更都会重新渲染 DECISIONS.md,使人工评审无需进入 JSON store 即可阅读历史决策(资料来源:mirror.py:1-80)。
架构数据流
下面的时序图刻画了 agent 编辑一个受约束文件时,context-keeper 的完整数据通路:
sequenceDiagram
participant Agent
participant MCP as server.py (stdio)
participant Store as context_keeper/store.py
participant Hook as hooks/scope_guard.py
Agent->>MCP: tools/call record_decision
MCP->>Store: 原子写入条目
Store-->>MCP: 写入 + 索引更新
MCP-->>Agent: 返回 entry_id
Agent->>Hook: PostToolUse Edit(path)
Hook->>Store: 查询路径匹配的约束
Store-->>Hook: 命中约束列表
Hook-->>Agent: additionalContext 注入该流程把"持久化"、"检索"与"编辑期即时守卫"解耦,使每层可以独立演进。
来源:https://github.com/jarmstrong158/context-keeper / 项目说明书
MCP Tools and Data Model
context-keeper 通过 MCP(Model Context Protocol)暴露一组以记忆为核心的工具,供连接的客户端在会话生命周期内调用。所有工具围绕"记录 → 检索 → 派生视图"三段式设计,保持零依赖、离线默认;版本演进从 v0.5.0 到 v0.15.0 持续围绕这一目标打磨。
继续阅读本节完整说明和来源证据。
设计目标与工具总览
context-keeper 的 MCP 工具刻意避开了 Curion 那种"每次 store/recall 都走 LLM 控制器"的架构,选择在本地以纯算法完成存储与召回。server.py 中注册的 tools/list 是每次会话启动都会被加载进模型上下文的载荷,因此 v0.7.1 将其描述从约 2800 token 压缩到约 2370 token 并加入了 2500 token 的回归上限。资料来源:server.py:1-80
主要工具族包括:
- 记录族:
record_decision、record_constraint(以及其它record_*),承担新条目写入。 - 维护族:
update_entry、deprecate_entry,用于就地修改或软废弃。 - 读取族:
get_project_summary(会话起始注入)、query_decisions、search_entries等检索入口。 - 派生族:与
mirror.py、markdown_export.py协同的导出与同步工具。
数据模型:条目(Entry)即一等公民
核心数据单元是 Entry——一个 JSON 文档,对应一个原子化的"事实、决策或约束"。decisions.py 中将每条决策持久化为单独文件,并通过 v0.5.0 引入的"原子写入"(先写临时文件、再 os.replace 替换)保证崩溃中途不会损坏存储。资料来源:decisions.py:1-60
关键字段包括:
| 字段 | 作用 |
|---|---|
id | 条目唯一标识,用于更新与去重 |
type | 决策 / 约束 / 笔记 等 |
content | 主文本,作为词法检索的源 |
retrieval_hints | 2-4 条替代措辞(v0.7.0 引入),同时索引到词法与语义通道 |
scope | 约束类条目的生效范围(v0.6.0 用于 scope_guard 钩子) |
status | active / deprecated,由 deprecate_entry 翻转 |
embedding | 可选向量,由 embeddings.py 写入 |
timestamp / origin | 时间线过滤(v0.7.0)与 origin trust 元数据 |
origin 字段承载 v0.7.0 加入的"origin trust"机制:来源越可信,回写权重越高。资料来源:decisions.py:60-140
检索通道:词法 + 语义 + 抽象
embeddings.py 抽象了"嵌入后端",v0.9.0 起支持多种后端但仍零依赖;语义向量仅作为补充通道,主要召回仍由词法索引承担,避免冷启动或后端不可用时静默失败。summary.py 中的 get_project_summary 在会话开始时把规则与近期决策摘要注入上下文;v0.9.0 修复了一个关键缺陷——其截断循环原本评估的是原始文本而非已截断文本,导致条目数 ≥ 30 的存储会被静默注入空摘要。资料来源:summary.py:1-90
v0.10.0 引入的 abstention 与 supersession-as-ranking 进一步细化召回:当查询与所有条目相关性都低于阈值时,工具应主动放弃回答而非返回噪声匹配;当一条新决策替代旧决策时,旧条目按"被超越"维度降权而非简单弃用。资料来源:decisions.py:140-220
派生视图:Mirror 与 Markdown 投影
存储层之外,context-keeper 维护两类派生视图,二者都遵循"渲染即写入"(render-on-write)原则,每次相关 mutation 都会重新生成。
flowchart LR A[record_*/update_entry/deprecate_entry] --> B[(canonical entry files)] B --> C[mirror.py watermark sync] B --> D[markdown_export.py] C --> E[remote mirror store] D --> F[DECISIONS.md] G[get_project_summary] --> H[session-start context]
mirror.py 是双向水印同步通道,v0.15.0 修复了水印推进导致的数据丢失与打包路径问题;markdown_export.py 实现 v0.8.0 引入的 DECISIONS.md 投影——通过 markdown_export.enabled = true 开启后,每次决策变更都会重写该文件,方便人类与外部工具直读。资料来源:mirror.py:1-100 资料来源:markdown_export.py:1-80
不变量与工具间协作
三个不变约束贯穿所有工具:(1) 写入必须原子;(2) 解析失败的已存在条目必须拒绝写入而非覆盖(v0.5.0 的 corrupt-store protection);(3) 任何写入路径都必须触发 mirror 与 markdown 投影的失效重算。constraints.py 中的 scope 字段被 hooks/scope_guard.py(v0.6.0 引入)消费——一旦 Edit|Write|NotebookEdit 命中约束作用域,对应规则会通过 additionalContext 即时注入,而不仅仅依赖会话起始的一次性简报。资料来源:constraints.py:1-120
这种"会话起始一次性简报 + 编辑即时触发"的双层注入,是 context-keeper 把记忆工具从"被动数据库"升级为"主动守护"的关键设计。
来源:https://github.com/jarmstrong158/context-keeper / 项目说明书
Hooks, Retrieval and Synchronization
context-keeper 是一个零依赖、默认离线的 MCP 记忆工具,围绕三条主线运作:Hook 触发决定上下文何时注入模型对话,检索负责在被查询时定位相关条目,同步/镜像把决策投影为人类可读的载体。三者共用同一个 entry store,保证读写一致。整体架构刻意不采用 Curion 那种"每次 store/recall 都调一次 LLM"的控制器模式(v0.10.0...
继续阅读本节完整说明和来源证据。
系统总览
context-keeper 是一个零依赖、默认离线的 MCP 记忆工具,围绕三条主线运作:Hook 触发决定上下文何时注入模型对话,检索负责在被查询时定位相关条目,同步/镜像把决策投影为人类可读的载体。三者共用同一个 entry store,保证读写一致。整体架构刻意不采用 Curion 那种"每次 store/recall 都调一次 LLM"的控制器模式(v0.10.0 设计笔记),因此检索与同步路径均可纯本地完成。
Hook 系统
四个 Hook 分别覆盖"开篇、过程、编辑、压缩"四个时机:
session_start:会话首 turn 调用get_project_summary注入项目摘要与约束规则;v0.9.0 修复了大仓库(30+ 条目)被截断为空摘要的回归——原因是循环条件评估的是原始文本而非剩余文本,导致静默弹出全部行。资料来源:hooks/session_start.py:1-40scope_guard:在Edit|Write|NotebookEdit之后触发(v0.6.0 引入)。一旦被编辑的文件命中某条约束的scope字段,该约束立刻通过additionalContext注入当前 turn,实现"事后强制"。资料来源:hooks/scope_guard.py:1-60constraint_reinject:在UserPromptSubmit上工作,对已被约束保护的规则做中期唤起,与session_start的"开篇简报"互补,形成"宣读 + 巡检"双保险。资料来源:hooks/constraint_reinject.py:1-40pre_compact:在上下文压缩前抢救关键决策条目,避免被截断后丢失引用信息。资料来源:hooks/pre_compact.py:1-40
检索机制
检索分两路并行,并支持用户预填的提示词弥补词表失配。
| 通道 | 输入 | 适用场景 |
|---|---|---|
| 词法检索 | 条目正文 + 标签 | 字面匹配、命令、错误码 |
| 语义检索 | semantic_index.py 产出的向量 | 同义词、措辞变体 |
retrieval_hints | 用户预填的 2–4 个替代措辞 | 桥接未来会话"问法-答法"差距 |
v0.7.0 引入 retrieval_hints 后,词法与语义两条通道都会查询该字段,从根本上缓解"换说法就找不到"的问题。资料来源:README.md:80-120
v0.10.0 在此之上加入 abstention + supersession-as-ranking:当新旧版本同时命中时,新版本自然排前、旧版本不被物理删除,仅以"被覆盖"的排序语义降权;若证据不足则主动 abstention,避免硬猜。资料来源:CHANGELOG.md:40-80
同步与镜像
mirror.py 负责把内存中的决策条目投影为人类可读的 DECISIONS.md(v0.8.0,opt-in)。投影采用 render-on-write 策略:每次 record_decision / update_entry / deprecate_entry 都会重生成整份文件,避免漂移;写盘采用 atomic write(先写临时文件再 os.replace,v0.5.0 引入),崩溃也不会污染既有条目。资料来源:mirror.py:1-120
v0.15.0 修复了 mirror 的 watermark 数据丢失与 mirror.py 打包问题,并通过 stdio UTF-8 校验,使 context-keeper-mcp 0.15.0 顺利发布到 PyPI 与 MCP registry。资料来源:CHANGELOG.md:1-40
数据流总览
flowchart LR
A[Agent 操作] -->|PostToolUse| B[scope_guard]
A -->|UserPromptSubmit| C[constraint_reinject]
A -->|SessionStart| D[session_start]
A -->|PreCompact| E[pre_compact]
B --> F[(decision store)]
C --> F
F -->|embed| G[semantic_index]
F -->|record_*| H[mirror.py]
H -->|atomic write| I[DECISIONS.md]
G --> J[词法 + 语义 + hints 检索]来源:https://github.com/jarmstrong158/context-keeper / 项目说明书
Configuration, Deployment and Evaluation
context-keeper 是一个零依赖、默认离线的 MCP(Model Context Protocol)内存服务。整个项目的配置、部署与质量评估都围绕"小而稳"这一目标展开:交付物通过 PyPI 与 MCP 注册中心分发,打包脚本统一管理可分发的 bundle,而 token 预算等关键约束则通过回归测试固化为不变量。本页说明打包元数据、构建脚本与运行时配置约束之间的...
继续阅读本节完整说明和来源证据。
配置、部署与评估
概述
context-keeper 是一个零依赖、默认离线的 MCP(Model Context Protocol)内存服务。整个项目的配置、部署与质量评估都围绕"小而稳"这一目标展开:交付物通过 PyPI 与 MCP 注册中心分发,打包脚本统一管理可分发的 bundle,而 token 预算等关键约束则通过回归测试固化为不变量。本页说明打包元数据、构建脚本与运行时配置约束之间的协同关系。
打包与元数据配置
项目在多个注册中心保持一致的元数据描述,便于客户端在不同目录发现并安装:
pyproject.toml定义 Python 包元数据、入口点与可选依赖,用于发布到 PyPI 的context-keeper-mcp包。资料来源:pyproject.toml:1-40server.json是 MCP 注册中心的服务声明,描述 stdio 传输、入口命令以及能力列表。资料来源:server.json:1-40mcpb/manifest.json是 MCPB(MCP Bundle)的清单,声明 bundle 名称、版本、打包文件清单与图标。资料来源:mcpb/manifest.json:1-40glama.json提供给 Glama 注册中心使用的元数据镜像,使第三方目录能够同步。资料来源:glama.json:1-40
v0.15.0 修复了 stdio 的 UTF-8 问题、镜像 watermark 的数据丢失问题以及 mirror.py 的打包问题,并对描述做了裁剪以通过注册中心校验。这些改动共同表明:打包元数据不是孤立文件,而是端到端部署链上的约束点。
构建脚本与发布流程
两个辅助脚本把源码变成可分发资产:
scripts/build-mcpb.sh调用必要工具把mcpb/目录打包成可安装的 MCPB 归档,是发布到 MCP 注册中心前的最后一步。资料来源:scripts/build-mcpb.sh:1-40scripts/make_placeholder_icon.py生成打包时所需的占位图标,避免在没有设计资源的情况下阻塞发布流程。资料来源:scripts/make_placeholder_icon.py:1-40
发布流程可概括为:先用 pyproject.toml 构建并上传到 PyPI,再通过构建脚本生成 MCPB bundle,最后把 manifest 与 server 描述同步到 MCP 与 Glama 注册中心。任一环节的不一致——例如版本号、描述字段长度——都会让注册中心校验失败,v0.15.0 的"描述裁剪"正是为此而做的修复。
运行时约束与回归评估
部署之上,context-keeper 通过若干硬性不变量守护运行质量:
- Token 预算不变量:每次会话开始时,MCP 客户端会把完整的
tools/list注入到模型上下文。schema 描述从约 2800 token 压缩到约 2370 token,并加入 2500 token 的回归测试上限,防止后续字段无序增长。资料来源:server.json:1-40 - 零依赖、离线优先:与同类 MCP 内存工具"每次存取都调用一次 LLM 控制器"的路线不同,context-keeper 刻意保持无第三方依赖,避免引入额外网络请求与运行时成本。资料来源:pyproject.toml:1-40
- 数据完整性护栏:条目写入采用临时文件 +
os.replace的原子替换;任何无法解析的已存在条目都会让record_*、update_entry、deprecate_entry拒绝写入,把损坏的存储挡在写入路径之外。
| 阶段 | 关键文件 | 作用 |
|---|---|---|
| 打包元数据 | pyproject.toml / server.json / mcpb/manifest.json / glama.json | 声明 PyPI、MCP、Glama 三个分发通道 |
| 构建脚本 | scripts/build-mcpb.sh / scripts/make_placeholder_icon.py | 产出可安装 bundle 与图标 |
| 运行时评估 | token 预算回归测试 + 原子写入 + 损坏拒绝 | 守住 schema 大小与存储完整性 |
整体来看,配置与部署层决定"用户能不能装上",而回归与不变量决定"装上之后会不会退化"。两者由脚本串联、由回归测试闭环,构成 context-keeper 的最小可行发布体系。
来源:https://github.com/jarmstrong158/context-keeper / 项目说明书
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
风险会影响是否适合普通用户安装。
Pitfall Log / 踩坑日志
项目:jarmstrong158/context-keeper
摘要:发现 7 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:配置坑 - 可能修改宿主 AI 配置。
1. 配置坑 · 可能修改宿主 AI 配置
- 严重度:medium
- 证据强度:source_linked
- 发现:项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主,或安装命令涉及用户配置目录。
- 对用户的影响:安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
- 证据:capability.host_targets | https://github.com/jarmstrong158/context-keeper | host_targets=mcp_host, claude
2. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 证据:capability.assumptions | https://github.com/jarmstrong158/context-keeper | README/documentation is current enough for a first validation pass.
3. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 证据:evidence.maintainer_signals | https://github.com/jarmstrong158/context-keeper | last_activity_observed missing
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 证据:downstream_validation.risk_items | https://github.com/jarmstrong158/context-keeper | no_demo; severity=medium
5. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 证据:risks.scoring_risks | https://github.com/jarmstrong158/context-keeper | no_demo; severity=medium
6. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 证据:evidence.maintainer_signals | https://github.com/jarmstrong158/context-keeper | issue_or_pr_quality=unknown
7. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 证据:evidence.maintainer_signals | https://github.com/jarmstrong158/context-keeper | release_recency=unknown
来源:Doramagic 发现、验证与编译记录