mcpField
R
Rithmo

Rithmo

远程服务
rithmo.ai记忆与知识05 周前更新

关于 Rithmo

Rithmo 是只读 MCP 服务,可查询组织的决策记录并跟踪决策变化。它帮助 Agent 和自动化在行动前核对当前决策、来源及替代关系;没有记录时会明确返回无记录,而非编造答案。

主要功能

  1. 01用自然语言查询决策
  2. 02通过游标跟踪决策变化
  3. 03返回来源及替代历史
  4. 04区分缺失、暂缓和未观测记录
  5. 05验证组织范围内的访问

适用场景

  1. 01自动化执行前核对决策
  2. 02没有可用答案时暂停操作
  3. 03轮询决策状态变化
  4. 04追溯决策如何被替代

MCP 相关

向 MCP 客户端提供 `query_decision`、`list_decision_changes` 和 `whoami`。访问需使用只读且限定组织范围的服务令牌。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://app.rithmo.ai/api/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

全部服务地址
Streamable HTTPhttps://app.rithmo.ai/api/mcp需要授权

工具

详细介绍

Rithmo MCP — 示例

用于连接 Rithmo 只读 MCP 服务的可运行示例。

Rithmo 是决策记录的权威来源。此服务让你的工具可以在采取行动前查询决策记录——“关于 X,我们实际决定了什么?”——并在决策变更时做出响应。Rithmo 会发出记录;你的工具据此采取行动。Rithmo 绝不会在你的系统中采取行动。

它使用 Model Context Protocol,因此可与 n8n、AI Agent 及其他任何使用 MCP 的工具配合使用。

初次接触?请从集成指南开始——了解它是什么、如何获取访问权限、如何连接,以及完整的工具参考。

几分钟内即可运行

git clone https://github.com/Rithmo-Inc/rithmo-mcp-examples
cd rithmo-mcp-examples
npm install
cp .env.example .env      # then edit .env

在 .env 中设置两个值:

  • RITHMO_MCP_URL — Rithmo 实例的 MCP 端点。Rithmo Cloud 的地址是 https://app.rithmo.ai/api/mcp;可自托管的部署会在你自己的地址提供同一服务。
  • RITHMO_MCP_TOKEN — 每个组织专用的 Rithmo MCP 服务令牌(只读,限定为一个组织,并带有 mcp:read 作用域)。

然后:

node examples/query_decision.mjs "What did we decide about GitHub integration?"
node examples/no_record.mjs
node examples/list_decision_changes.mjs

你将看到什么

query_decision — 当前决策,或 no_record:

Q: What did we decide about GitHub integration?
✓ found (confidence 0.66)
  answer: Look into GitHub integration details, including permissions and OAuth requirements.
  status: open
  owner:  participant_50332672
  source: meeting · <source ref>
  decided_at: 2026-07-09T14:39:48.296Z

no_record — Rithmo 不会捏造决策(这是它的差异化特点):

Q: Did we decide anything about office snacks?
∅ no_record — Rithmo will not fabricate a decision.
   Branch here: pause the automation, route to a human, or use a safe default.

no_record 可能附带类型化拒绝,说明记录为何被暂缓:

Q: Are we still migrating to the new billing provider?
∅ no_record — held (closed · superseded)
   subject: Billing provider migration
   held_because: That decision was superseded; acting on the old premise is unsafe.
   safe_to_proceed: false

list_decision_changes — 通过游标逐批获取的决策状态变更流:

changes: 20   has_more: true
next cursor: 1783606904319_f901849e-...   ← pass this as the argument on the next poll
  [open] Push the local build after the meeting ends.  (changed 2026-07-09T14:21:44.319Z)
  ...

工具

query_decision

根据自然语言问题查找当前权威决策。

输入

fieldtyperequirednotes
questionstring是例如:“我们还会发布新的引导流程吗?”
typeaction_item | follow_up | decision否限定为一种类型
ownerstring否限定为指定负责人(子字符串匹配,不区分大小写)
areastring否主题提示,用于调整检索权重
surfacestring否此任务涉及的外部系统(例如 "jira"、"salesforce")。如果 Rithmo 未观察该系统,未找到记录时会返回类型化的 not_observed,而非单纯的 no_record——在未观察的系统上没有记录意味着未知,而非“没有”。

