# How UCP Handles Extension Version Requirements and Schema Resolution

> Learn how UCP handles extension version requirements and schema resolution. Discover UCP's approach to version-aware modules and deterministic validation for schema composition during negotiation.

- Repository: [Universal Commerce Protocol (UCP)/ucp](https://github.com/Universal-Commerce-Protocol/ucp)
- Tags: how-to-guide
- Published: 2026-04-26

---

**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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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.

```json
{
  "$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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/source/schemas/discount.json) demonstrates a complete extension definition including protocol and capability constraints:

```json
{
  "$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:

```python
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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/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.