关于 orangerail
orangerail 根据 Prisma schema 生成类型化 MCP 服务,为 Agent 提供有界数据库工具,而非任意查询。它面向需要为指定写入设置人工审批并追踪治理变更的团队。
主要功能
- 01从 Prisma schema 生成类型化工具
- 02按对象提供 get 和 list 工具
- 03根据 schema 生成写入操作
- 04为指定写入设置人工审批
- 05治理基线与审计链
- 06交互式领域关系图
适用场景
- 01让 Agent 执行获准的数据库读取
- 02无人值守地运行常规写入
- 03暂存破坏性写入以供后续审核
MCP 相关
它生成的 MCP 服务公开基于 schema 的读写工具及 check_approval,不提供任意查询工具;指定写入可暂存,等待人工审批。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍

在包含三个模型的 Prisma schema 上运行 orangerail init,然后对它生成的服务实际调用 tools/list。共 16 个工具:每个对象各有一个 get 和一个 list,每种写入操作各有一个工具,以及 check_approval。列表中的工具都不接受查询。 三个锁是 --gate delete,这是一个可以通过一行配置更改的默认设置,并非最终判定。
规则文件做不到这一点。 它可以要求 Agent 不要运行查询,却无法将工具从列表中移除——而且我们测量了这种差异的价值,其中包括我们验证后不再成立的四项主张。
orangerail 会读取你已有的 schema,并据此生成 Agent 可用的工具界面。
orangerail init 会将 prisma/schema.prisma 转换为 MCP 服务:每个对象各有一个 get 和一个 list,每种写入操作各有一个带 zod 输入 schema 的工具,仅此而已——没有 execute_sql,工具列表中也没有任何接受查询的工具。它是一个扫描器和代码生成器,不会调用大模型,也不需要 API 密钥。你愿意让它无人值守运行的写入操作,就会无人值守地运行。对于你不愿意无人值守运行的写入操作,会附加 policy: { approval: 'required' },这会阻止调用,并将其转为可由他人稍后处理的审批请求——即使处理者不是你,甚至生成该请求的对话早已结束。
受限不等于安全,本 README 不会假装两者相同。 生成的工具界面提供的是有限且清晰可见的操作范围,并不意味着其中绝无有害操作。你声明了哪些操作动词,Agent 就能调用其中哪些;所以你声明的破坏性操作,Agent 也能调用。
是否值得安装,取决于一个前提:orangerail 只管控它自己的工具。 如果 Agent 还能使用带凭据的 shell,或另一个数据库 MCP 服务,它就能绕过这道防线——请参阅 orangerail 不管控的内容。
预发布且可安装:npm 上的 0.1.5——orangerail(CLI),以及 orangerail-core、orangerail-mcp、orangerail-docs-gen 和 orangerail-studio。API 会在 1.0 之前变更,状态中有一条重要的升级说明。
将整个领域视为一张地图
只需一条命令,init 生成的界面就是一张可供阅读的地图。
$ orangerail studio
orangerail studio: scanning ontology — 9 object(s), 27 action(s)
orangerail studio: building the interactive map…
orangerail studio: serving on http://127.0.0.1:4820 — open it in your browser
每个对象、它们之间的关系,以及 Agent 能触达的每个写入操作。将鼠标悬停在表上,即可高亮显示其关系和操作;点击表即可查看管理它的策略。