输出 — result 是一个带判别标签的联合类型,有 三种变体: "found"、"found_record" 和 "no_record"。

⚠️ 不要写 result === "found" ? act : treat-as-no-record。 found_record 是一个有效答案,而两种状态的分支逻辑会悄悄将有效答案导入无记录路径。

但也不要改成 ["found", "found_record"].includes(result)——这同样 不正确。found_record 可能指向一个确实有记录的主题,但没有答案文本(subject.currentAnswer === null)。正确做法是根据可用答案进行判断:

const r = res.structuredContent;
const answer =
  r.result === 'found'        ? r.answer :
  r.result === 'found_record' ? r.subject.currentAnswer :
  null;
if (answer != null && String(answer).trim() !== '') { /* act */ }
else { /* hold — no answer to act on */ }
result: "found" — 来自承诺账本的实时答案
{
  "result": "found",
  "answer": "<verbatim decision text>",
  "status": "open | in_progress | blocked | fulfilled",
  "lifecycle_state": "confirmed",
  "owner":  { "state": "owned", "name": "...", "user_id": "..." },   // or { "state": "unowned" }
  "source": { "state": "resolved", "system": "meeting|slack|github|...", "ref": "...", "url": "..." }, // or { "state": "unresolved" }
  "decided_at": "2026-07-19T15:04:00Z",
  "due_at": "2026-07-25T00:00:00Z",   // or null
  "rationale":  "<the WHY behind the decision>",   // or null
  "conditions": "<what it depends on>",            // or null
  "director":   "<who assigned/directed it>",      // or null
  "context":    { /* arbitrary payload */ },       // or null
  "supersedes": {                                  // or null
    "answer": "<the decision this one replaced>",
    "decided_at": "2026-06-02T10:00:00Z",
    "supersedes": null                             // recurses — walk it for the full chain
  },
  "spine": {                                       // or null; present when spine context resolves
    "decision_id": "...",
    "lifecycle_state": "<canonical live state>",
    "verified": true,
    "board_status": "..." ,                        // or null
    "presence": [{ "provider": "github", "external_status": "...", "external_url": "..." }],
    "related_count": 3
  },
  "confidence": 0.71
}

supersedes 是替代历史。 它是一个链表,按时间从新到旧排列:每个节点都是此答案所取代的先前决策,并带有自己的 supersedes 指针。遍历它即可重建答案随时间的变化过程(例如:云端 → 私有环境 → 客户服务器)。null 表示此决策未取代任何内容。此链设有循环保护,最多遍历 12 层。

result: "found_record" — 来自主体记录的实时答案

当主体记录中有当前答案,而承诺账本中没有时返回。这是答案,不是拒绝。

{
  "result": "found_record",
  "subject": {
    "subjectId": "...",
    "label": "<human-readable subject>",
    "state": "<subject state>",
    "currentAnswer": "<the current answer>"   // may be null
  },
  "observation": {                            // optional
    "sinceMs": 1783606904319,
    "claim": "<nothing observed since … bears on this subject>",
    "surfaces": [
      { "surface": "slack", "observedFromMs": 0, "observedThroughMs": 0, "basis": "..." }
    ]
  }
}

答案位于 subject.currentAnswer,而不是 answer。

currentAnswer 可能为 null,这种情况不可采取行动。 主体确实有记录——Rithmo 知道你在询问什么——但没有可供采取行动的答案文本。这是一个有意义的信号(主体存在,但没有答案),与 no_record 不同;no_record 表示 Rithmo 完全找不到有把握的匹配项。无论哪种情况,你都必须暂缓:将答案为 null 的 found_record 按与 no_record 相同的路径处理。

result: "no_record" — 没有有把握的答案
// bare form — no confident match, no refusal detail
{ "result": "no_record" }

// typed-refusal form — `record` explains WHY
{
  "result": "no_record",
  "record": { /* one of the four shapes below */ }
}

如果存在,record.status 的值为以下之一:

record.status含义额外字段
closed主体事项已解决,前提已失效disposition:reversed | abandoned | superseded;subject
no_record已查阅记录,确实一无所获reason(字符串)
not_observedRithmo 未观察你询问的信息表面,因此缺失并不能证明任何事unobserved_surfaces:string[]

这三种状态都包含 premise_check;closed/no_record/not_observed 还可能包含 observation.surfaces 数组,用于描述读取了哪些内容以及读取的时间范围。

