首页
/ claude-mem Server API:/v1 REST 接口、API Key 鉴权、事件生成管线与远程 MCP 召回全解析

claude-mem Server API:/v1 REST 接口、API Key 鉴权、事件生成管线与远程 MCP 召回全解析

2026-09-06 18:52:57作者:廉皓灿Ida

本文以 claude-mem 仓库的 docs/api.md 为主体,逐条解析 Server Beta 的 REST V1 API:从 API Key 鉴权与读写 scope 模型,到限流/配额/计量的三个环境开关,再到事件生成的 generate/wait 语义、远程 MCP 召回端点与数据删除(forget)接口。读完本文,你可以独立完成一个云端记忆服务的接入:签发只读 Key、把 MCP 链接粘贴进 Claude Code、用 REST 写入 agent 事件并追踪生成任务,同时理解每个接口在 ServerV1PostgresRoutes.ts 中的真实实现与保障边界。

API 端点总览

REST V1 挂载在 /v1 下,遗留 worker 路由仍保留在 /api 下(兼容层见 compat 适配层)。当前可用的 beta 端点完整列表如下(源自 api.md):

方法 路径 用途
GET /healthz 健康检查
GET /v1/info 服务名称、版本、runtime、authMode
GET /v1/projects 列出项目(项目级 Key 只返回自己的项目)
POST /v1/projects 创建项目
GET /v1/projects/:id 读取单个项目
POST /v1/sessions/start 开启(或幂等查找)一个 server session
POST /v1/sessions/:id/end 结束 session,入队会话摘要生成任务
GET /v1/sessions/:id 读取 session
POST /v1/events 写入单条 agent 事件,可选触发 observation 生成
POST /v1/events/batch 批量写入(1~500 条,事务原子插入)
GET /v1/events/:id 读取单条事件
POST /v1/memories 直接插入一条 observation(手动/兼容路径)
GET /v1/memories/:id 读取单条记忆
PATCH /v1/memories/:id 更新记忆(projectId 不可变更)
POST /v1/search 全文搜索 observations
POST /v1/context 搜索并返回可直接注入 prompt 的 context 拼接串
ALL /v1/mcp 远程 MCP 召回端点(streamable-HTTP)
POST /v1/keys 为调用方团队签发只读 API Key,返回可粘贴的 connect 命令
GET /v1/connect 返回带 <YOUR_API_KEY> 占位符的 connect 命令(GET 不签发)
GET /v1/usage 当前团队本月的分类型用量汇总
DELETE /v1/memories/:id 删除单条 observation(sources 级联删除)
DELETE /v1/projects/:projectId/memory 清空整个项目的所有捕获内容
GET /v1/audit?projectId=<id> 查询项目级审计日志

CLAUDE_MEM_AUTH_MODE=api-key(默认)模式下,请求需携带 Authorization: Bearer <key>。读端点要求 memories:read scope,写端点要求 memories:write

从源码结构看,这套路由由两层组成:

  • ServerV1Routes.ts —— 本地 SQLite 运行时版本,其中 hasSearchableContent 守卫(第 31–49 行)会在 POST /v1/memories 时拒绝任何「可搜索字段全空」的记录,避免写入一条对 FTS 索引不可见的"冻结"记忆;
  • ServerV1PostgresRoutes.ts —— Server Beta 的 Postgres 运行时版本,是本文档描述的限流、配额、generate/wait 语义、远程 MCP、数据删除等付费就绪特性的宿主。

鉴权与 scope 模型:读 Key 不能变出写 Key