更清晰的版本:
assets/studio-map.mp4——同一次运行的完整分辨率版本。在示例电商领域中进行的一次真实运行,使用--gate delete。这些锁并非为视频添加的注释:它们是工作室根据本体绘制的,因此九个操作带有锁,十八个没有。
准确理解它的价值。 这些关系来自 ontology/_links.mjs,init 会根据你的 Prisma 关系生成它,因此 Customer_list 的描述显示为 List Customer records. Relations: has many Order.。Agent 会被告知一个 Customer 有多个 Order。但它仍无法沿着这条边进行查询:没有遍历工具、没有联接、没有聚合,而且 Customer_list 会拒绝涉及 Order 的筛选条件。了解领域的结构和能够跨结构进行查询是两回事,而这里具备的只有前者。
快速入门
七个步骤,所有输出均逐字来自一次录制的运行。每一步背后的原因,以及它能避免的故障,详见快速入门注释;该页面首先列出了前置要求和 Prisma 7 的注意事项。
1. 将 orangerail 安装到即将扫描的项目中。
npm i -D orangerail
2. 扫描你的项目,所在仓库中须有 prisma/schema.prisma。
$ npx orangerail init --yes --preset approval-for-writes --no-studio
✓ scanned your sources — 2 object(s), 6 action(s)
✓ generated a governed MCP server under ontology/
✓ --gate delete: 2 of 6 write action(s) gated behind human approval — the other 4 run when the agent calls them
✓ recorded that posture in orangerail.governance.json — commit it
✓ approvals queue + audit chain at .orangerail/store/ — inside this project, so an
agent with file tools over this directory can write them
These files are yours — re-scans never modify them; `orangerail sync` reports drift.
Change what is gated by editing `policy` in ontology/<action>.mjs, or re-run init
with `--gate all` (gate every write) or `--gate none` (gate nothing).
orangerail.governance.json holds the posture init just generated, which nobody has reviewed yet.
From now on `orangerail sync` fails when an action gets weaker than that file, and
`orangerail mcp` refuses to serve it. Read the file, then run
`orangerail sync --accept-governance` to vouch for it as reviewed.
That store is the record of which writes a human approved, and appending one line to
.orangerail/store/approvals.jsonl marks a staged action approved — the next
`check_approval` then executes it, because the gate reads that store and never the
audit chain. `orangerail audit verify` reports the forgery afterwards; it is a report,
not a gate, and it does not prevent the write. The generated config carries the
one-line move at the `createFileStore` call — see docs/audit-log.md.
orangerail docs: wrote /private/tmp/shop/.orangerail/generated/AGENTS.md
Done. Run `orangerail studio` to explore the map, or `orangerail mcp`.
3. 安装生成的代码所加载的运行时。
npm install orangerail-core zod
4. 为生成的操作提供可访问的数据库。
npm install @prisma/client@6
export DATABASE_URL="file:./dev.db"
npx prisma generate
npx prisma db push --skip-generate
已有数据库?不要在其上运行
db push—— 针对现有数据库采用 orangerail。
5. 将 Agent 宿主指向它。 将以下内容放在项目根目录中,文件名为 .mcp.json:
{
"mcpServers": {
"orangerail": {
"type": "stdio",
"command": "./node_modules/.bin/orangerail",
"args": ["mcp"],
"env": { "DATABASE_URL": "file:./dev.db" }
}
}
}
其他宿主、claude mcp add 单行命令,以及从源码运行的方法:
将其接入 Agent 宿主。
6. 记录治理基线,并将其提交。 ontology/ 可由你编辑,因此只需一次不慎删除,就能用一行代码解除整个流程的限制,而重新扫描也不会察觉。系统会将当前状态与已记录的文件进行比较。
npx orangerail sync --accept-governance
提交 orangerail.governance.json。 它的全部价值在于:如果某个拉取请求移除了审批关卡,审查者就能在 CI 运行之前,从该请求自己的差异中看到 "approval": "required" 变成了 null。
7. 现在离开即可。 你离开期间,Agent 会处理队列:你未设置关卡的写入操作会继续执行,而它收到的删除请求则会停止。回来后:
$ npx orangerail status
orangerail status
objects: 2
actions: 2 approval-gated, 4 auto
baseline: 6 action(s) match orangerail.governance.json
preset: approval-for-writes
pending: 1 approval(s) awaiting a decision
store: /private/tmp/shop/.orangerail/store
Inside the project root, so an agent with file tools over this directory can
write it: one appended line in approvals.jsonl is a decision no human made,
and the next `check_approval` executes the staged action — the gate reads
this store, never the audit chain. `orangerail audit verify` reports the
forgery afterwards; it is a report, not a gate. Pointing the store `dir` at a
directory this agent's process cannot write is what removes the reach — see
docs/audit-log.md.
server: not detected — no orangerail mcp is running against this store
hosts: .mcp.json declares orangerail and nothing else.
Project scope only (.mcp.json, .cursor/mcp.json, .vscode/mcp.json); user- and
machine-scope MCP config is not read.
audit: chain OK — 5 record(s) verified
$ npx orangerail approvals list
c4818df5-770c-446f-883a-e9c0f7e615a2 "deleteCustomer" by "local-dev" [dev] 0s ago input={"id":2}
1 pending approval(s).
$ npx orangerail approvals approve c4818df5-770c-446f-883a-e9c0f7e615a2
approve ok (approved)
Agent 的下一次 check_approval 才是该行可以发生更改的第一个时刻。你未作出许可之前,什么都不会执行,而且每个步骤都记录在哈希链上。每次回归测试都会针对本仓库自己的构建运行
tests/e2e/ONT-093-quickstart-runs-as-documented.sh,验证上述完整流程。
为什么提示词不是正确的控制方式
阻止你离开的,并不是 Agent 做得太多,而是你唯一的控制手段是它必须向你提出的问题。 下午问到第二十次时,得到的还是和第一次相同的点击;而终止询问的开关就随软件一同提供——按照 Claude Code 自己的权限参考,bypassPermissions 模式会“跳过权限提示,但明确 ask 规则强制要求的提示除外”。如果每次调用都要由人重新设定边界,那么一旦没人守着,边界就无法维持。
提示词在形式上也不对。它询问的是某个工具——是否可以运行 Bash——而你承担的风险关乎你的业务领域:编辑库存没问题,删除订单不行;低于 $50 的退款不需要任何人审批。这种区别并不存在于工具层面,而存在于你的架构中。
本项目适用的运行场景