"premise_check": {
  "safe_to_proceed": false,
  "held_because": "<why acting on this premise is unsafe>"
}

只要存在 premise_check.safe_to_proceed,它的值就始终为 false。 它不是一个需要判断的布尔值,而是一个标记,表示答案处于暂缓状态;held_because 会说明原因。无论是否带有 premise_check,都绝不可在 no_record 时继续操作。

有一种 reason 值需要特别处理:record.status: "no_record" 且 reason 以 record_unavailable: 开头,表示记录无法读取(暂时性故障)。这不表示“没有记录”,也不表示“我们决定不做”——应暂缓并重试。

说明:

  • 答案具有确定性。 对于同一个已解析的决策,每次调用返回的 found 字段除 confidence 外都逐字节相同(内容直接来自记录,响应路径中不涉及 LLM)。confidence 是排序检索信号,可能略有波动;不要根据其精确值设置门槛。
  • owner 和 source 是有区分的状态,绝不会是裸 null——unowned 和 unresolved 都是真实且有意义的答案,代码可据此分支处理。
  • no_record 是预期情况,不是错误。应将其作为一个分支处理:Rithmo 没有有把握的答案,因此不要根据猜测采取行动。
list_decision_changes

获取自某个游标以来状态发生变化的决策。专为轮询触发器设计。

输入

字段类型必填说明
sincestring否来自此前调用的不透明游标。首次调用时省略,从“现在”开始(不回溯);传入 "0_" 可从头回溯。
limitinteger否1–200,默认值为 50。超出范围或非整数的值会被拒绝,并返回验证错误,不会静默截断。

输出

{
  "changes": [
    {
      "decision_id": "...",
      "answer": "...",
      "status": "open | in_progress | blocked | fulfilled | dropped | superseded",
      "lifecycle_state": "confirmed",
      "owner": { ... }, "source": { ... },
      "decided_at": "ISO", "due_at": "ISO|null",
      "rationale": "...|null", "conditions": "...|null",
      "director": "...|null",  "context": {...}|null,
      "changed_at": "ISO"
    }
  ],
  "cursor": "<opaque — pass as `since` next time>",
  "has_more": true   // page was full; call again to drain the rest
}

每项变更都携带与 query_decision 的 found 相同的记录核心,包括 rationale、conditions、director 和 context。它不包含 supersedes、 spine 或 confidence;这些字段仅用于 query_decision。

此处的 status 取值范围比 found 更广。撤销也会作为变更发出,因此 status 可以是 dropped 或 superseded——这两个值不会由 query_decision 的 found 返回(它只会解析为仍有效的行)。如果将此数据流镜像到 自己的存储中,请显式处理这两个值。

游标语义:

  • **首次调用(不传 since)**返回 changes: [] 和当前的头部游标。你将从现在开始, 不会重放历史记录。has_more 为 false。
  • has_more 的值为 rows.length === limit,表示当前页已满。即使最后一页刚好 填满,值也可能为 true,因此下一次调用可能会合法地返回零项变更。这不是错误; 这是数据排空过程的结束方式。
  • 当一页没有返回任何行时,cursor 会原样返回,因此使用它重新轮询是安全且幂等的。
  • 按 (changed_at, decision_id) 排序。
whoami

不需要参数。返回 { org_id, scopes },可用于快速验证令牌和 连接。

身份验证

每个请求都通过 Bearer 标头携带一个组织专属服务令牌:

Authorization: Bearer rmcp_...

该令牌只对应一个组织,并且必须具有 mcp:read 范围;所有结果都限定在该组织内。请将 令牌保存在机密/凭据存储中,绝不要写入代码。

没有令牌或令牌未知的请求会被拒绝,并返回 401。不支持匿名访问。

注册表

此仓库中的 server.json 描述了 Rithmo 的 MCP 服务,其注册表标识为 ai.rithmo/rithmo(版本 0.1.0)。完整文档请参阅 https://rithmo.ai/mcp。

部署模式

上文的 RITHMO_MCP_URL 指向 Rithmo Cloud。可自托管的 Rithmo 部署会在客户自己的地址 提供相同的 MCP 服务——工具和协议约定相同,主机不同。

在 n8n 中使用

请参阅 n8n/,其中提供了可导入的工作流(在执行步骤之前查询决策)和设置说明。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。