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 a min date string (e.g., "2026-01-23"), with an optional max.
  • capabilities – A map where each key is a fully‑qualified parent capability name and the value is a version constraint object containing min and optionally max date 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:

  1. Protocol version verification – Confirm the negotiated UCP protocol version meets the requires.protocol.min (and max if present).
  2. 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:

  1. Discovery – Fetch the business profile from /.well-known/ucp.
  2. Negotiation – Intersect platform and business capabilities, selecting the highest mutually‑supported versions.
  3. Schema Fetch – Download base schemas for active capabilities and schema files for all candidate extensions.
  4. Version Compatibility – Apply requires checks to each extension; drop incompatible extensions and re‑prune orphaned extensions (step 4 of the intersection algorithm).
  5. Compose – Merge base schemas with each extension’s $defs[{root_capability}] using allOf chains to produce a single composite JSON‑Schema.
  6. 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 requires object in extension schemas, specifying min and optional max versions 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 allOf chains), and Validate.
  • Deterministic mapping requires extensions to place capability fragments in $defs using 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →