How UCP Handles Extension Version Requirements and Schema Resolution
UCP treats extensions as optional, version‑aware modules that declare strict compatibility constraints through a requires object, then undergoes deterministic validation during negotiation to compose a final JSON‑Schema via allOf merging.
The Universal Commerce Protocol (UCP) defines a deterministic mechanism for handling extension version requirements and schema resolution that ensures only compatible modules participate in request/response validation. As implemented in Universal-Commerce-Protocol/ucp, this process relies on declared version constraints, intersection algorithms, and schema composition rules documented in the specification overview.
Declaring Version Requirements in Extension Schemas
Extension schemas declare their compatibility boundaries using a top‑level requires object placed alongside standard metadata fields like name, title, and description. This object specifies minimum (and optional maximum) version constraints for both the UCP protocol itself and any parent capabilities the extension augments.
The requires Object Structure
According to docs/specification/overview.md (lines 66‑84), the requires object contains two primary keys:
protocol– Defines the UCP protocol version the extension needs, specified as amindate string (e.g.,"2026-01-23"), with an optionalmax.capabilities– A map where each key is a fully‑qualified parent capability name and the value is a version constraint object containingminand optionallymaxdate strings.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://acme.com/ucp/schemas/loyalty.json",
"name": "com.acme.shopping.loyalty",
"title": "Acme Loyalty Points",
"requires": {
"protocol": { "min": "2026-01-23" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-06-01" }
}
},
"$defs": {
"dev.ucp.shopping.checkout": { }
}
}
These constraints are declared by the schema author, not the profile publisher. The publisher later advertises compatible versions in their business profile, but the extension's hard requirements are embedded in the schema definition itself.
Validating Extension Compatibility During Negotiation
During the capability negotiation phase, both the platform and the business must verify that selected versions satisfy all extension constraints before the extension enters the active set.
The validation process documented in docs/specification/overview.md (lines 105‑112) requires:
- Protocol version verification – Confirm the negotiated UCP protocol version meets the
requires.protocol.min(andmaxif present). - Capability constraint checking – Ensure each parent capability's selected version satisfies the corresponding entry in
requires.capabilities.
If any constraint fails, the extension is deemed incompatible and excluded from the active set before schema composition begins. This prevents version mismatches from reaching the validation layer.
The Schema Resolution Flow
After computing the intersection of supported capabilities, the platform executes a six‑step resolution flow defined in docs/specification/overview.md (lines 30‑43) to construct the final validation schema:
- Discovery – Fetch the business profile from
/.well-known/ucp. - Negotiation – Intersect platform and business capabilities, selecting the highest mutually‑supported versions.
- Schema Fetch – Download base schemas for active capabilities and schema files for all candidate extensions.
- Version Compatibility – Apply
requireschecks to each extension; drop incompatible extensions and re‑prune orphaned extensions (step 4 of the intersection algorithm). - Compose – Merge base schemas with each extension’s
$defs[{root_capability}]usingallOfchains to produce a single composite JSON‑Schema. - Validate – Use the composite schema to validate request and response payloads.
This flow guarantees that only extensions compatible with the negotiated protocol and capability versions participate in validation.
Mapping Extensions to Parent Capabilities via $defs
UCP establishes a deterministic mapping between the extends declaration and schema fragments. As specified in docs/specification/overview.md (lines 53‑61), each extension that declares extends must provide a $defs entry whose key exactly matches the fully‑qualified name of the parent capability.
This convention allows the resolver to locate the correct fragment ($defs[{parent_name}]) without ambiguity when merging schemas during the composition phase.
Pruning Orphaned Extensions
The resolution algorithm includes a pruning step to maintain schema integrity. According to docs/specification/overview.md (lines 40‑46), if an extension's parent capability is missing from the intersection (or removed due to version incompatibility), the extension is pruned from the active set.
This step recurs iteratively until no orphaned extensions remain, ensuring every surviving extension has at least one valid parent capability in the final composition.
Practical Implementation Examples
Example 1: Defining an Extension with Version Requirements
The following schema in source/schemas/discount.json demonstrates a complete extension definition including protocol and capability constraints:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/ucp/schemas/discount.json",
"name": "com.example.shopping.discount",
"title": "Example Discount Extension",
"requires": {
"protocol": { "min": "2026-01-01" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-06-01", "max": "2027-12-31" }
}
},
"extends": "dev.ucp.shopping.checkout",
"$defs": {
"dev.ucp.shopping.checkout": {
"type": "object",
"properties": {
"discounts": {
"type": "array",
"items": { "$ref": "#/$defs/discount_object" }
}
}
},
"discount_object": {
"type": "object",
"properties": {
"code": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["code", "amount"]
}
}
}
Example 2: Programmatic Resolution in Python
This Python snippet illustrates how to implement the resolution flow programmatically:
import json
import requests
from jsonschema import validate, RefResolver
# 1️⃣ Fetch the business profile (simplified)
profile = requests.get("https://business.example.com/.well-known/ucp").json()
# 2️⃣ Compute intersected capabilities (pseudo‑code)
def intersect_caps(platform_caps, business_caps):
# pick highest common version for each capability name
# returns dict {name: {"version": "...", "schema": "..."}}
pass
active = intersect_caps(platform_profile["capabilities"],
profile["ucp"]["capabilities"])
# 3️⃣ Load base schema & active extensions
schemas = {}
for name, caps in active.items():
base = requests.get(caps["schema"]).json()
schemas[name] = base
for ext in caps.get("extends", []):
ext_schema = requests.get(ext["schema"]).json()
# Verify version requirements
req = ext_schema.get("requires", {})
if not _compatible(req, platform_profile["version"], active):
continue # drop incompatible extension
# Merge using $defs entry that matches the parent capability
parent_defs = ext_schema["$defs"][name]
base = {
"allOf": [base, parent_defs]
}
schemas[name] = base
# 4️⃣ Validate a checkout request
checkout_req = {"line_items": [], "discounts": []}
validate(instance=checkout_req,
schema=schemas["dev.ucp.shopping.checkout"],
resolver=RefResolver('', None))
The helper _compatible checks the requires constraints against the negotiated protocol version and capability versions before allowing the extension into the composition.
Summary
- Version requirements are declared via the
requiresobject in extension schemas, specifyingminand optionalmaxversions for the UCP protocol and parent capabilities. - Validation occurs during negotiation, where platforms and businesses verify that selected versions satisfy all declared constraints; incompatible extensions are excluded before composition.
- Schema resolution follows a six‑step flow: Discovery, Negotiation, Schema Fetch, Version Compatibility, Compose (using
allOfchains), and Validate. - Deterministic mapping requires extensions to place capability fragments in
$defsusing keys that exactly match the parent capability's fully‑qualified name. - Orphan pruning ensures that extensions without valid parent capabilities (due to version mismatches or missing capabilities) are recursively removed from the active set.
Frequently Asked Questions
What happens if an extension's version requirements aren't met?
If an extension's requires constraints are not satisfied by the negotiated protocol or capability versions, the extension is deemed incompatible and excluded from the active set before schema composition occurs. This prevents validation errors by ensuring only compatible extensions participate in the allOf merge.
How does UCP resolve conflicts between multiple extensions extending the same capability?
UCP resolves multiple extensions targeting the same capability by including all compatible extensions in the allOf chain during the composition phase. Each extension's schema fragment (located in $defs[{parent_capability}]) is merged with the base capability schema. The deterministic mapping rules ensure no ambiguity exists in fragment location, and version constraints filter out incompatible extensions before merging.
Where are extension version constraints declared in the UCP specification?
Extension version constraints are documented in docs/specification/overview.md in the Universal-Commerce-Protocol/ucp repository, specifically between lines 66‑84 for declaration syntax and lines 105‑112 for validation rules. The core schema definitions reside in source/schemas/ucp.json.
Can extensions specify maximum version constraints for capabilities?
Yes, extensions can specify optional max version constraints alongside the required min field in both the protocol and individual capability entries within the requires object. This allows extension authors to declare upper bounds for compatibility, ensuring the extension is not used with newer capability versions that might introduce breaking schema changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →