mcpField
C
Clay Seal

Clay Seal

本地服务
github.com/clayseal安全17 周前更新

关于 Clay Seal

Clay Seal 是面向需要约束 AI 工具使用的团队的策略网关,会在整个会话范围内评估授权、累计预算、参数来源和既往操作,再允许、暂缓或拒绝调用。

主要功能

  1. 01会话级策略检查
  2. 02调用与金额累计预算
  3. 03允许、升级审批或拒绝
  4. 04按策略过滤工具目录
  5. 05Python 包装器与 MCP 代理

适用场景

  1. 01限制多次退款的累计金额
  2. 02要求审批名单外的邮件
  3. 03限制 MCP 服务公开的工具

MCP 相关

Clay Seal 可作为 MCP 网关,置于客户端与现有服务之间。它按会话策略检查工具调用,并从目录中移除未授权工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"clayseal-clayseal": {
"command": "uvx",
"args": [
"clayseal"
]
}
}
}
启动命令uvx clayseal

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypiclayseal0.6.2

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

Clay Seal

Clay Seal 标志

许可证:MIT Python 测试 PyPI

将业务流程逻辑约束纳入 AI 系统。

你的 Agent 退款上限为 $1,000,却发出 11 笔 $900 的退款。每笔退款都没有超过单笔上限,因此每次调用检查都能通过,最终退款总额达到 $9,900。任何一次只检查单笔调用的机制都无法发现这一点。

Clay Seal 位于你的工具前方,会结合整个会话来判断每次调用:授权、累计总额、参数来源,以及 Agent 已经执行的操作。

目录

从这里开始安装并观察它阻止两种攻击
两行即可使用封装你已有的工具
部署在 MCP 服务前无需更改代码
编写策略操作员签署的授权
工作原理按执行顺序排列的六层机制
衡量什么指标及其不代表的含义
边界在哪里它不会做什么
文档先看 docs/START.md,然后看 docs/
从源代码构建并运行测试

正在开发代码? docs/INTEGRATION.md 列出了已发布的网关实际执行的模块,这是区分实时执行路径与未接入部分的最快方式。决策逻辑位于 clayseal/capabilities/broker.py 中的 SessionBroker._authorize_locked;大多数拒绝由同一文件中的 _floor 触发。

从这里开始

如果你是编码 Agent,跳转到部署操作手册。

pip install clayseal
clayseal try

clayseal try 大约需要一分钟。它会在你面前运行两种攻击,并展示网关如何阻止它们。无需配置、无需获取密钥,也不会有任何内容离开你的设备。

clayseal try:针对 $1,000 上限发起 11 笔 $900 退款,第一笔获准,其余被拒绝;随后,注入的电子邮件收件人被暂缓处理,等待人工审核

图中的每个判定都由与你实际部署时相同的网关实时作出。图片由 python scripts/render_try_svg.py 根据真实运行生成;如果图片与命令输出不一致,测试就会失败,因此它不会变成一张展示过时功能的截图。

支持 Python 3.10 至 3.14。依赖项有两个:cryptography 和 pyyaml。

关于名称。 目录名为 agentauth-capabilities,包名为 clayseal,导入路径为 clayseal.capabilities。该项目在 0.6 版本时更名; agentauth 仍可通过弃用兼容层导入。详情请参阅文末的 命名。

如果你是 Agent

按顺序在你正在编辑的项目中执行:

clayseal try --fast
clayseal howto
clayseal policy new > policy.yaml
# rename your_* tools; put mailboxes in egress.recipients; then:
clayseal policy lint policy.yaml
clayseal skill --write

Python 函数:Guardrail.from_policy_file("policy.yaml").wrap_all({...}) 并绑定包装器。键必须与 tools.allow 匹配。每个会话构造一个 Guardrail(新实例会重置限额)。捕获 Refused (预算不足 / 未授权)和 StepUpRequired(已暂缓 — 通常用于非允许列表中的 电子邮件)。非文件工具放在 paths.pathless 中。

