How UCP Extensions Function and Integrate with Base Capabilities: A Technical Deep Dive
UCP extensions function through a schema-driven inheritance model where an extension declares one or more parent capabilities using the extends field, merges schemas via allOf composition within a $defs map keyed by parent capability name, and undergoes version-gated intersection during profile negotiation.
The Universal Commerce Protocol (UCP) enables flexible commerce APIs through an extensibility system that augments base capabilities without modifying root definitions. According to the Universal-Commerce-Protocol/ucp source code, this integration relies on JSON Schema composition patterns defined in source/schemas/capability.json and formalized in docs/specification/overview.md. Understanding how UCP extensions are declared, discovered, composed, and validated is essential for implementing protocol-compliant commerce platforms.
Core Concepts of UCP Extensions
The Capability and Extension Model
Capabilities are the fundamental building blocks of UCP, identified by reverse-domain names like dev.ucp.shopping.checkout. Each capability contains a JSON schema describing request and response payloads for a specific service. Extensions are optional modules that add fields or behaviors to existing capabilities without altering the original schema. This separation allows platforms to inherit base functionality while layering custom features such as discount calculations or fraud detection.
The extends Field and Schema Inheritance
Every extension declares its relationship to parent capabilities through the extends field defined in source/schemas/capability.json (lines 14-30). This field accepts either a single string or an array of strings, each containing the exact reverse-domain identifier of a parent capability. For example, a discount extension targeting both checkout and cart capabilities would declare:
"extends": ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"]
The value must precisely match the parent capability’s identifier, ensuring unambiguous schema resolution during composition.
The $defs Map and Schema Composition
Extensions store their incremental schema definitions within a $defs object where each key must be the full parent capability name. As documented in docs/specification/overview.md, the value associated with each key is an allOf composition that merges the parent schema with the extension’s additional properties. This pattern ensures that extensions explicitly namespace their contributions under the parent they augment.
Extension Discovery and Negotiation
Profile Advertisement and Capability Intersection
Both businesses and platforms publish a UCP profile at the well-known endpoint /.well-known/ucp that lists supported capabilities. Extensions appear as entries under the capabilities object with the extends attribute populated. The platform fetches the business profile and computes an intersection of supported capabilities, matching versions to determine compatibility.
The intersection process follows the algorithm described in the specification’s Intersection Algorithm section:
- Filter capabilities to include only those present in both profiles with matching versions.
- Identify extensions whose
extendsvalues reference capabilities within the intersected set. - Prune extensions that lack valid parent references.
Pruning Orphaned Extensions
During negotiation, the implementation prunes orphaned extensions—any extension whose extends values are not present in the intersected capability set is removed. This prevents validation failures against missing base schemas. The following Python pseudo-code illustrates the pruning logic:
def intersect_profiles(platform, business):
# Keep only capabilities that appear in both profiles
intersect = {
name: [p for p in platform[name] if any(b['version'] == p['version'] for b in business.get(name, []))]
for name in platform
}
# Prune orphaned extensions
changed = True
while changed:
changed = False
for name, caps in list(intersect.items()):
for cap in caps:
parents = cap.get('extends', [])
if not isinstance(parents, list):
parents = [parents]
if not any(p in intersect for p in parents):
caps.remove(cap)
changed = True
return intersect
Schema Composition and Validation
The allOf Merging Process
Once capabilities are negotiated, the implementation fetches base schemas (e.g., checkout.json, cart.json) and active extension schemas. The resolver locates the $defs entry matching the parent capability name and composes the final schema using allOf chaining. For example, the Discount extension in source/schemas/shopping/discount.json composes with checkout as follows:
"$defs": {
"dev.ucp.shopping.checkout": {
"title": "Checkout with Discount",
"allOf": [
{ "$ref": "checkout.json" },
{
"type": "object",
"properties": {
"discounts": { "$ref": "#/$defs/discounts_object" }
}
}
]
}
}
Because extensions merge via allOf, added fields are optional unless the extension schema explicitly marks them as required.
Multi-Parent Extensions
Extensions may declare multiple parents, enabling a single module to enrich several capabilities simultaneously. The specification requires at least one parent to be present in the intersected set; otherwise, the extension is dropped entirely. This many-to-many relationship allows cross-cutting concerns like analytics or taxation to apply across checkout, cart, and order capabilities without code duplication.
Version Compatibility Requirements
Extensions may include a requires object declaring minimum (and optional maximum) protocol and capability versions. Implementations must verify that negotiated versions satisfy these constraints before schema composition. Incompatible extensions are excluded during the intersection phase, preventing runtime validation errors against mismatched schema versions.
Runtime Behavior and Relevance
Capability Echoing in Responses
An extension is considered relevant for a specific operation if any of its extends values matches the root capability of that operation. Only relevant extensions are echoed back in the ucp.capabilities section of the response. For example, a checkout response including the Discount extension would appear as:
{
"ucp": {
"version": "2026-01-23",
"capabilities": {
"dev.ucp.shopping.checkout": [{ "version": "2026-01-23" }],
"dev.ucp.shopping.discount": [{ "version": "2026-01-23" }]
}
},
"id": "ck_12345",
"line_items": [],
"discounts": {
"codes": ["SUMMER20"],
"applied": [
{
"code": "SUMMER20",
"title": "Summer Sale 20% Off",
"amount": { "currency": "USD", "value": 2000 },
"automatic": false,
"allocations": [
{
"path": "$.line_items[0]",
"amount": { "currency": "USD", "value": 2000 }
}
]
}
]
}
}
The discounts object structure follows the Discount extension schema defined in source/schemas/shopping/discount.json.
Practical Implementation Examples
Business Profile Configuration
To advertise an extension, a business publishes a profile containing both the base capability and the extension with its extends declaration:
{
"ucp": {
"version": "2026-01-23",
"capabilities": {
"dev.ucp.shopping.checkout": [
{
"version": "2026-01-23",
"spec": "https://ucp.dev/2026-01-23/specification/checkout",
"schema": "https://ucp.dev/2026-01-23/schemas/shopping/checkout.json"
}
],
"dev.ucp.shopping.discount": [
{
"version": "2026-01-23",
"spec": "https://ucp.dev/2026-01-23/specification/discount",
"schema": "https://ucp.dev/2026-01-23/schemas/shopping/discount.json",
"extends": ["dev.ucp.shopping.checkout"]
}
]
}
}
}
Composed Schema Output
The resulting composed schema for a checkout operation with the Discount extension active would resolve to:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/checkout+discount.json",
"allOf": [
{ "$ref": "https://ucp.dev/schemas/shopping/checkout.json" },
{
"type": "object",
"properties": {
"discounts": {
"$ref": "https://ucp.dev/schemas/shopping/discount.json#/$defs/discounts_object"
}
}
}
]
}
Summary
- Schema-Driven Inheritance: UCP extensions use the
extendsfield to declare parent capabilities and merge schemas viaallOfcomposition within$defs. - Profile Negotiation: Extensions are discovered through
/.well-known/ucpprofiles and survive only if their parents exist in the capability intersection. - Multi-Parent Support: Extensions can augment multiple capabilities simultaneously, provided at least one parent is present in the negotiated set.
- Version Safety: The
requiresobject enforces version constraints, filtering incompatible extensions before schema composition. - Runtime Echoing: Only relevant extensions (those whose
extendsmatches the current operation) are included in response metadata.
Frequently Asked Questions
What is the difference between a UCP capability and an extension?
A capability is a core service definition (like checkout or cart) that contains the base JSON schema for request and response payloads. An extension is an optional augmentation that adds fields or behaviors to one or more capabilities without modifying the original schema files. Extensions reference capabilities via the extends field and merge their schemas using allOf composition.
How does UCP handle extensions when a parent capability is missing?
The implementation prunes orphaned extensions during the profile intersection process. If an extension declares extends: ["dev.ucp.shopping.checkout"] but the checkout capability is not present in the negotiated set, the extension is removed entirely before schema composition begins. This prevents validation errors against undefined base schemas.
Can a single extension modify multiple capabilities simultaneously?
Yes, UCP supports multi-parent extensions. An extension can declare an array of parent capability names in the extends field, such as ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"]. The extension remains active if at least one parent is present in the intersected capability set, and it contributes its schema additions to all available parents.
How are version conflicts resolved between extensions and base capabilities?
Extensions may include a requires object specifying minimum and maximum versions for the protocol and parent capabilities. During profile intersection, the implementation validates that negotiated versions satisfy these constraints. Extensions with unsatisfied version requirements are excluded before schema composition, ensuring that only compatible schema definitions are merged and validated.
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 →