运行一次 examples/unattended-queue——使用真实 MCP 客户端,无需 API 密钥,并对每一行进行断言。视频展示了 12 条中的 6 条;行号有所跳跃,方便你看出位置。
一个包含 15 项任务的后台队列,涉及一个电商数据库。队列交给一个 Agent 处理,操作员当天离开,并明确要求不要请求确认。其中 12 项是普通的可撤销写入,3 项具有破坏性:删除一个已取消的订单、删除一个处于数据擦除请求下的客户,以及删除一个已停产的商品。完成后根据数据库评分,而不是根据 Agent 自称完成了什么:
| orangerail | |
|---|---|
| 无人值守完成的普通任务 | 12 / 12 |
| 执行的破坏性任务 | 0 |
| 被拦下并暂存的破坏性任务 | 3 |
| 次日早上等待处理的内容 | 3 条审批记录,每条都通过哈希绑定到对应的具体调用 |
| 审计链 | 27 条记录,验证通过 |
这就是本项目围绕的指标,而它并不是“我们拦下了多少”。而是在无人看守时完成了多少,以及你回来时还有什么在等待处理。
这张表里有两个可以分开的主张,而且它们的证据强度并不相同。12 项任务能够通过、3 项任务无法通过,这是服务端的特性,你可以在自己的机器上复现——examples/unattended-queue 会通过真实 MCP 客户端,确定性地运行整个队列,并对每一行进行断言。而当队列以文字形式提供时,模型会选择这些调用,则需要一个实时 Agent 驱动真实主机,这一部分是测量结果,而非可复现结果:样本量较小,只足以说明在测试过的场景中这道关卡有效,不足以得出发生率。
与你会采用的方案对比
真正有意义的对比对象不是原始 SQL 服务,而是一份规则文件:写得很好的 CLAUDE.md
列明允许和禁止访问的表,再配上拥有完整写入权限的 Postgres MCP 服务。同一队列、同一模型、三次克隆。
| markdown 规则,完整写入权限 | orangerail | |
|---|---|---|
| 已完成的普通项 | 12 / 12,三次运行均如此 | 12 / 12 |
| 已执行的破坏性操作 | 0 | 0 |
| 停止后留下的内容 | 报告中的一段文字 | 一条审批记录 |
| 在另一个目录启动相同任务 | 行已删除 | 已暂存 |
合规性方面打成了平手,而且一直如此——无论面对对抗性改写、伪造的先前审批、植入数据库行中的指令,还是小得多的模型,结果都是如此。有一项指标上它胜过了我们。因此,本项目并不主张你的 Agent 会无视规则:在这里测量的每次运行中,它都遵守了规则。
一共进行了六次运行。 这足以否定“它不会遵守规则”这一说法,但远不足以得出比率——六次中零次失败,只能将尾部风险限定在约 39%,而非接近于零。要获得可靠的界限,成本很高,而且下一次模型发布就会让它失效;因此,本项目押注的是下面这一行: 十次运行无法证明什么。
唯一没有打成平手的是最后一项,而且这与 Agent 的行为无关:授权随其注册的会话流转,而规则文件随其所绑定的机器账户流转。对于单台机器上的单个开发者,全局 ~/.claude/CLAUDE.md 可以弥补大部分差距——如果你正是这种情况,可能不需要它。 但遇到 CI 运行器、容器、服务账户或队友的检出目录时,这种弥补就到头了;这些环境无论如何都会拿到数据库凭据。
每次运行、规则文件胜出的指标,以及这项测量的局限:
与您会采用的方案对比。
examples/vs-a-rules-file 会执行两种方案。
Agent 获得的不是 execute_sql,而是这些工具
在包含三个模型(Order、OrderItem、Payment)的 Prisma schema 上运行 orangerail init 后,整个工具列表共有 16 项:每个对象各有一个 get 和一个 list,每种写入操作各有一个工具,另有 check_approval。 没有其他工具,也没有任何工具接受查询。
每个操作的输入都是根据你自己的列生成的 zod schema,并发布在 tools/list 中,因此 updateProduct 会拒绝将字符串用于整数列,并指出对应字段。每次读取都是按 id 执行的 findUnique,或分页的 findMany;其 filter 是针对已声明字段的一组封闭谓词——由服务在请求到达 resolver 之前强制执行,而不只是公布出来。
固定的接口范围很窄:没有聚合、没有联接、没有自由格式查询,也没有 DDL。无法通过它表达的问题必须在其他地方解答——orangerail 不负责管控的内容列出了全部内容,以及实际实施管控的位置。
看看它如何拦住 Agent