如果已经是 MCP 服务(Claude Desktop / Cursor):使用 clayseal proxy,而不是 clayseal serve。不要让 proxy 指向普通的 .py 文件。clayseal howto 是操作手册。

两行即可上手

包装你已有的工具。Agent 的其他部分无需更改。

from clayseal.capabilities import Guardrail, Refused, StepUpRequired

def list_open_refunds():
    return [{"invoice": "INV-001", "amount": 900.0},
            {"invoice": "INV-002", "amount": 900.0}]

def issue_refund(invoice, amount):
    return f"refunded {invoice} ${amount:.2f}"

guard = Guardrail.from_dict({
    "version": 1,
    "goal": {"id": "refund-run",
             "summary": "Refund the invoices the customer disputed."},
    "expires_at": "2030-01-01T00:00:00Z",
    "tools": {"allow": ["list_open_refunds", "issue_refund"],
              "harmless": ["list_open_refunds"],
              "effects": {"list_open_refunds": "read", "issue_refund": "transfer"}},
    "paths": {"pathless": ["list_open_refunds", "issue_refund"]},
    "budgets": {"value": {"ceilings": {"refunds": "1000.00"},
                          "tracked": {"issue_refund": {"arg": "amount",
                                                       "budget": "refunds"}}}},
})
tools = guard.wrap_all({"list_open_refunds": list_open_refunds,
                        "issue_refund": issue_refund})

# Call them exactly as before. The gateway decides before the tool runs.
for row in tools["list_open_refunds"]():
    try:
        print(tools["issue_refund"](invoice=row["invoice"], amount=row["amount"]))
    except Refused as exc:
        print("refused:", exc.reasons)      # hand the reason back to the agent
    except StepUpRequired as exc:
        print("needs a human:", exc.reasons)

第一次退款会通过。第二次会被拒绝,因为 $1,000 的 会话上限已经用完。两次调用都没有到达你的函数。

这里直接内联了策略,因此你可以将全部内容粘贴到文件中并运行。 在实际部署中,策略单独存放在 YAML 文件中,安全团队可以审核, 并且可以通过拉取请求进行把关。clayseal policy new > policy.yaml 会生成一份带注释的模板供你开始使用,Guardrail.from_policy_file 会加载它。

包装器会保留原函数的名称、文档字符串和签名,因此任何会检查函数信息的 框架看到的工具都与之前相同。这适用于 LangGraph、OpenAI Agents SDK、CrewAI 和手写循环。像调用原函数那样调用 包装器,可以使用位置参数或按名称传参。

或将其置于 MCP 服务前面

完全无需更改代码。网关使用 MCP 协议,因此它位于 Agent 与 服务之间:

clayseal proxy --policy policy.yaml -- npx @your-org/mcp-server

也可以将此内容粘贴到 Claude Desktop 的 MCP 配置、Cursor 的 MCP 设置,或 .cursor/mcp.json 中。Agent 与 Clay Seal 通信;Clay Seal 与服务通信:

{
  "mcpServers": {
    "billing": {
      "command": "clayseal",
      "args": ["proxy", "--policy", "policy.yaml",
               "--", "npx", "@your-org/mcp-server"]
    }
  }
}

策略未授权的工具会从目录中移除,因此 Agent 根本不会知道它们的存在。

编写策略

这是一份完整策略,通过 lint 检查。

version: 1

goal:
  id: refund-run
  summary: Refund the invoices the customer disputed.

expires_at: 2027-12-31T00:00:00Z

tools:
  allow:    [list_open_refunds, issue_refund]
  harmless: [list_open_refunds]                 # a read spends nothing
  effects:  {list_open_refunds: read, issue_refund: transfer}

paths:
  pathless: [list_open_refunds, issue_refund]   # these act on invoices, not files

egress:
  domains: [acme.example]
  recipients: [[email protected]]               # a domain alone is every mailbox
  bind_recipients: true

