Skip to content

feat(memory): expose expert memory as an MCP server for external agents - #356

Open
ArvinQi wants to merge 4 commits into
TencentCloud:mainfrom
ArvinQi:feat/memory-mcp-server
Open

feat(memory): expose expert memory as an MCP server for external agents#356
ArvinQi wants to merge 4 commits into
TencentCloud:mainfrom
ArvinQi:feat/memory-mcp-server

Conversation

@ArvinQi

@ArvinQi ArvinQi commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Adds a memory MCP server (Streamable HTTP at /mcp/memory) so external
agents (coding agents, bots, other AI tools) can directly read / write /
update Octop expert memory
, aligned 1:1 with the in-process
MemoryService capabilities. Every write stamps a source marker that is
traceable on recall.

Why

Octop experts accumulate rich memory (facts, conversations, decisions), but
today only the Octop dashboard / in-process agent can access it. External
agents that need to reuse that expertise (e.g. a coding agent asking a
business expert's accumulated knowledge) have no way in. This PR exposes the
same memory surface over the standard MCP protocol so any MCP-capable agent
can join the loop.

What

  • New module src/octop/infra/agents/memory_mcp.py — FastMCP server
    bound to one expert per connection, plus token auth and header routing.
  • Mount in api/app.py (build_app) at /mcp/memory, with
    streamable_http task groups wired into the FastAPI lifespan.
  • Tests tests/unit/agents/test_memory_mcp.py (13 tests).

Tools

Tool Purpose Backing API
memory_recall(query, limit=5) Recall memories (full pipeline) recall_for_prompt
memory_save(content, source, topic?) Persist a structured fact directly (durable) Memory.store
memory_capture(content, source, session_id?) Write an L0 raw event (goes through extraction) Memory.add_raw
memory_search_raw(query, limit=10) FTS-search L0 raw events Memory.search_raw
memory_update(atom_id, new_content, source) Deprecate old atom + save new deprecate_atom + store

Expert binding & auth

  • One connection binds one expert via X-Octop-Agent-Id header (callers never
    pass an agent id per tool call).
  • Auth: OCTOP_MEMORY_MCP_TOKEN (fail-closed), Authorization: Bearer.

raw vs atom (for callers)

  • memory_capture → L0 raw event (evidence layer), distilled by extraction;
    visible immediately via memory_search_raw.
  • memory_save → atom/tree directly (durable, no extraction).

Implementation notes

  • Lives in infra/agents/ (no api-layer dependency), opens Memory via
    open_memory_kwargs + Memory(...).
  • DNS rebinding protection disabled (behind reverse proxy).
  • streamable_http_path collapsed to / (endpoint = /mcp/memory).
  • One FastMCP per expert, routed by ASGI dispatcher on header; unknown → 404.

Usage example

{
  "mcpServers": {
    "octop-memory": {
      "type": "streamable_http",
      "url": "http://<host>/mcp/memory/",
      "headers": { "Authorization": "Bearer <token>", "X-Octop-Agent-Id": "<expert-id>" }
    }
  }
}

Target branch

  • Base is develop (feature / fix — default)
  • Base is main (release/* or hotfix/* only)

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactor / chore
  • Release / hotfix

Test plan

  • make all passes locally
  • Added/updated tests

Checklist

  • Updated CHANGELOG.md (if user-facing)
  • README / docs updated (if needed)

Add a memory MCP server (Streamable HTTP at /mcp/memory) so external
agents (coding agents, bots) can read/write Octop expert memory directly,
aligned with the in-process MemoryService capabilities.

Tools (per expert, bound at connect time via X-Octop-Agent-Id header):
- memory_recall(query, limit): full recall pipeline (tokenize + FTS +
  rerank), returns structured memories + rendered markdown
- memory_save(content, source, topic?): persist a structured fact directly
  into the atom/tree (durable, no extraction)
- memory_capture(content, source, session_id?): write an L0 raw event
  (extraction pipeline); visible immediately via memory_search_raw
- memory_search_raw(query, limit): FTS-search L0 raw events (capture
  visible before extraction)
- memory_update(atom_id, new_content, source): deprecate old atom + save new

Auth: independent token via OCTOP_MEMORY_MCP_TOKEN (fail-closed if unset);
authorization via Authorization: Bearer or X-Octop-Memory-Token.

Implementation:
- Lives in infra/agents/memory_mcp.py (no api-layer dependency; opens the
  agent Memory instance via open_memory_kwargs)
- One FastMCP per agent, routed by X-Octop-Agent-Id header at /mcp/memory
- DNS rebinding protection disabled (server runs behind a reverse proxy)
- streamable_http task groups wired into the FastAPI lifespan

Tests: tests/unit/agents/test_memory_mcp.py (tools, header routing, token
middleware, unified mount).
jinlongqi and others added 3 commits August 23, 2026 11:34
保留两者:
- main: enable_mobile 路由挂载
- PR: memory_mcp MCP server 挂载
- Add assert for server.services to satisfy mypy strict mode
- Add return type annotation to _memory closure
@Bosheng0422
Bosheng0422 self-requested a review August 25, 2026 02:26
@Bosheng0422

Copy link
Copy Markdown
Collaborator

hi 评估了一下这个PR 有几个想要询问的问题
1.晋升渠道没有看到,是打算依赖octop服务本身的记忆晋升吗?(raw->candidate->atom)
2.memory_save 在 octop里默认没有注册,默认通过每轮对话结束提取raw event进入记忆晋升管线,我看这里注册了
可以把设计方案发一下探讨一下

@ArvinQi

ArvinQi commented Aug 25, 2026

Copy link
Copy Markdown
Author

hi 评估了一下这个PR 有几个想要询问的问题 1.晋升渠道没有看到,是打算依赖octop服务本身的记忆晋升吗?(raw->candidate->atom) 2.memory_save 在 octop里默认没有注册,默认通过每轮对话结束提取raw event进入记忆晋升管线,我看这里注册了 可以把设计方案发一下探讨一下

hi 谢谢 review,两个问题都问到点子上了,简单说一下:

  1. 是的想用octop本身的记忆晋升,不直接干预晋升流程
  2. save和update的确有点让人误解,MVP只需要recall和capture可能就够了,但capture之后直接recall是没有结果依赖晋升管线,这里save和update主要是想用在用户明确需要遵守的规则或要求可以直接更新不需要走晋升了,这个可以讨论下

设计文档已整理(本 PR diff 里的 PR.md 是一版简版),完整版我贴在回复里/PR 描述中,欢迎继续探讨。

Octop 专家记忆 MCP 服务设计说明(PR #356

1. 背景与目标

Octop 专家会积累大量记忆(事实、对话、决策、规则),目前只有 Octop 站内 agent /
控制台能访问。外部 agent(coding agent、bot、其他 AI 工具)无法复用这些专家知识。

本 PR 在标准 MCP 协议(Streamable HTTP,端点 /mcp/memory)上暴露同一套专家记忆能力,
让任何 MCP 兼容的 agent 都能加入读写循环。

2. 总体设计(一句话)

读走完整召回管线,写分两条通道,晋升流程完全留在服务端。

能力 工具 语义
读(原子) memory_recall 完整召回管线(分词→FTS→rerank→去重),返回结构化片段 + 可直接注入 system prompt 的 markdown
读(原始) memory_search_raw FTS 检索 L0 raw event(capture 后即时可见)
写(走管线) memory_capture 写入 L0 raw event,进入站内提取/晋升管线
写(规则通道) memory_save / memory_update 用户明确要求遵守的规则/指令,直接落原子(权威直写,不走候选评审)

3. 记忆晋升边界:MCP 不干预晋升流程

  • raw → candidate → atom 的提取、候选、审批、晋升完全由 Octop 服务自身的记忆管线负责
    MCP 层不暴露任何提取触发、候选列表、审批晋升入口。
  • memory_capture 写入的是 L0 raw event(host="mcp-external"),之后由站内现有管线统一蒸馏:
    • 站内会话记忆:走 extract_on_session_end(会话结束提取,默认开启);
    • 外部 capture 的记忆:不在任何会话内,由周期 sweepextract_interval_seconds,默认
      21600s ≈ 6 小时)兜底拾取。
  • 因此外部写入与站内会话产生的记忆走同一条晋升与治理路径,不会出现两套记忆质量规则。

时效说明(明确契约)

外部 capture 的晋升时效依赖周期 sweep,MVP 阶段接受"分钟~小时级"延迟;
capturerecall 无结果属预期行为,即时查询请用 memory_search_raw
若后续需要更实时,可在非 MVP 版本增加"按需触发提取"的信号接口(仍不暴露晋升审批本身)。

4. 两条写通道

站内默认记忆路径是"每轮对话结束提取 raw event → 候选评审 → 晋升",站内不给 agent
注册原子直写工具。MCP 层把"写"分成两类,刻意隔离:

4.1 capture 通道(学习型记忆)

  • 适用:对话/事件中"学到的"内容(事实、经历、观察)。
  • 语义:写入 L0 raw event,走标准提取与候选评审,质量由站内管线把关。
  • 特性:写入后即时可通过 memory_search_raw 检索,晋升为 atom 后才能被 memory_recall 召回。

4.2 规则通道(指令型记忆)— save / update

  • 适用:用户明确要求遵守的规则或要求(合规红线、固定流程、回复风格约束等)。
  • 语义:权威指令,直接落 atom(Memory.store,metadata 带 source 标记),
    更新走 deprecate_atom + store(旧原子废弃、新事实落库,可追溯)。
  • 为什么绕过候选评审:规则/要求是权威内容,不应被提取器改写,也不应排队等待晋升;
    它们需要即时生效。
  • 质量责任:由调用方保证内容权威性,source 必填 + journal 审计保证可溯源。
  • 隔离手段:落库时打 metadata.kind="rule"(建议),recall 时规则类可加权/置顶,
    与普通对话记忆区分;可通过配置开关(如 OCTOP_MEMORY_MCP_RULES_ENABLED)控制是否注册。

5. MVP 工具集与调用契约

MVP 必带:memory_recall + memory_capture + memory_search_raw
memory_save / memory_update 以规则通道形式保留(可配置开关)。

# 召回(读原子)
memory_recall(query="key project decisions", limit=5)
# 记录原始事件(写,走晋升管线;即时查询用 search_raw)
memory_capture(content="user reported: banner not rendering", source="review-bot", session_id="review-1")
# 检索原始事件(读 L0,capture 后即时可见)
memory_search_raw(query="banner not rendering")
# 规则直写(可选通道):用户明确要求遵守的规则,直接生效
memory_save(content="release window is every Tuesday", source="coding-agent", topic="release")
memory_update(atom_id="atom_xxx", new_content="release window is every Thursday", source="coding-agent")

6. 专家绑定与鉴权

  • 专家绑定:端点统一为 /mcp/memory,连接时通过 X-Octop-Agent-Id header 选择专家
    (一个连接绑定一个专家,调用方无需知道专家 id 列表,URL 不泄露 id)。
  • 鉴权:独立 token OCTOP_MEMORY_MCP_TOKEN(未配置时不挂载,fail-closed),
    支持 Authorization: BearerX-Octop-Memory-Token
  • 已知边界:当前 token 为全局单值,持有 token 者可访问任意专家记忆(agent 仅靠
    header 区分、不鉴权)。若需按专家隔离,属后续版本(per-expert token / ACL)。
  • 关闭 DNS rebinding 保护:Octop 部署在反向代理后(Host 为公网域名),SDK 的 localhost
    防护不适用,否则会 421。

7. 与站内默认记忆路径的关系

维度 站内会话 MCP 外部写入
读取 召回管线(同) 同一 recall_for_prompt
写(学习) 会话结束提取 raw event memory_capture 写 raw event,走同一管线
写(规则) 站内默认不提供原子直写 memory_save/update 规则通道(刻意隔离)
晋升 站内管线 不干预,完全复用站内管线

8. 后续演进(非 MVP)

  • 按需触发提取信号接口(提升外部 capture 的晋升时效);
  • per-expert token / ACL(解决全局 token 的隔离边界问题);
  • memory_recall 增加 include_raw 可选参数(capture 后空窗期的召回兜底,MVP 不加)。

9. 实现说明

  • 模块:src/octop/infra/agents/memory_mcp.py(infra 层,无 api 依赖);
  • 挂载:api/app.py build_app/mcp/memory,streamable_http task group 并入 FastAPI lifespan;
  • 存储:通过 open_memory_kwargs + Memory(...) 打开专家实例(sqlite 默认 / postgres 可选),
    与站内 agent 同一存储;
  • 测试:tests/unit/agents/test_memory_mcp.py(13 项:工具注册、召回、capture 语义、
    search_raw、update、token 中间件 401/通过、header 路由、404、统一挂载、fail-closed)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants