关于 Rithmo
Rithmo 是只读 MCP 服务,可查询组织的决策记录并跟踪决策变化。它帮助 Agent 和自动化在行动前核对当前决策、来源及替代关系;没有记录时会明确返回无记录,而非编造答案。
主要功能
- 01用自然语言查询决策
- 02通过游标跟踪决策变化
- 03返回来源及替代历史
- 04区分缺失、暂缓和未观测记录
- 05验证组织范围内的访问
适用场景
- 01自动化执行前核对决策
- 02没有可用答案时暂停操作
- 03轮询决策状态变化
- 04追溯决策如何被替代
MCP 相关
向 MCP 客户端提供 `query_decision`、`list_decision_changes` 和 `whoami`。访问需使用只读且限定组织范围的服务令牌。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://app.rithmo.ai/api/mcpclaude mcp add --transport http ai-rithmo-rithmo https://app.rithmo.ai/api/mcp{"mcpServers":{"ai-rithmo-rithmo":{"type":"http","url":"https://app.rithmo.ai/api/mcp"}}}在 Cursor 中打开{"servers":{"ai-rithmo-rithmo":{"type":"http","url":"https://app.rithmo.ai/api/mcp"}}}在 VS Code 中打开{"mcpServers":{"ai-rithmo-rithmo":{"type":"http","url":"https://app.rithmo.ai/api/mcp"}}}{"mcpServers":{"ai-rithmo-rithmo":{"serverUrl":"https://app.rithmo.ai/api/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
https://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
根据自然语言问题查找当前权威决策。
输入
| field | type | required | notes |
|---|---|---|---|
question | string | 是 | 例如:“我们还会发布新的引导流程吗?” |
type | action_item | follow_up | decision | 否 | 限定为一种类型 |
owner | string | 否 | 限定为指定负责人(子字符串匹配,不区分大小写) |
area | string | 否 | 主题提示,用于调整检索权重 |
surface | string | 否 | 此任务涉及的外部系统(例如 "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_observed | Rithmo 未观察你询问的信息表面,因此缺失并不能证明任何事 | 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
获取自某个游标以来状态发生变化的决策。专为轮询触发器设计。
输入
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
since | string | 否 | 来自此前调用的不透明游标。首次调用时省略,从“现在”开始(不回溯);传入 "0_" 可从头回溯。 |
limit | integer | 否 | 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/,其中提供了可导入的工作流(在执行步骤之前查询决策)和设置说明。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。