budgets:
  value:
    ceilings: {refunds: "1000.00"}              # dollars, for the whole session
    tracked:
      issue_refund: {arg: amount, budget: refunds}

发布前运行 clayseal policy lint policy.yaml。它会发现最关键的错误: 能够花钱的工具却没有扣减预算。对于上面的文件,它会报告没有错误和一条警告 (除非绑定主体,否则上限按会话计算),这指出了一个你还没有做出的真实决策。

完整参考:docs/POLICY.md。

查看它如何阻止攻击

在代码仓库检出目录中运行。examples/ 中有七个示例,每个都无需密钥和 网络即可运行:

python examples/02_the_proxy.py
tools advertised to the agent: list_open_refunds, issue_refund
  (the server offers wire_funds; the policy does not grant it, so the agent is never told it exists)

  ok   issue_refund       INV-001   paid INV-001 $900.00
  DENY issue_refund       INV-002   refused by policy: value_budget_exceeded
  ...
  DENY issue_refund       INV-011   refused by policy: value_budget_exceeded
  DENY wire_funds         ops-float refused by policy: tool 'wire_funds' is not in this session's policy

the server executed 2 call(s):
  list_open_refunds {}
  issue_refund {"amount": 900.0, "invoice": "INV-001"}

clayseal proxy: 2 allowed, 11 denied, 0 held for approval; withheld from the catalog: wire_funds

阅读最后一段输出。JSON-RPC 错误证明 Agent 收到了 拒绝;服务自身的账本证明退款并未发生,这是 两种不同的说法。相同的运行也可以在命令行上执行,这正是实际部署时 采用的方式:

clayseal proxy --policy examples/refund.yaml -- python examples/refund_server.py

工作原理

你向网关提供一个策略文件和本次会话的目标。网关会在会话开始时封存目标,因此,Agent 之后读取的任何内容都无法扩大已批准的范围。每次工具调用执行前,都会经过同一个决策点。

flowchart LR
    P["policy.yaml<br/>reviewed and diffed<br/>like any other file"]
    A["your agent"]
    G{{"Clay Seal"}}
    T["your tools<br/>or MCP server"]
    H["a person"]
    X["never runs"]
    S[("session state:<br/>running totals,<br/>where arguments<br/>came from")]

    A -- "tool call" --> G
    P -. "sealed at session start" .-> G
    G -- "allow" --> T
    G -- "step up" --> H
    G -- "deny" --> X
    H -- "yes, once, for<br/>these arguments" --> T
    T -- "result" --> S
    S -. "the next call is judged<br/>against all of this" .-> G

底部的循环就是核心思路。网关不只是检查一次调用,还会根据会话至今的全部操作来检查调用。

调用会得到以下三种结果之一:

  • 允许,调用会转交给工具
  • 升级审批,调用会等待人工批准
  • 拒绝,调用不会执行

设置升级审批,是因为错误拒绝的代价很高。对于自主攻击者,等待批准的调用和被拒绝的调用一样能有效阻止攻击;而合法的 Agent 不会因此被永久阻断。只有存在问题的确凿证据时才会拒绝。

产生这些结果的检查会按固定顺序运行,从成本最低、限制最严格的检查开始,因此,较早被拒绝的调用不会进入成本更高的检查层。

  1. 底线规则。 一组简单规则:授权是否过期、是否允许使用此工具、此路径是否在范围内、此目的地是否在出站列表中、预算是否还有余额。这是最快的一层,也是大多数拒绝的来源。
  2. 声明检查。 如果 Agent 预先说明了计划,就会先将计划与封存的目标进行核对,再执行任何操作。
  3. 内容检查。 检查已声明的写入是否符合目标,并检查会产生影响的写入所携带的内容。会触发升级审批,但绝不直接拒绝。
  4. 会话状态。 检查累计总量、哪些值来自不可信文本,以及针对这些值设定的规则。
  5. 来源检查。 检查目的地的来源。封存目标中指定的收款方是可信的;之后 Agent 读取的文本中出现的收款方则不可信。
  6. 行为检查。 根据目标监测会话的行为模式。默认仅提供建议:它会升级处理,但不会阻止操作。它的拦截层级在算术上也不可能触发,除非已观察到足够多的正常会话来完成校准,因为 conformal p 值不能低于 1/(n+1),而它们所设的 alpha 阈值低于此下限。检测器会标明未启用的层级,因此,不会让无法触发的层级看起来像是经过检查但未发现问题。