Postgres 运行时的鉴权中间件在 postgres-auth.ts 中实现,几个关键设计值得注意:

  1. 只存哈希:客户端传来的 Key 会被 SHA-256 哈希后按 key_hashapi_keys 表(verifyPostgresApiKey,第 124–168 行),数据库不保留明文。被吊销(revoked_at)或已过期的 Key 直接拒绝。
  2. scope 校验:路由注册时即区分 readAuth / writeAuth(分别绑定 memories:readmemories:write),hasRequiredScopes 支持 * 通配。
  3. 团队/项目双级作用域:Key 行的 team_idproject_id 直接进入 req.authContext。所有 /:id 读取都先做 team 作用域探查,跨团队请求统一返回 404 而不是 403——这是刻意设计,避免响应状态泄露「某个资源在别的团队存在」这一事实。项目级 Key 访问其他项目则返回 403
  4. 本地开发旁路:仅当 authMode === 'local-dev'CLAUDE_MEM_ALLOW_LOCAL_DEV_BYPASS=1、请求来自 loopback 且无代理头时才生效,绝不用于生产路径。

这个模型在「签发 Key」接口上体现得最充分:POST /v1/keys 挂在 writeAuth 之后——即必须先拥有写权限才能签发一把只读 Key,从源头阻止读 Key 自我提权(源码注释在 ServerV1PostgresRoutes.ts 第 191–195 行)。

签发 MCP 客户端 Key(key issuance + connect)

POST /v1/keyswrite scope)为调用方团队签发一把只读 API Key,并返回可直接粘贴的 connect 命令。Body 支持:

{ "expiresInDays": 30 }   // 可选,1–365 天,源码 z.number().int().positive().max(365)

响应示例(原文档示例,与源码第 219–226 行一致):

{
  "id": "...",
  "apiKey": "cm_...",
  "scopes": ["memories:read"],
  "expiresAt": null,
  "mcpUrl": "https://<host>/v1/mcp",
  "connectCommand": "claude mcp add --transport http claude-mem https://<host>/v1/mcp --header \"Authorization: Bearer cm_...\""
}

实现细节:Key 明文以 cm_ 前缀加 24 字节随机 hex 生成,库中只落 SHA-256 哈希(randomBytes/createHash,第 204–205 行)——明文只在响应中出现一次,错过即需重新签发。

GET /v1/connect(read scope)返回同样的命令,但 Key 位置是 <YOUR_API_KEY> 占位符(GET 绝不签发),并附带 hint 提示用 POST /v1/keys 获取真实 Key。mcpUrl 优先取自环境变量 CLAUDE_MEM_PUBLIC_URL(建议置于反向代理之后),否则由请求 Host 推导——见 mcpConnectUrl第 46–50 行)。

原文档也如实记录了冷启动限制:团队第一把 Key 的签发仍需要会话路径(web dashboard);better-auth 的 apiKey() 插件存在,但它写入的存储与这些路由实际鉴权的 Postgres api_keys 表不是同一份,org → Server Beta 团队的映射接线是遗留工作。该行为的回归保护见 connect-keys.test.ts

限流、配额与用量计量:全部是 opt-in 环境开关

api.md 明确:这些付费就绪守卫在鉴权之后运行,且全部通过环境变量开启——不设置(默认)就没有限流、没有配额、没有计量,行为与关闭时完全一致。源码在路由注册时集中体现(第 163–180 行):中间件顺序固定为 限流 → 配额 → 计量,只有被放行的请求才会被计数。

环境变量 语义 触顶响应
CLAUDE_MEM_RATE_LIMIT_PER_MIN 每个 API Key 每分钟的最大请求数(固定 60 秒窗口,按 API Key id 计数) 429 + Retry-After,并携带 X-RateLimit-Limit/Remaining/Reset 头;fail-open
CLAUDE_MEM_MONTHLY_REQUEST_CAP 每团队每自然月(UTC)最大请求数 402 quota_exceeded,body 含 usedcap;fail-open
CLAUDE_MEM_MONTHLY_TOKEN_CAP 每团队每月最大 provider token 数。只拦截写路径(ingestion 驱动生成 = token 消耗),读永远可用,超预算团队仍可召回 402;fail-open
CLAUDE_MEM_USAGE_METERING=1 为每个已认证调用记录一条 request 类型 usage 事件(fire-and-forget)