通过真实 MCP 客户端运行 examples/governed-writes 的一次真实记录。破坏性工具会保持可用,而不是被隐藏;Agent 无法强行执行该操作,并且只有在人工作出决定后行才会发生变化——这发生在另一个终端、另一个时间点,这正是能够离开的关键。
声明生成器无法推导的规则
以上内容都是生成的。当某条规则只存在于你的脑海中,而不在架构中——“绝不为售罄商品发放优惠券”——你只需用 TypeScript 写一次,它就会加入同一界面:
import { defineAction, defineObject } from 'orangerail-core';
import { z } from 'zod';
// Your existing backend. orangerail never replaces it — it only gates the call.
declare const findProduct: (id: string) => Promise<{ id: string; status: string } | null>;
declare const grantCoupon: (args: { productId: string; amount: number }) => Promise<void>;
// A `where` guard has to read the row it guards, so the target needs `resolve`.
export const Product = defineObject({
name: 'Product',
schema: z.object({ id: z.string(), status: z.string() }),
resolve: { get: async ({ id }) => findProduct(id) },
});
export const issueCoupon = defineAction({
name: 'issueCoupon',
target: Product,
input: z.object({ productId: z.string(), amount: z.number() }),
policy: {
approval: 'required',
where: { field: 'status', op: 'neq', value: 'soldout' },
},
// `execute` runs only after the approval clears, and receives the validated
// input plus the resolved caller. There is no `audit` switch: every staged,
// approved, rejected and executed action is written to the hash chain.
execute: async ({ input, identity }) => {
await grantCoupon({ productId: input.productId, amount: input.amount });
return { issuedBy: identity.subject };
},
});
这段代码不是示例——它是
packages/cli/test/readme-example.ts 的原样输出,
由仓库的类型检查编译,并在每次运行时与此文件比对,因此不会逐渐变成从未编译通过的内容。
状态
如今,orangerail init 无需检出此仓库即可在你自己的项目中运行,而在 1.0 之前,API 还会有所变动。全部五个包都通过 npm 的 Trusted Publishing 从
.github/workflows/release.yml 发布,因此每个包都带有来源证明,注明构建它的工作流和提交;此仓库中没有 npm 令牌。
如果你使用的是 0.1.0,请升级。 该版本向 Agent 发布了一个读取用的 filter,但从未对其进行检查,因此 <Object>_list 调用可以读取服务从未公开过的对象类型
(实现机制)。修复已包含在 0.1.2 中,并位于 orangerail-mcp,因此升级软件包即可应用修复,无需重新运行 init。0.1.2 还缩小了 filter 接受的范围,并改变了待审批项在升级过程中的处理方式——这两项内容都列在 CHANGELOG 的 从 0.1.0 升级 一节中。
文档
- 命令 — 介绍每条命令、
--gate的选择方式,以及如何将操作范围缩小到你指定的表。 - 快速入门注解 — 七个步骤及其背后的理由、要求,以及每个步骤可避免的故障。
- 将其接入你的 Agent 宿主 — stdio、
.mcp.json,以及如何从源代码运行。 - orangerail 不会管控什么 — 前置条件、缺失的能力,以及实际执行管控的位置。
- 审计日志能证明什么 — 具体标准、为何不使用“防篡改”一词、何时数据库级审计是更好的工具,以及应将存储放在哪里。
- 与可替代方案比较 — 完整的规则文件对比。
- orangerail 对比情况 — 与
--read-only、OpenAPI 代码生成、Prisma 自带的服务和 Supabase 服务进行比较。 - 针对现有数据库采用 orangerail —
对运行中的数据库执行
prisma db pull,以及 Prisma 7 带来的变化。 - 也让宿主提示用户 — 对未设门控的写入,可选的客户端提示。
- 故障排除 — 用于报告安装问题而非策略问题的输出信息。
- 我们的测量结果,以及失败的尝试 — 本项目提出或曾考虑提出的每项主张,以及它们经实际运行后是否成立。其中四项未能成立。
bench/— 该页面背后的测试样例,你可以通过复现来提出不同看法,而不是只争论。- MCP 注册表条目 — 介绍该条目是什么,以及百字描述无法容纳的步骤。
示例
unattended-queue— 将本文开头的运行过程变为可复现示例。确定性、经过断言、无需 API 密钥。governed-writes— 单独展示同一门控,一次执行一个破坏性调用。vs-a-rules-file— 将规则文件对比变为可运行示例,执行两种方案,包括规则文件胜出的那一列。
开发
此仓库采用确定性的 9 阶段门禁流程构建。每次变更都会通过
./scripts/verify.sh 运行检查——语言、结构、门禁自测、无 LLM、
模板,然后是类型检查 / lint / 测试 / 构建——而 CI 只运行此脚本,因此本地运行通过即表示构建通过。一项硬性约束:绝不捆绑 LLM 推理 SDK
(./scripts/check-no-llm.sh)。
许可证
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。