下文中的预算是指网关在整个会话中累计的总量:可以是金额、调用次数,也可以是任何其他可计数的项目。这是唯一能够发现多个单独合法的调用累积后变得不合法的检查;下方的测量结果也表明,它决定了这种方法是否有效。

测量结果

场景和测试范围扫描脚本位于代码仓库中,而不在 wheel 包里,因此需要克隆仓库。大约需要 10 秒,且不会访问网络:

git clone https://github.com/clayseal/clayseal-capabilities.git
cd clayseal-capabilities
pip install -e .
python -m benchmarks.bpl_sweep --suite full

共 132 个业务流程场景,每个场景都有一个脚本化攻击及其良性对应场景。结果分为两列,因为单独优化任何一列都很容易:拒绝所有操作即可赢得攻击遏制率,允许所有操作即可赢得任务完成率。真正重要的是两者同时满足。

在完整测试集中,攻击被遏制,且对应的良性场景也完成,n=132:

条件遏制攻击且完成任务
未防护0/132,97.5% 上界为 2.8%
拒绝所有操作0/132,97.5% 上界为 2.8%
按策略进行逐次调用授权0.8% (1/132)
数据流污点分析11.4% (15/132)
Clay Seal39.4% (52/132)

相比数据流污点分析,提升了 28.0 个百分点 [18.2, 37.9],精确 McNemar p=1.2e-07。在 132 个良性对应场景中,此网关拒绝了 2 个,且都未造成工作损失;污点分析拒绝了 49 个,其中 43 个造成了工作损失。

标题之前要先看这个数字

39.4% 是两个不同情况的平均值,不是成功率。 决定你属于哪种情况的是规则;运行任何东西之前,先阅读自己的策略就能判断:

规则是否规定了可计数的限额?场景数Clay Seal数据流污点分析
是4283.3% [69.4%, 91.7%]2.4% [0.4%, 12.3%]
否9018.9% [12.1%, 28.2%]15.6% [9.5%, 24.4%]

Fisher 精确检验 p = 6.5e-11。

所以,请将规则写成对可计数事项设定上限。“每个会话的退款不超过 $1,000”可以强制执行。“不要做任何不妥的事”则不行:网关无法维护这类事项的累计值。clayseal policy lint 已会标记能花费资金却不扣减预算的工具,而这项测量说明了该警告的意义。

其余内容

上面的测试套件是我们编写的,这是首先应该怀疑的地方。 docs/EVIDENCE.md 全面列出了非我们所有的证据、网关的成本和局限:

了解防御机制的攻击者24 组语料库与目标,共 173,699 个候选项。其中 23 组里,完全查看判定结果对攻击者毫无帮助。第 24 组则说明自适应攻击奏效时的价值
十一组外部语料库,均非由本项目作者编写当危害由网关能够约束的事项定义时,成功率为 98% 到 100%;当危害由获授权操作的内容定义时,成功率为 31% 到 55%;另有摩擦成本列,说明策略不完整的代价
一个真实模型,AgentDojo 提示词注入216 次运行中有 1 次攻击成功,成功率为 0.5% [0.1, 2.6];Progent 公布的成功率为 11.1% 到 16.7%
相应成本,在四个模型上按任务配对测量最弱模型下降 25 个百分点,最强模型下降 3 个百分点;CaMeL 公布的成本为 7 个百分点
每次判定耗时 34 µs,不随会话长度变化以及比中位数更值得关注的三个数字
凌驾于其他所有指标之上的局限对于无法预先知道下一步操作的开放式任务,每项任务会中断 21.67 次