两个值得强调的实现事实(rate-limit.tsusage-metering.ts):

  • fail-open 是刻意的:限流/配额存储抖动时记录 warn 日志并放行请求,绝不让计量组件拖垮 API 可用性(rate-limit.ts 第 56–64 行)。
  • 计量零延迟meterRequestsvoid repo.record(...).catch(...) 异步落 usage_events 表,请求路径不等待写库;token/observation 维度的计量则由生成 worker 写入同一张表,/v1/usage 因此能给出分类型汇总。

GET /v1/usage 返回调用方团队本月的分类型累计(since 为当月 UTC 1 号 0 点):

{ "since": "2026-06-01T00:00:00.000Z", "usage": { "request": 1280, "observation": 44 } }

写路径与读路径守卫差异在源码中一目了然:writeGuards 在基础 guards 之后额外追加 kind: 'tokens' 的月度配额中间件(第 174–180 行),这正是"token 配额只卡写、读保持可用"文档承诺的落点。相关测试见 paid-readiness.test.ts

事件生成语义:generate 与 wait 两个查询开关

POST /v1/events(以及 /v1/events/batch)接受两个控制 observation 生成的查询参数:

  • generate=false —— 只写事件行,不入队生成任务(响应中无 generationJob)。
  • wait=true —— 在返回前轮询 outbox 行直到任务到达终态(completed / failed / cancelled),响应中的 generationJob 描述符必然填充(仅当显式禁用生成时才为 null)。

不加 wait=true 时,响应包含新事件行和一个 best-effort 的 generationJob 字段。真正的 provider 调用发生在独立的 BullMQ worker 进程中(claude-mem server worker start)——HTTP 路径从不阻塞等待 provider 响应

源码层面这条管线非常完整:

  1. 事务内 outboxIngestEventsService.ingestOne 在单个 Postgres 事务中写入 agent_events 行、outbox 行(observation_generation_jobssource_type='agent_event')和生命周期日志行,然后提交后才发布 BullMQ 任务。发布失败时 outbox 行保持 queued,由启动对账(startup reconciliation)补发——HTTP 层绝不会谎报"已入队"。
  2. 确定性 job idbuildServerJobId(kind, team_id, project_id, source_type, source_id) 派生,天然幂等,重试时重复发布会在队列侧收敛。
  3. wait 的硬超时WAIT_TIMEOUT_MS = 30_000、轮询间隔 100ms(第 81–82 行)。即使 provider 卡死,调用方也总能拿到响应;超时会在响应中附带 waitTimedOut: true
  4. 批量语义/v1/events/batch 先对 1–500 条数组做 Zod 校验与项目作用域预校验(preValidateBatch),在事务中原子插入后再逐条发布;wait=true 时为每个任务分摊剩余等待预算。

POST /v1/sessions/:id/end 走同一套 outbox 模式:置 ended_at(幂等)并入队 session-summary 生成任务;(team_id, project_id, source_type='session_summary', source_id) 上的 UNIQUE 约束保证重复 end 不会产生第二行任务。

远程 MCP 端点:一条只读、可审计的云端召回链接

/v1/mcp 是一个 streamable-HTTP 的 MCP 服务器——用户粘贴进 Claude Code(或任何 MCP 客户端)来召回云端记忆的"安全链接"。它与 REST 读路由使用同一把 API Keymemories:read scope)鉴权,Key 绑定的团队(以及项目级 Key 的项目)约束所有读取。

连接命令(与 POST /v1/keys 返回的 connectCommand 一致):

claude mcp add --transport http claude-mem <server-base>/v1/mcp \
  --header "Authorization: Bearer cm_..."

三个工具定义在 recall-mcp-server.tsTOOLS 常量中,与文档一一对应:

工具 参数 返回 limit 约束(default/max)
search { projectId, query, limit? } 匹配的 observations(FTS,与 POST /v1/search 同一路径) 20 / 100
context { projectId, query, limit? } observations + 用 \n\n 拼接好的 context 串,可直接注入 prompt(同 POST /v1/context 10 / 50
recent { projectId, limit? } 该项目最新的 observations 20 / 100

实现要点(对照 ServerV1PostgresRoutes.ts 第 1003–1063 行):

  • 无状态传输:每个请求新建一个 StreamableHTTPServerTransportsessionIdGenerator: undefined)+ 一个 MCP server,响应关闭时即销毁——负载均衡器背后不需要会话亲和。
  • 只读是硬约束createRecallMcpServer 的 backend 只暴露 search/context/recent 三个方法,不存在任何 mutating 工具——粘贴出去的召回链接不可能写入
  • 审计无例外:MCP 每次工具调用都会以 mode: 'search'/'context'/'recent', via: 'mcp' 写审计行,与 REST 读路径同一套审计口径。
  • 方法收敛:路由只注册 POST(JSON-RPC)与 GET(SSE)到 /v1/mcpDELETE/PUT/PATCH/OPTIONS 不会白跑一遍鉴权与传输初始化。

MCP 读路径与 REST 读路径共享同一个 PostgresObservationRepository.search,FTS 基于 GIN tsvector 索引,按 ts_rank 降序再按 updated_at 降序排序(第 906–950 行),因此同一份记忆无论从 REST 还是 MCP 召回,结果与守卫完全一致。

数据删除(forget):受控的右至被遗忘

两个删除接口都要求 write scope 且严格限定在调用方团队内:

  • DELETE /v1/memories/:id —— 删除单条 observation,其 observation_sources 级联删除;对该团队不存在时返回 404。项目级 Key 只能在自己项目内删除;团队级 Key 按 id + team_id 跨项目匹配(deleteObservationForScope第 1294–1308 行)。
  • DELETE /v1/projects/:projectId/memory —— 清空该项目捕获的全部内容(observations、agent events、sessions、generation jobs),保留项目壳(project shell),响应返回逐表 counts

第二个接口的源码里有一段很有价值的防御性注释:ensureProjectAllowed 只校验 Key 的可选项目 scope,若不做额外确认,团队级 Key 就能对任意 projectId 发 purge 请求并以零计数 200 返回——把未授权的清空误报为成功。因此实现中先用 PostgresProjectsRepository.getByIdForTeam 确认项目归属本团队,否则 404(第 1086–1113 行)。

两个操作都会写审计日志:observation.deleted / project.memory_purged,且审计行的 details 携带 via: 'api'requestId,可回溯到具体 HTTP 请求。删除逻辑本体在 data-deletion.ts,行为由 data-deletion.test.ts 覆盖。

验证与测试入口

以上行为均有对应测试可复核,集中在 tests/server/ 目录:

小结

docs/api.md 描述的是 claude-mem 云端记忆服务(Server Beta)对外的完整契约:/v1 REST 负责项目的写入与运营(events/sessions/memories/jobs 的生成与删除),/v1/mcp 负责一条只读、无状态、全审计的 MCP 召回链路,/v1/keys + /v1/connect 负责把这两者安全地连接起来。三个计量守卫(CLAUDE_MEM_RATE_LIMIT_PER_MIN / CLAUDE_MEM_MONTHLY_REQUEST_CAP / CLAUDE_MEM_MONTHLY_TOKEN_CAP / CLAUDE_MEM_USAGE_METERING=1)全部 opt-in、fail-open,默认不改变任何行为;token 配额只卡写路径,保证超预算团队仍可读取记忆。生成管线以"事务 outbox + 提交后 BullMQ 发布 + 确定性 job id"保证事件不丢、不重、可重试,wait=true 提供 30 秒硬上限的同步等待。对需要自建团队记忆后端的读者而言,这条从鉴权、计量到审计的完整链路,是一个值得逐文件对照的多租户 API 参考实现。

登录后查看全文
热门项目推荐
相关项目推荐