关于 mcp-opa-authz
mcp-opa-authz 是用于通过本地 Rego 求值或 AuthZEN 1.0 PDP 检查授权的 MCP 服务,适合调试策略及核验 PDP 决策,并区分拒绝、未定义结果和请求失败。
主要功能
- 01本地求值 Rego 策略
- 02查看定义状态与返回值
- 03查询单项及批量 PDP 决策
- 04搜索获准的主体、资源和操作
- 05发现 PDP 端点
- 06在沙箱中运行策略
适用场景
- 01结合输入数据调试 Rego 策略
- 02向 PDP 核验授权决策
- 03列出获准的资源或主体
- 04检查 PDP 功能与端点
MCP 相关
向 MCP 客户端提供五类授权工具:本地 Rego 求值、AuthZEN 决策查询与搜索,以及 PDP 发现。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e AUTHZEN_PDP_URL -e AUTHZEN_PDP_TOKEN -e AUTHZEN_PDP_TIMEOUT -e MCP_OPA_EVAL_TIMEOUT -e MCP_OPA_ALLOW_NETWORK_BUILTINS ghcr.io/kanywst/mcp-opa-authz:0.6.2AUTHZEN_PDP_URL密钥Default AuthZEN Access Evaluation endpoint, e.g. https://pdp.example.com/access/v1/evaluation. Only evaluate_policy works without it.AUTHZEN_PDP_TOKEN密钥Authorization header value for the PDP. A value with no scheme is sent as "Bearer <token>".AUTHZEN_PDP_TIMEOUT密钥Per-request timeout for PDP calls, as a Go duration. Default 10s.MCP_OPA_EVAL_TIMEOUTWall-clock limit on a single Rego evaluation, as a Go duration. Default 5s.MCP_OPA_ALLOW_NETWORK_BUILTINSRe-enable http.send, net.lookup_ip_addr and opa.runtime inside evaluated policies. Off by default: policy source evaluated here comes from a model and runs inside the server process.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/kanywst/mcp-opa-authz:0.6.2stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
mcp-opa-authz
别再让 Agent 猜测授权结果。 这是一个 MCP 服务,可根据实际策略代码回答“是否允许?”——策略代码可以是你提供的 Rego,也可以是实际管理你系统的 OpenID AuthZEN 1.0 PDP。
询问模型 Alice 是否可以删除该文档,它会给出自信、看似合理且无法证伪的答案。使用这些工具,答案就会来自策略。
brew install kanywst/tap/mcp-opa-authz
# or, without Homebrew:
go install github.com/kanywst/mcp-opa-authz@latest
claude mcp add opa-authz -- mcp-opa-authz
这足以使用 evaluate_policy。将它指向 PDP,即可使用其余工具:
claude mcp add opa-authz \
--env AUTHZEN_PDP_URL=http://localhost:8181/access/v1/evaluation \
-- mcp-opa-authz
两层能力,同一个问题
| 工具 | 回答的问题 | 所需条件 |
|---|---|---|
evaluate_policy | “这段 Rego 表达了什么?”由 OPA 在进程内进行评估。 | 无外部依赖 |
authzen_evaluate | “管理这个系统的 PDP 会如何回答?” | 可访问的 PDP |
authzen_evaluate_batch | 同样的问题,对一组对象进行评估——主体可以访问其中哪些? | 可访问的 PDP |
authzen_search | “谁可以……”“哪些资源可以……”“可以做什么……”——由 PDP 列出获准的内容。 | 支持 Search API 的 PDP |
authzen_discover | “这个 PDP 提供哪些端点?” | 可访问的 PDP |
在编写或调试你拥有源代码的策略时,使用 evaluate_policy。当决策必须来自生产环境,而不是粘贴到聊天中的策略时,使用 authzen_evaluate。服务的 MCP 指令也会告诉模型这一点,因此模型通常会自行正确选择。
与 Shell 工具中的 opa eval 有何不同
拥有 Shell 访问权限的 Agent 已经可以运行 opa eval。但它无法得到一个可以信赖的答案:
- 未定义不等于 false。 Rego 查询没有匹配规则且没有默认值时,会返回
[]。所有模型都会将其理解为拒绝。evaluate_policy会同时返回defined和value,因此“策略拒绝了”和“策略没有给出意见”不再是同一个答案。 - PDP 未作答不等于拒绝。 AuthZEN 要求必须包含
decision成员。缺少该成员的响应会被解码为 Gobool类型的false——有问题的 PDP 看起来就像严格的 PDP。此服务会将缺少decision视为失败,而不会视为拒绝。401 也是如此:这表示此服务未能通过身份验证,而非主体被拒绝;错误信息会明确说明这一点。 - 策略在沙箱中运行。 提供给 MCP 服务的 Rego 由模型编写,内容可能来自网页。OPA 的
http.send会让该策略从你的笔记本电脑发出任意 HTTP 请求,而opa.runtime()会向它暴露你的环境。这两项功能都会在编译时移除。详见安全性。 - 批量决策会携带索引。
permit_on_first_permit返回的决策数少于提交数是正常行为。若直接将数组配对,就会把决策错误地关联到其他资源。 - 搜索结果为空不等于搜索失败。
results: []表示没有任何内容获准;响应缺少results成员,或结果实体类型错误,则表示发生错误。
演示
调试一项不该拒绝却正在拒绝请求的策略:
> Why is bob getting denied on doc-1? Here's the policy and the input.
evaluate_policy(rego=…, query="data.rbac.allow", input_json=…, trace=true)
{
"defined": true,
"value": false,
"printed": ["checking roles for", "bob"],
"trace": [
"Enter data.rbac.allow",
"| Eval some role in roles[input.user]",
"| Fail roles[\"bob\"]",
…
]
}
Bob has no entry in `roles` at all — the rule never reaches the permission
check. Adding "bob": {"viewer"} fixes it.
然后向实际运行的 PDP 确认:
> Does production agree?
authzen_evaluate(subject={"type":"user","id":"bob"}, …)
{ "decision": false, "context": { "reason": "no role binding" },
"request_id": "3f9c…", "pdp_url": "https://pdp.internal/access/v1/evaluation" }
工具
evaluate_policy
| 参数 | 必填 | 描述 |
|---|---|---|
rego | 是 | 带有 package 声明的 Rego 源码。 |
query | 是 | Rego 查询,例如 data.example.allow。 |
input_json | 否 | JSON 编码的 input 文档。 |
data_json | 否 | data 命名空间的 JSON 编码基础文档。 |
rego_version | 否 | v1(默认)或用于 OPA 1.0 之前语法的 v0。 |
trace | 否 | 返回格式化的求值跟踪。内容较多;上限为 4000 个事件、200 行,每行 1 KiB。 |
返回 defined、value、原始 OPA result_set(编码后超过 256 KiB 时省略,并返回 result_set_omitted)、任何 print() 输出(200 行,每行 1 KiB),以及在请求时返回的跟踪信息。
authzen_evaluate
| 参数 | 必填 | 描述 |
|---|---|---|
subject | 是 | JSON 对象。AuthZEN 要求包含 type 和 id。 |
action | 是 | JSON 对象。AuthZEN 要求包含 name。 |
resource | 是 | JSON 对象。AuthZEN 要求包含 type 和 id。 |
context | 否 | 包含运行时上下文的 JSON 对象(IP、时间、MFA 强度)。 |
pdp_url | 否 | 为本次调用覆盖 AUTHZEN_PDP_URL。 |
返回 decision、PDP 的 context(如果有)、作出响应的 pdp_url,以及作为 X-Request-ID 发送的 request_id——因此可以在 PDP 的日志中找到记录了该决策的对话。
authzen_evaluate_batch
参数相同,另外包含 evaluations(一个 JSON 数组,其中的条目会覆盖顶层默认值)和 evaluations_semantic(execute_all、deny_on_first_deny、permit_on_first_permit)。每次调用最多 100 个条目。未提供 pdp_url 时,端点为 PDP 在其元数据中公布的 access_evaluations_endpoint;如果未公布,则使用 AUTHZEN_PDP_URL 根路径下的 /access/v1/evaluations——其解析方式、缓存和同源规则与 authzen_search 相同。结果中的 endpoint_source 会说明使用了哪个端点。
{
"subject": "{\"type\":\"user\",\"id\":\"alice\"}",
"action": "{\"name\":\"read\"}",
"evaluations": "[{\"resource\":{\"type\":\"doc\",\"id\":\"1\"}},{\"resource\":{\"type\":\"doc\",\"id\":\"2\"}}]"
}
authzen_search
AuthZEN 搜索 API。search 用于选择要列出的实体;该实体只携带其 type。
search | 问题 | subject | action | resource |
|---|---|---|---|---|
subject | 谁可以对该资源执行此操作? | 仅 type | 必填 | type 和 id |
resource | 主体可以对哪些此类型的资源执行操作? | type 和 id | 必填 | 仅 type |
action | 主体可以对该资源执行什么操作? | type 和 id | 必须省略 | type 和 id |
context、pdp_url、page_limit 和 page_token 均为可选项。未提供 pdp_url 时,端点为 PDP 在其元数据中公布的端点(如 search_subject_endpoint 等),符合规范要求;如果没有可用的元数据,则回退到 AUTHZEN_PDP_URL 根路径下的默认路径(/access/v1/search/{subject,resource,action})。元数据按 PDP 缓存五分钟,结果中的 endpoint_source 会说明使用了哪个端点。公布的端点必须与 PDP 同源,因为 PDP 令牌会随请求发送;除非通过 pdp_url 显式传入,否则会拒绝跨源端点。返回 results、has_more 和 next_page_token;将该令牌作为 page_token 传回,并保持其他参数不变,即可获取下一页。如果响应的 page 对象没有 next_token,则会报错,尽管规范自身的示例中包含该字段:没有它就无法判断列表是否完整。
{
"search": "resource",
"subject": "{\"type\":\"user\",\"id\":\"alice\"}",
"action": "{\"name\":\"read\"}",
"resource": "{\"type\":\"document\"}"
}
authzen_discover
从 PDP 根路径获取 /.well-known/authzen-configuration。pdp_url 可以是根路径,也可以是求值端点——已知的 AuthZEN 路径后缀会被剥除。对于挂载在前缀下的 PDP,well-known 字符串会放在主机与前缀之间,符合 RFC 8615 和规范中的位置要求(https://gw.example.com/.well-known/authzen-configuration/pdp);如果该路径返回 404,则会尝试较旧的附加形式(…/pdp/.well-known/authzen-configuration)。如果文档中的 policy_decision_point 不是获取该文档的根路径,则会拒绝该文档,符合规范要求。
标准符合性
实现了 Authorization API 1.0,该规范于 2026 年 1 月获批为 OpenID 最终规范:
| 章节 | 状态 |
|---|---|
访问评估(POST /access/v1/evaluation) | authzen_evaluate |
批量访问评估(POST /access/v1/evaluations) | authzen_evaluate_batch |
PDP 元数据(GET /.well-known/authzen-configuration) | authzen_discover |
| Subject / Action / Resource 信息模型 | 发送请求前验证必需成员 |
X-Request-ID 关联 | 每次调用都会发送,并在结果中返回 |
| 搜索 API(subject / resource / action,支持分页) | authzen_search |
值得了解的相关工作:AuthZEN 工作组的 COAZ 配置文件将 AuthZEN 与 MCP 工具调用本身绑定起来,因此网关可以使用工具 inputSchema 中声明的 x-authzen-mapping 来授权 tools/call。这是同一问题的执行侧——本服务负责检查,两者可以组合使用。
配置
| 变量 | 默认值 | 说明 |
|---|---|---|
AUTHZEN_PDP_URL | — | 默认访问评估端点。 |
AUTHZEN_PDP_TOKEN | — | Authorization 标头的值。不带方案的值会作为 Bearer <token> 发送。 |
AUTHZEN_PDP_TIMEOUT | 10s | 单次请求超时时间。 |
AUTHZEN_PDP_MAX_RESPONSE_BYTES | 1048576 | 响应读取上限。 |
MCP_OPA_EVAL_TIMEOUT | 5s | 单次 Rego 评估的总时限。 |
MCP_MAX_ARG_BYTES | 1048576 | 单个参数的大小上限。 |
MCP_OPA_ALLOW_NETWORK_BUILTINS | false | 重新启用网络内置函数。见下文。 |
值格式错误时,服务会在启动时停止,而不会静默替换为默认值——操作人员认为已生效的限制,就应该确实生效。
安全性
evaluate_policy 会在此进程内编译并运行模型生成的 Rego。因此,有三个 OPA 内置函数会从能力集中移除:
| 内置函数 | 原因 |
|---|---|
http.send | 可从服务运行所在的任何位置(笔记本电脑或 CI 运行器)发起任意 HTTP 请求,进入该位置所处的网络边界。这构成了完整的 SSRF 攻击面,也与该工具自身的描述相矛盾。 |
net.lookup_ip_addr | 即使没有可访问的端口,也足以一次一个 DNS 标签地外传 input。 |
opa.runtime | 会返回运行时配置,包括进程环境及其中的所有凭据。 |
时间、JWT、UUID 和随机数内置函数不受影响——这些函数会出现在实际的授权策略中。使用已移除内置函数的策略会编译失败,并显示指出该函数的消息;对于确实需要启用它的情况,消息还会提示 MCP_OPA_ALLOW_NETWORK_BUILTINS。
PDP 客户端也受到约束:pdp_url 必须是包含主机且不带用户信息的绝对 http(s) URL;客户端会拒绝重定向,而不会携带 Authorization 标头跟随重定向;响应会通过字节数上限读取;PDP 错误正文会在传入模型上下文前截断。
报告漏洞:参见 SECURITY.md。
运行
从源代码运行
make smoke
此流程会构建二进制文件,启动一个模拟 AuthZEN PDP,通过完整的五个工具运行一次真实的 MCP stdio 会话,并断言每个工具都已响应——包括 http.send 仍会被拒绝。无需 MCP 客户端,也无需真实 PDP。
容器
docker run -i --rm \
-e AUTHZEN_PDP_URL=https://pdp.example.com/access/v1/evaluation \
ghcr.io/kanywst/mcp-opa-authz
Cursor 和其他 MCP 客户端
{
"mcpServers": {
"opa-authz": {
"command": "mcp-opa-authz",
"env": { "AUTHZEN_PDP_URL": "http://localhost:8181/access/v1/evaluation" }
}
}
}
在 :8181 上运行的本地 opa-authzen-plugin 可用作 PDP。Topaz、Cerbos、带有实验性 AuthZEN 支持的 Keycloak,以及 互操作列表中的其他任何实现也都可以。
示例
examples/ 中包含供 evaluate_policy 使用的 Rego 策略参考:
rbac.rego— 角色到权限的映射abac.rego— 清除级别比较k8s_admission.rego— 准入控制:必需标签
目录结构
刻意采用扁平结构。单二进制文件的 MCP 服务不需要 cmd/、internal/ 或 pkg/。
main.go server bootstrap, CLI, tool registration
config.go environment, bounds, defaults
args.go tool argument decoding
tool_opa.go evaluate_policy
opa_capabilities.go the Rego sandbox
authzen.go AuthZEN 1.0 wire types and PDP client
tool_authzen.go authzen_evaluate, _batch, _discover
tool_authzen_search.go authzen_search
scripts/smoke.sh end-to-end MCP session
验证发布版本
发布包会附带由 cosign 签名的校验和文件(通过 GitHub OIDC 使用 Sigstore 无密钥签名),并为每个归档文件提供 CycloneDX SBOM。签名及其证书会一起包含在一个 Sigstore bundle 中,文件名为 *-checksums.txt.sigstore.json。
TAG=v0.3.0
gh release download "$TAG" -R kanywst/mcp-opa-authz -p '*-checksums.txt*'
cosign verify-blob \
--bundle "mcp-opa-authz-${TAG#v}-checksums.txt.sigstore.json" \
--certificate-identity-regexp 'https://github.com/kanywst/mcp-opa-authz/.github/workflows/release.yml@refs/tags/' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
"mcp-opa-authz-${TAG#v}-checksums.txt"
然后使用该文件校验你下载的归档文件:
sha256sum -c "mcp-opa-authz-${TAG#v}-checksums.txt" --ignore-missing
贡献
欢迎提交问题和拉取请求 — 请参阅 CONTRIBUTING.md。适合初次贡献的方向:添加另一种示例策略、尝试一个尚未测试过的 PDP,或补齐 AuthZEN 文本中的缺漏。
此仓库合并了原先的 0-draft/mcp-opa 和 0-draft/mcp-authzen。两者的历史记录均在此保留。
许可证
MIT。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。