每项主要结果及其复现命令: benchmarks/results/README.md。

边界所在

有三种放置强制执行点的方式。它们在 Agent 必须如何绕过才能通过方面有所不同,与其将其归为一个数字,不如直截了当地说明这种差异。

tier中介对象绕过方式状态
in-process由你的 harness 通过 authorize() 路由的工具调用不调用它的 harness,或未封装的工具已发布
proxy按设计拦截传输上的每个工具调用能通过其他方式访问该工具的进程已发布
syscall来宾能够尝试的一切,且不可伪造此层不存在绕过方式参考后端仅支持 macOS

代理还会运行进程内路径无法执行的三项检查。它会检查调用中提及的每条路径,而不只是操作携带的那条路径。它会拒绝 JSON 中存在重复键的消息。它还会暂缓执行有副作用的调用,直到收到之前的结果,因此在获知目标来源之前,不会被要求判断其来源。 docs/POLICY.md 中的表格列出了这些差异,以及进程内集成必须自行完成的工作。

大多数部署都需要代理。Agent 连接到 clayseal proxy,而 clayseal proxy 会运行真正的 MCP 服务,因此被拒绝的调用会收到 JSON-RPC 错误,服务器子进程也不会收到该帧。策略之外的工具也会从公布的目录中移除,因此 Agent 不会围绕一个之后会被拒绝的工具制定计划。

syscall 层会将信封的出站流量范围和路径范围编译为沙箱策略,并返回一条不可伪造的判定流。参考后端由 iVisor 驱动。

它能捕获上层都无法捕获的一类行为。 DNS 隧道会将负载编码到子域名中,数据通过解析器流出。整个过程不涉及工具。没有工具调用,因此不会请求 authorize(),上层各层也都不会拒绝任何操作。在捕获的跟踪记录中,iVisor 拒绝了全部四个查询,而工具调用层一个也没有拒绝 (syscall_tier.md)。

反过来也一样。通过允许列表中的通道发送信标,iVisor 没有可拒绝的内容,因为每次 syscall 确实都获准执行;异常之处在于累计总量,只有会话预算能对此进行限制。两层互不取代,这正是同时运行两者的理由。

iVisor 从设计上仅支持 macOS:Hypervisor.framework 允许每个进程运行一个 VM,并会对调用方应用不可逆的 Seatbelt 配置文件。agentauth.sandbox_backends 入口点支持替换底层实现,而 Linux seccomp 或 Landlock 后端仍是待完成的工作,并非我们已发布的内容。

仍未弥合的缺口

范围内暂存。Agent 在授权范围内读取一个秘密级对象,并将其内容写入同一授权范围内的一个公开级对象。这两个操作都获准执行,而路径范围无法提供帮助,因为 Agent 获准使用的工作区确实同时包含这两个对象。以预言机攻击者衡量,泄露任务集合与 allow-all 完全相同 (in_scope_exfiltration.md)。机密性流跟踪器作为逐步升级的防护层覆盖了其中一部分;宽泛的片段拆分和无密钥编码仍未解决。对编码 Agent 的任何遏制能力声明都必须涵盖这一点。

策略文档

授权依据是一个文件,因此安全团队可以阅读它,在拉取请求中查看差异,并以此作为合并门禁。

version: 1
goal:
  id: billing-triage-2026-08
  summary: Triage the open billing tickets and email a summary to the ops archive.
profile: supervised          # autonomous | supervised | benchmark
expires_at: 2026-12-31T00:00:00Z

tools:
  allow:    [list_tickets, read_ticket, write_summary, send_email]
  harmless: [list_tickets, read_ticket, write_summary]
  effects:  {write_summary: write, send_email: send}

paths:
  allow: ["out/**", "tickets/**"]
  deny:  [".env", ".git/**"]
  arg_names: {write_summary: path}     # which argument carries the path
  pathless:  [send_email]              # asserted to act on no path

