Understanding Multi-Parent Extensions in UCP: Schema and Negotiation Logic

Multi-parent extensions in the Universal Commerce Protocol allow capabilities to inherit from multiple parent capabilities by specifying an array of parent identifiers in the extends property, requiring only one parent to be present during capability negotiation for the extension to remain valid.

The Universal Commerce Protocol (UCP) models capabilities as reusable building blocks that support inheritance through the extends property. Within the Universal-Commerce-Protocol/ucp repository, multi-parent extensions enable a capability to declare multiple potential parents, providing flexibility for complex business use cases such as checkout flows that combine payment handling and discount models. This article examines the schema definitions in source/schemas/capability.json, the validation rules that enforce correct structure, and the negotiation logic that determines extension validity during capability intersection.

How Multi-Parent Extensions Work

UCP capabilities declare inheritance using the optional extends field, which supports two distinct forms:

  • Single-parent: "extends": "parent.capability" requires the exact parent to exist in the negotiated capability set.
  • Multi-parent: "extends": ["parent.one", "parent.two"] allows the capability to inherit from any number of roots, requiring at least one of the listed parents to be present for validity.

The multi-parent pattern enables developers to create flexible capabilities that combine features from disparate branches of the capability hierarchy. For example, a fulfillment extension can simultaneously reference both checkout and discount capabilities without mandating that both be present in every deployment scenario.

Schema Validation in capability.json

The structural rules for multi-parent extensions are enforced in source/schemas/capability.json. The schema defines the extends field using a oneOf clause that accepts either a single string or an array of strings.

For array-based declarations, the schema enforces "minItems": 1 to prevent empty parent lists, ensuring that every multi-parent extension declares at least one valid ancestor. This validation guarantees that capability manifests cannot declare extension dependencies that resolve to zero parents. The schema documentation in lines 14-30 of source/schemas/capability.json formally specifies these constraints, enabling runtime validation during manifest ingestion.

Negotiation Pruning and Transitive Validation

During capability negotiation between platforms and businesses, UCP performs intersection based on capability name and version. Following intersection, the system prunes orphaned extensions to ensure only valid inheritance chains remain.

The Pruning Algorithm

According to docs/specification/overview.md (lines 40-44), the pruning logic differentiates between single and multi-parent extensions:

  • If extends is a string, the exact parent must exist in the intersected set.
  • If extends is an array, at least one parent from the array must be present.

Extensions that fail this validation are removed from the capability set. This rule applies recursively: if removing an extension orphans its children, those children are also pruned in subsequent iterations.

Transitive Processing

The pruning process repeats until no further extensions are removed. This transitive pruning guarantees that any capability remaining in the final negotiated set has a complete and valid inheritance chain. The algorithm terminates when a full pass through the capability list finds no orphaned extensions, ensuring platform stability before capability activation.

The following Python pseudocode illustrates the pruning logic as specified in the UCP documentation:

def prune_extensions(caps):
    """Remove extensions whose parents are absent."""
    changed = True
    while changed:
        changed = False
        for cap in list(caps):
            if "extends" in cap:
                parents = cap["extends"]
                # Ensure `parents` is always a list

                if isinstance(parents, str):
                    parents = [parents]
                # Keep the extension if any parent is present

                if not any(p in {c["name"] for c in caps} for p in parents):
                    caps.remove(cap)
                    changed = True
    return caps

Practical Configuration Examples

Declaring Multi-Parent Capabilities

Capability manifests specify multi-parent relationships using standard JSON or YAML syntax. The following example from the UCP specification demonstrates a fulfillment capability that extends both checkout and discount capabilities:

{
  "name": "dev.ucp.shopping.fulfillment_plus",
  "version": "2026-01-11",
  "extends": [
    "dev.ucp.shopping.checkout",
    "dev.ucp.shopping.discount"
  ],
  "spec": "https://ucp.dev/2026-01-11/specification/fulfillment_plus",
  "schema": "https://ucp.dev/2026-01-11/schemas/shopping/fulfillment_plus.json"
}

In this configuration, fulfillment_plus inherits behavior from both parents but remains valid if the negotiating platform provides only dev.ucp.shopping.checkout or dev.ucp.shopping.discount.

Playground Configuration

The UCP playground supports multi-parent extensions in YAML configuration files. As documented in docs/specification/playground.md, you can declare array-based extensions within the capabilities section:

capabilities:
  "dev.ucp.shopping.fulfillment":
    - extends: ["dev.ucp.shopping.checkout", "dev.ucp.shopping.discount"]
      version: "{{ ucp_version }}"
      spec: "https://ucp.dev/{{ ucp_version }}/specification/fulfillment"
      schema: "https://ucp.dev/{{ ucp_version }}/schemas/shopping/fulfillment.json"

This format enables rapid testing of complex capability inheritance scenarios without requiring full platform deployment.

Summary

  • Multi-parent extensions allow UCP capabilities to declare multiple potential parents via an array in the extends field, with validation enforced in source/schemas/capability.json.
  • The schema requires "minItems": 1 for parent arrays and permits oneOf string or array types.
  • During negotiation, an extension survives pruning if any of its declared parents exists in the intersected capability set.
  • Transitive pruning iteratively removes orphaned extensions until all remaining capabilities have valid inheritance chains.

Frequently Asked Questions

What is the difference between single-parent and multi-parent extensions in UCP?

Single-parent extensions require the exact specified parent to be present in the negotiated set for the extension to remain valid. Multi-parent extensions, specified as an array in the extends field, remain valid if at least one of the listed parents is present during capability intersection, providing greater flexibility in capability composition.

How does UCP validate multi-parent extension declarations?

The validation occurs in source/schemas/capability.json, which defines the extends field using a oneOf clause that accepts either a string or a non-empty array of strings. The schema enforces "minItems": 1 to prevent empty parent arrays, ensuring every multi-parent extension declares at least one potential ancestor.

What happens during negotiation if none of a multi-parent extension's parents are present?

According to docs/specification/overview.md, the extension undergoes pruning and is removed from the capability set. This orphan removal process repeats transitively until no remaining extensions lack at least one valid parent in the intersected catalog.

Can a capability combine features from incompatible capability branches using multi-parent extensions?

Yes, multi-parent extensions enable capabilities to draw from multiple inheritance roots, such as combining payment-handling and discount-model capabilities. However, the extension only activates if the negotiating parties support at least one parent, allowing platforms to selectively enable complex features without requiring full lineage support.

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 →