跳到主要内容

UCP — 能力、扩展与命名治理

本章讲 UCP 的“名词与动词系统”:怎么命名、怎么把功能切成能力(capability)和扩展(extension),以及扩展如何在 schema 层面拼到父能力上。

1. 反向域名命名:把治理写进名字

所有能力/服务名都用反向域名格式:

{reverse-domain}.{service}.{capability}
例:dev.ucp.shopping.checkout

这一招很关键——它消灭了中央注册中心。谁拥有域名,谁就掌管自己的命名空间:

名字权威域名谁治理
dev.ucp.shopping.checkoutucp.devUCP 治理机构
dev.shopify.catalogshopify.devShopify
com.example.payments.installmentsexample.comexample.com 自己

dev.ucp.* 是保留给 UCP Tech Council 的;任何厂商都能在自己的域名下定义能力(org.acme.*),无需 UCP 批准。来源:docs/documentation/core-concepts.md:304-331docs/specification/overview.md:52-103

安全绑定(必记): 能力声明里的 specschema URL 必须来自命名权威域名——dev.ucp.* 的 spec 必须挂在 https://ucp.dev/...。平台 MUST 校验这层绑定,SHOULD 拒绝 spec 源与命名权威不符的能力,否则会被伪造能力攻击。来源:docs/specification/overview.md:80-92docs/documentation/core-concepts.md:319-323

2. 三件套:Capability / Extension / Service

别把这三个搞混——它们职责不同:

  • Capability(能力) = 协议的“动词”:可独立发现、协商、调用的功能单元,可独立版本化。如 checkout、cart、order。
  • Extension(扩展) = 可选增强:用 extends 声明父能力,靠 JSON Schema allOf 组合到父 schema 上。如 discount、fulfillment、ap2_mandate。
  • Service(服务) = 一个垂直域(如 dev.ucp.shopping)的操作集合,声明有什么功能;而怎么访问由传输绑定决定。

来源:docs/documentation/core-concepts.md:128-211

一个服务可挂多种传输绑定,平台按场景挑:

传输描述格式适合谁
RESTOpenAPI 3.1.0标准服务端对接
MCPOpenRPCAI agent(Model Context Protocol)
A2AAgent CardAgent-to-Agent 集成
EmbeddedOpenRPC嵌入式集成

来源:docs/documentation/core-concepts.md:197-204。注意传输绑定 MUST 是“薄”的——只声明方法名、只引用基础 schema,不得内联枚举字段。docs/specification/overview.md:118-121216-219

3. 扩展怎么拼到能力上(核心机制)

这是本章最该学的一点。一个扩展 schema 要在它的 $defs 里,为每个父能力建一个键,键名就是父能力全名,值用 allOf 把“父能力基础 schema”和“本扩展新增字段”拼起来。

真实实现:discount 扩展

看折扣扩展怎么挂到 checkout 和 cart 上(source/schemas/shopping/discount.json:124-145):

{
"name": "dev.ucp.shopping.discount",
"$defs": {
"discounts_object": { "...": "折扣码输入 + 已应用折扣输出" },
"dev.ucp.shopping.checkout": {
"title": "Checkout with Discount",
"allOf": [
{ "$ref": "checkout.json" },
{ "type": "object", "properties": { "discounts": { "$ref": "#/$defs/discounts_object" } } }
]
}
}
}

注意:$defs 里的键 "dev.ucp.shopping.checkout" 恰好等于它要扩展的父能力全名。这不是巧合,是确定性解析的约定:

  • 扩展 schema MUSTextends 里声明的每个父,都有一个同名 $defs 键。
  • 平台解析时:确定根能力(如 checkout 操作 → dev.ucp.shopping.checkout)→ 对每个生效扩展,取 <扩展>.json#/$defs/<根能力全名> → 用 allOf 链合成最终 schema。

来源:docs/specification/overview.md:226-267docs/specification/overview.md:320-349

直觉演示(示意,非源码)

# 示意,非源码:演示平台如何为一次 checkout 合成校验 schema
def compose_schema(root_cap: str, active_extensions: list[dict]) -> dict:
schemas = [load(f"{root_cap}.json")] # 先放基础能力 schema
for ext in active_extensions:
# 关键:用“根能力全名”当键去扩展 schema 的 $defs 里取那一支
branch = ext["$defs"].get(root_cap)
if branch: # 该扩展确实扩展了这个根能力
schemas.append(branch)
return {"allOf": schemas} # allOf 只能“叠加约束”,永远是收紧

重点看:合成永远是 allOf 叠加——只能加约束、不能放松。这条性质决定了下面的“单向门”陷阱。

4. 巧妙之处 / 坑

  • 巧:自描述 schema。 每个能力 schema 顶层带 name + version,平台拿到 schema 不用查别处就知道它是什么、哪个版本;构建期还能交叉校验“声明里的 schema URL 指向的 schema,其内嵌 name/version 是否对得上”。来源:docs/documentation/schema-authoring.md:38-55
  • 巧:requires 声明版本依赖。 扩展可声明它需要的协议/能力最低版本(min/可选 max),由 schema 作者而非 profile 发布者声明;不满足的扩展在解析时被排除并触发重新剪枝。来源:docs/specification/overview.md:268-318source/schemas/ucp.json:31-47
  • 坑(单向门):闭集枚举不可扩展。 因为扩展只能 allOf 叠加,而两个 enum/oneOfallOf取交集,永远加不进新值/新分支。所以规范强烈建议用开放字符串词表(文档化“well-known values”+ 容忍未知值)而非闭 enumoneOfadditionalProperties: false 同理是单向门。只有“天生封闭”的集合(如 status: open|completed|expired)才用 enum。来源:docs/documentation/schema-authoring.md:263-421

这个“开放词表 vs 闭枚举”的取舍贯穿整个 UCP,是它面向分布式演进的核心工程判断——值得带走。

5. 代码地图

主题文件符号 / 锚点
命名治理 / spec URL 绑定docs/specification/overview.md“Namespace Governance” §52-103
能力/扩展 schema 定义source/schemas/capability.json$defs.baseplatform_schema
扩展组合范例source/schemas/shopping/discount.json$defs["dev.ucp.shopping.checkout"]$defs["dev.ucp.shopping.cart"]
三件套概念docs/documentation/core-concepts.md“Capabilities/Extensions/Services” §128-212
版本依赖 requiressource/schemas/ucp.json$defs.requires$defs.version_constraint
开放词表 vs 枚举docs/documentation/schema-authoring.md“String Vocabularies vs Enums”、“Extensibility and Forward Compatibility”