egress:
  domains: [acme-internal.com]
  bind_recipients: true

budgets:
  calls:
    ceilings: {emails: 3}
    tracked:  {send_email: emails}
clayseal policy show policy.yaml   # what it authorizes
clayseal policy lint policy.yaml   # what a reviewer should ask about

如果发现错误,lint 会以非零状态码退出,因此可用作合并前门禁。它会报告网关启动时会拒绝的内容,因此作者不必等到出现回溯信息才发现问题:

ERROR   untracked-effectful-tool issue_refund: reachable and in the 'value'
        family but debits no budget, so no ceiling applies to it at all
WARNING session-scoped-ceiling   emails: counted per session, so a second
        session gets a second ceiling

编译后的策略包含对文档计算出的摘要,网关会将其附加到每项决策上,因此审计轨迹记录的不只是决策内容,还会注明作出该决策的授权依据。

指向你自己的工具

两项声明即可完成这项工作,两者之所以存在,都是因为默认值针对的是基准目录,而非真实目录。

tools.effects 用于说明每个工具的作用。动词决定适用哪些底线规则;根据名称推测动词,对 send_email 这样的名称行得通,对 terraform_destroy 这样的名称则不行:从广泛使用的 MCP 服务中抽取的 24 个工具名称,有 17 个无法被分类器识别。

paths.arg_names 用于说明哪个参数携带路径。网关会查找 file_path、path、filename 和 file;如果某个工具将其称为 target_dir 或 key,就不会得到路径,而无法找到路径的路径范围不会生效。无法解析路径的有效操作调用会被拒绝,而不是未经检查就放行,因此故障会明确暴露,而非悄无声息。

clayseal policy lint 会列出缺少上述任一项声明的所有工具。为你未参与设计的目录编写第一份策略前,请先阅读 docs/POLICY.md。

如果负责签字确认的人从事风险管理而非工程工作, docs/CONTROLS.md 会说明这能为哪些义务提供证据:映射准确时引用框架原文,不准确时则停留在功能层面。文档开篇便说明了它不是什么:库不是控制制度,也不能让任何人因此符合任何要求。

根据已有内容编写第一份策略

有两个命令可用,因此第一份策略不必从空白文件开始。两者都会生成由人完成的草稿,绝不会授予权限。

# What the tools are called, from the server you already run.
clayseal policy init -- npx @your-org/mcp-server

# What the organisation permits, from the document that already says so.
clayseal policy draft delegation_of_authority.md

# Both halves in one file. This is the one worth running.
clayseal policy init --rules delegation_of_authority.md \
    -- npx @your-org/mcp-server > draft.yaml
clayseal policy lint draft.yaml

init 会向服务请求 tools/list,并从服务已发布的架构中读取工具名称、每个工具的作用,以及每个工具用于传递路径的参数。draft 会从文字中提取额度、时间窗口、仅一次规则、目录和允许的域名,例如“单笔供应商付款不得超过 $10,000”。

两者都不是权威依据,原因各不相同。文档是由没有考虑 Agent 的人编辑的产物,因此如果由它决定授权,控制文档的人就能控制授权。目录由受此策略约束的服务编写,因此,如果服务将 wire_funds 描述为“读取余额”,它就等于在自行设定限制。由此得出三项特性:

  • 每条规则都会注明其来源文档中的行号,审阅者无需重新阅读源文档,就能核对 YAML 与对应句子。
  • 看起来像规则的句子如果未能转换,就会成为 TODO 注释。看似完整的草稿不如坦承遗漏的草稿,因为转换过程中消失的规则无人会注意到。
  • 目录可以提高工具的影响等级,但绝不能降低;如果服务将自己的有影响操作工具称为只读,系统会报告这一点,而不会采信。未发布架构的工具不会被记录为不需要路径: “服务未说明”与“服务说明了不需要”是两回事。

刻意留空的是 budgets.tracked,也就是额度与工具的关联处。文档知道额度,目录知道哪个参数承载金额,但哪个工具会扣减哪个额度,两者都没有说明;关联错误会把共享额度拆成两个额度。系统会将两者都打印为注释形式的建议,供人连接。

在一份 30 行的授权委托文档和一个包含 7 个工具的 AP 服务上进行了端到端测试。系统提取了 9 条规则,并将 3 个句子保留为 TODO。审阅后,网关会拒绝四种操作:导致滚动 24 小时总额超过 $50,000 的付款、对已付款发票的再次付款、写入 .ledger 文件,以及写入 /finance/ap/ 目录之外的位置。其中一个 TODO 是“准备付款的人不得批准该付款”。这是一条本层无法表达的真实规则,它会以注释形式出现在输出中,而不是悄无声息地消失。

为你自己的工作负载添加规则

以上每个选项都在调整别人选择的行为。要添加“在我们这里,X 也禁止”这类规则,可直接在此处添加,无需分叉:

from clayseal.capabilities import GoalSpec
from clayseal.capabilities.deployable_stack import DeployableStack
from clayseal.capabilities.session_rules import SessionRuleHit

def no_competitor_domains(action, session, *, goal_summary, egress_verbs):
    if "competitor.test" in str(action.args or {}):
        return SessionRuleHit("house-rules", "destination is a competitor domain")
    return None      # None means "this rule has nothing to say"

goal = GoalSpec(query_id="refund-run", summary="Refund the disputed invoices.")
stack = DeployableStack.from_goal(goal, house_rules=(no_competitor_domains,))

命中规则时会要求升级处理,而非拒绝。针对你的工作负载编写的规则尚未根据它将拒绝的流量进行验证;要求升级处理也能同样有效地阻止自主攻击者,同时让人仍可选择批准。你的规则会在内置规则之后运行,因此自定义规则无法掩盖内置规则。引发异常的规则会被跳过,并计入 session_rules.RULE_FAILURES,决策仍会继续处理。因正则表达式出错而停止授权的网关,比漏掉一条规则的网关更糟。

低层 API

上面的 Guardrail 是大多数集成所需的封装。如果你正在构建自己的循环,并希望直接调用网关,决策 API 只有一个方法:

from clayseal.capabilities.monitor.action import Action
from clayseal.capabilities.policy import compile_policy
from clayseal.capabilities.tool_verbs import classify_verb

gateway = compile_policy({
    "version": 1,
    "goal": {"id": "billing",
             "summary": "Triage tickets and email [email protected]"},
    "expires_at": "2030-01-01T00:00:00Z",
    "tools": {"allow": ["read_ticket", "send_email"],
              "harmless": ["read_ticket"],
              "effects": {"read_ticket": "read", "send_email": "send"}},
    "paths": {"pathless": ["read_ticket", "send_email"]},
    "egress": {"domains": ["acme-internal.com"], "bind_recipients": True},
}).build()

agent_calls = [
    ("read_ticket", {"id": "T-1042"}),
    ("send_email", {"to": "[email protected]", "body": "3 open, 1 escalated"}),
    ("send_email", {"to": "collector-metrics.example", "body": "3 open, 1 escalated"}),
]

for step, (tool, args) in enumerate(agent_calls):
    decision = gateway.authorize(Action(
        step=step, tool=tool, resource=f"mcp:tool:{tool}",
        verb=classify_verb(tool), args=args,
    ))
    print(tool, decision.outcome, decision.reasons)
    if not decision.allowed:
        continue                    # hand the reasons back to the agent
    # run(tool, args)

(从代码检出目录运行的)examples/01_gateway.py 会针对植入在一张 Agent 获准读取的工单中的提示词注入攻击,端到端运行整个流程。第四次调用会获准,第五次调用会转交给人工处理。

**在此拦截攻击的是出口允许列表,这是一条针对单次调用的规则。**注入的地址不在允许的域名范围内,因此最基础的防护规则会将其拦截,不会查询来源层。这是网关按正确顺序工作的结果,并非本文声称具有独特性的能力演示,因此示例会打印触发的层级,而 examples/README.md 会移除域名列表,使用相同会话运行,此时由来源层作出判断。

如果你的策略指定了一个域名,而攻击者指定了该域名内的地址,基础防护规则就无法判断,此时由 egress.recipients 限定收件邮箱。此项默认未启用,clayseal policy lint 也不会要求配置,因此允许某个域名的策略会允许该域名下的所有邮箱。

安全态势

除非环境明确标记为开发环境,否则防护机制都会默认拒绝。

CLAYSEAL_ENV=development    # relaxes them, and says so once per process

未设置该环境,或将其设为 production,都意味着:拒绝未固定提交令牌签发密钥的操作、拒绝缺失重放存储的操作、拒绝未签名的升级审批,并拒绝来自未固定签名者的意图信封。过去采用的默认规则恰好相反,因此所有未读过本节的部署都会接受这四种情况。

请参阅 docs/THREAT_MODEL.md,了解哪些内容经过签名、由谁签名,以及哪些内容不在范围内。

从源码构建

git clone https://github.com/clayseal/clayseal-capabilities.git
cd clayseal-capabilities
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest python/tests -q
python examples/01_gateway.py

可选附加项:

pip install "clayseal[oidc]"     # live OIDC/JWKS verification
pip install "clayseal[spiffe]"   # SPIFFE Workload API
pip install "clayseal[redis]"    # shared replay and ledger stores
pip install "clayseal[monitor]"  # the learned trajectory scorer

身份

网关不需要身份层。使用你自己的 IdP 提供已验证的声明,构建 IdentitySession,并据此签发提交令牌。适配器支持 SPIFFE JWT-SVID、OIDC、Auth0、AWS STS、Entra Agent ID、Azure AD、GCP 和 A2A 签名 Agent 卡片。

from clayseal.capabilities.identity_adapters import get_identity_provider

session = get_identity_provider("oidc").build_session(
    verified_claims,          # your IdP already checked signature, aud, exp
    evidence_verified=True,
)

文档

docs/README.md 是索引。你最可能需要查看:

  • 前十分钟,了解如何从演示开始构建自己的 Agent
  • 如果你是负责部署此项目的编码 Agent,请参阅 Agent 说明
  • llms.txt,机器可读的索引
  • 证据,列出所有测量数值及其未超出的限制
  • API 参考,按大多数集成实际使用的情况分级列出导出名称
  • 开发者指南,介绍如何安装并接入
  • 策略参考,说明策略文件可以包含哪些内容
  • 威胁模型,说明它防御哪些威胁,以及不防御哪些威胁
  • 隐私与数据处理,说明它存储哪些内容,以及哪些内容会离开进程

如需报告漏洞,请参阅 SECURITY.md;如需贡献,请参阅 CONTRIBUTING.md 和 行为准则。从 agentauth-capabilities 升级: docs/MIGRATION.md。语料及其许可证: THIRD_PARTY.md。发布版本: docs/RELEASING.md。

命名

产品、发行包、导入根路径和 CLI 都名为 clayseal。

在 0.6 之前,该项目通过私有源向设计合作伙伴分发,名称为 agentauth-capabilities,导入路径为 agentauth.capabilities。这些导入 路径仍可解析,并会发出 DeprecationWarning;它们将在 0.7 中移除。别名模块 与实际模块是同一个对象,因此通过旧路径注册的插件也能通过新路径访问。迁移只需搜索并 替换:docs/MIGRATION.md。

存储键和线路标识符有意没有更名。重放存储仍使用 agentauth:commit: 作为提交令牌的键,因为如果网关悄然停止识别已使用过的令牌,就会重新打开它原本要关闭的重放窗口。

clayseal.core 过去是单独的私有发行包,包含共享契约和加密辅助工具,现在已移至此仓库。身份和回执层仍是独立发行包,这里不需要它们。

许可证

MIT。请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

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

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