Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion adr/0002-defer-entry-dependencies.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# ADR-0002: Defer Dependency Expression Between Entries

**Status:** Accepted
**Status:** Accepted (this decision — no dependency field in the *core*
schema — still stands; the deferred capability was later delivered as an
official extension by [ADR-0021](0021-dependencies-extension.md), which
resolves the AND/OR concern raised below)

**Date:** 2026-04-02

Expand Down Expand Up @@ -66,3 +69,9 @@ semantics.
- A mechanism for OR relationships (possibly via nested catalogs
or a `relationship` annotation)
- Alignment with how OCI expresses dependencies (referrers/layers)

**Update:** [ADR-0021](0021-dependencies-extension.md) delivers this
capability as the official `https://ai-catalog.org/extensions/dependencies`
extension rather than a core field, so the core schema stays as decided here.
It resolves the AND/OR question by placing AND on a `required` array and OR on
an `anyOf` group (kept distinct from nested-catalog protocol alternatives).
172 changes: 172 additions & 0 deletions adr/0021-dependencies-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# ADR-0021: Entry Dependencies as an Official Extension

## Status
Proposed

## Date
2026-08-05 (Proposed)

## Context
[Issue #63](https://github.com/Agent-Card/ai-catalog/issues/63) (originally
[ards-project/ard-spec#42](https://github.com/ards-project/ard-spec/issues/42))
asks the catalog to describe an artifact's **downstream dependencies**: the
other agents, MCP servers, or datasets it needs to function, and — critically —
the credentials those dependencies require. The motivating use cases are
pre-flight ("can I use this agent — do I hold the credentials its dependencies
need?"), security and compliance planning (user-attributed vs. agent-attributed
access), discovery filtering ("hide agents requiring credentials I don't have"),
and impact analysis ("what breaks if I decommission MCP server X?").

[ADR-0002](0002-defer-entry-dependencies.md) previously **deferred** a
`dependencies` field. It did so for four reasons: (1) the AND-vs-OR ambiguity
(required co-requisites are AND; multi-protocol alternatives are OR); (2) a
nested catalog cannot say whether its members are alternatives or co-requisites;
(3) dependency semantics may belong to the downstream artifact formats; and (4)
scope-creep risk — the catalog should stay a simple index. ADR-0002 explicitly
left the door open: "implementation experience should drive whether and how
dependencies are added."

Two facts have changed the calculus since then:

- The specification now has a first-class, well-defined **extension mechanism**
([ADR-0012](0012-extensibility-via-metadata.md)): a closed core schema plus an
`extensions` map keyed by reverse-DNS or URL, with an "Official Extensions"
tier under the `https://ai-catalog.org/extensions/` prefix (currently just
Metadata). This gives us a sanctioned home for capabilities that not every
catalog needs, *without* enlarging the core.
- The AND/OR modeling problem — ADR-0002's central blocker — has a clean
resolution once the two relationships are placed on separate structural axes
(see Decision).

The original proposal predates several current conventions and must be
reconciled: it used `urn:ai:` (now `urn:air:{publisher}:{namespace}:{name}`,
[ADR-0015](0015-agent-identifier-naming.md)), `application/mcp-server+json`
(now `application/mcp-server-card+json`), `minVersion` (a lower bound only),
and `identityType` (which already means something else on Publisher and Trust
Manifest — an identifier *scheme* hint like `did`/`dns`/`spiffe`).

## Decision
Implement [issue #63](https://github.com/Agent-Card/ai-catalog/issues/63) as an **official extension**, not as a core Catalog Entry
field. Register `https://ai-catalog.org/extensions/dependencies` in the
specification's Official Extensions section. This keeps the core schema
minimal and dependency-free — honoring ADR-0002's decision rather than
reversing it — while delivering the capability through the extension point
ADR-0012 established for exactly this purpose.

**No core change, no version bump.** Because the capability rides on the
existing `extensions` map and unrecognized extensions MUST be ignored, the
change is purely additive. A catalog carrying the extension remains a
conformant `specVersion` "1.0" document; no core field, CDDL rule, data-model
diagram entry, or conformance level changes.

**Shape.** The extension value has two OPTIONAL arrays, `required` and
`optional`. Each element is a *Dependency Requirement* that is EITHER a single
Dependency object (the flat, common case) OR a Dependency Group carrying an
`anyOf` array of interchangeable alternatives. A Dependency has a REQUIRED
`identifier` (a `urn:air` reference to another artifact) plus OPTIONAL `type`,
`versionConstraint`, `credentialPropagation`, and `purpose`.

**AND/OR resolution (ADR-0002 concerns 1 & 2).** The two relationships live on
two independent axes so neither field is overloaded:

- **AND** is the outer `required` array — every element is a co-requisite.
- **OR** is the inner `anyOf` group — any one alternative satisfies that one
slot (e.g. "a vector store: Pinecone *or* pgvector").

This is kept deliberately distinct from the nested-catalog dual-protocol
pattern ([ADR-0004](0004-eliminate-bundle-concept.md)): a nested catalog is
**one** logical artifact with multiple protocol faces; `anyOf` is **different**
artifacts that interchangeably fill a capability slot. Routing OR through
nested catalogs was rejected because it recreates ADR-0002's ambiguity.

**Downstream ownership & scope (ADR-0002 concerns 3 & 4).** The extension
declares *discovery edges* only — references by `identifier`, never embedded
artifacts, and no mandated transitive resolution. The depended-on artifact
still owns its own internal requirements; the catalog does not become a package
manager or resolver. This preserves the thin-pointer posture
([ADR-0013](0013-authoring-vs-distribution-formats.md)) and loose coupling
([ADR-0017](0017-ard-loose-coupling.md)).

**Field reconciliations.**

- `versionConstraint` (not `minVersion`, not `version`): a SemVer *range*
matcher. `minVersion` is only a floor and cannot express an upper bound
("needs v3 but v4 is breaking"), which is exactly the pre-flight
compatibility question. Reusing `version` would mislead readers into
treating a range as a concrete literal (and `version` is the multi-version
uniqueness key).
- `credentialPropagation` (not `identityType`): a distinct field to avoid a
name collision — `identityType` already means an identifier *scheme* hint
(`did`/`dns`/`spiffe`) on Publisher and Trust Manifest, whereas this concept
is credential/audit attribution. Open text with a RECOMMENDED value set
(`obo`, `agent`, `user`, `none`, lowercase to match existing `identityType`
casing), matching how the spec treats every other type-ish field. Marked
**advisory**, never an authoritative security control.

## Rationale
- **Honors ADR-0002 instead of reversing it.** ADR-0002's goal was a simple
core; an extension keeps the core simple while shipping the capability.
- **Uses the mechanism designed for this.** ADR-0012 created Official
Extensions precisely so non-universal capabilities need not enter the core.
- **Resolves the real blocker.** The AND/OR ambiguity that stalled ADR-0002 is
removed by separating the axes (`required` = AND, `anyOf` = OR).
- **Zero-cost to non-adopters.** Additive, ignorable, no version bump; a
planner that understands the extension gains pre-flight/impact-analysis
without imposing anything on catalogs that don't.
- **Cross-vendor governance.** Standardizing the shape (rather than leaving it
to per-vendor `extensions` keys) is what makes cross-platform pre-flight and
impact analysis interoperable — the concern raised at the end of
[issue #63](https://github.com/Agent-Card/ai-catalog/issues/63).

## Consequences
- The spec's Official Extensions section gains a **Dependencies Extension**
subsection (structure, Dependency Requirement, Dependency object, resolution
semantics, example, and a CDDL block for the extension value). A new Security
Considerations subsection ("Dependency Confusion and Malicious Dependencies")
and a note under Catalog Poisoning cover reference substitution, advisory
hints not being controls, cycle/depth bounds, and topology leakage.
- No change to the core CatalogEntry CDDL, the Data Model diagram, the
conformance levels, or `specVersion`.
- ADR-0002 is annotated with a forward reference to this ADR but **remains
Accepted**: its decision (no *core* dependency field) still stands.
- The MCP and Claude-Plugins mapping appendices gain a row noting dependencies
as catalog-layer extension metadata the native formats do not carry.
- Docs gain an authoring subsection, a consumer pre-flight-check section, a
dedicated example page, and nav/feature entries.

## Alternatives Considered
- **A core `dependencies` field on Catalog Entry.** Rejected: it re-opens
ADR-0002's "keep the core simple" decision and forces the capability on every
catalog reader. The extension delivers the same shape without enlarging the
core. (If adoption becomes near-universal, ADR-0012 already anticipates
promoting an official extension to a core field in a future version.)
- **Keeping the proposal's `minVersion`.** Rejected: a lower bound cannot
express an upper bound, the key compatibility question for pre-flight.
- **Reusing `identityType` for credential propagation.** Rejected: name
collision with the existing identifier-scheme meaning.
- **Expressing OR via nested catalogs.** Rejected: recreates the exact
AND/OR ambiguity ADR-0002 flagged; explicit `anyOf` removes it.
- **Always-array `anyOf` (every requirement is a group).** Rejected as verbose
for the common single-dependency case; the discriminated
single-Dependency-or-group union keeps the flat case flat.
- **A closed MUST-enum for `credentialPropagation`.** Rejected for now: open
text with a RECOMMENDED set matches the spec's treatment of `type` and
`identityType` and leaves room for cases like multi-hop delegation. A future
ADR MAY tighten it to a registered enum once usage settles.

## Open Questions
- Whether `obo`/`agent`/`user`/`none` are sufficient for real deployments
(multi-hop delegation, per-tool credential scoping) before this stabilizes.
- Whether the spec should go beyond SHOULD in RECOMMENDING that catalogs
relying on dependencies for pre-flight decisions carry a catalog-level
signature, given the extension sits outside the Trust Manifest's signed
subject.
- Whether OCI / xRegistry substrate bindings need to round-trip the extension
explicitly, or whether carrying it inline as opaque entry metadata suffices.

## Meeting Reference
Records a proposal ahead of an AI Catalog bi-weekly working-group discussion,
implementing the deferred capability from [ADR-0002](0002-defer-entry-dependencies.md)
via the extension mechanism of [ADR-0012](0012-extensibility-via-metadata.md).
Update the Status and Date (and note who raised concerns and the agreed
outcome) once the working group ratifies it.
101 changes: 101 additions & 0 deletions docs/examples/agent-with-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Example: Agent with Dependencies

A catalog entry can declare the downstream artifacts it needs to run using the official [Dependencies extension](../specification.md). This catalog lists a single CRM assistant agent that requires a Salesforce MCP server, needs one of two interchangeable vector stores, and can optionally use an email-sending agent.

```json
{
"specVersion": "1.0",
"host": {
"displayName": "Acme Corp",
"identifier": "did:web:acme-corp.com"
},
"entries": [
{
"identifier": "urn:air:acme-corp.com:agent:crm-assistant",
"displayName": "CRM Assistant",
"type": "application/a2a-agent-card+json",
"description": "An agent that reads and writes CRM records and answers questions grounded in retrieved context.",
"url": "https://api.acme-corp.com/agents/crm-assistant.json",
"publisher": {
"identifier": "did:web:acme-corp.com",
"displayName": "Acme Corp"
},
"extensions": {
"https://ai-catalog.org/extensions/dependencies": {
"required": [
{
"identifier": "urn:air:salesforce.com:mcp:sales-cloud",
"type": "application/mcp-server-card+json",
"versionConstraint": ">=3.0.0",
"credentialPropagation": "obo",
"purpose": "Read and write Salesforce CRM records"
},
{
"purpose": "Vector store for retrieval",
"anyOf": [
{
"identifier": "urn:air:acme-corp.com:mcp:pinecone",
"type": "application/mcp-server-card+json",
"credentialPropagation": "agent"
},
{
"identifier": "urn:air:acme-corp.com:mcp:pgvector",
"type": "application/mcp-server-card+json",
"credentialPropagation": "agent"
}
]
}
],
"optional": [
{
"identifier": "urn:air:acme-corp.com:agent:email-sender",
"type": "application/a2a-agent-card+json",
"credentialPropagation": "agent",
"purpose": "Send email notifications"
}
]
}
}
}
]
}
```

## How it works

**The extension key** (`https://ai-catalog.org/extensions/dependencies`) is an *official* AI Catalog extension — signalled by the `https://ai-catalog.org/extensions/` prefix, the same tier as the built-in `metadata` extension. It lives under the entry's `extensions` map, not as a core field. The core schema is unchanged, so the document stays a conformant `specVersion` `"1.0"` catalog; a consumer that doesn't recognize the extension simply ignores it.

**`required` is an AND** — every element must be satisfiable for the agent to function. A consumer SHOULD treat an unmet `required` element as a hard blocker. Here the agent has two required slots: the Salesforce MCP server *and* a vector store.

**`anyOf` is an OR** — a Dependency Group whose slot is filled by *any one* of its listed alternatives. The vector-store slot is satisfied by either `pinecone` *or* `pgvector`; the consumer only needs one. The group carries a single `purpose` ("Vector store for retrieval") stated once for all alternatives.

**`optional` is graceful degradation** — each element is independently droppable. If the `email-sender` agent is unavailable, the CRM assistant still works; it just can't send email notifications.

**`credentialPropagation`** is an advisory hint about how the agent authenticates to each dependency at runtime, letting a planner reason upfront about which credentials it (or its caller) must hold:

- `obo` — *on behalf of*: the caller's user identity is propagated to the dependency, so the audit trail is user-attributed. The Salesforce dependency uses `obo` because it reads and writes records as the end user.
- `agent` — the agent uses its own workload identity, so activity is agent-attributed. The vector stores and email sender use `agent`.
- `user` — a distinct interactive user credential for the dependency is required (the caller authenticates separately).
- `none` — the dependency is public and needs no credential.

This is an *advisory* hint only: it MUST NOT be treated as a security control. The depended-on artifact enforces its real credential requirements at connection time. It is also distinct from the `identityType` field on a Publisher or Trust Manifest, which hints at an identifier *scheme* (`did`, `dns`, `spiffe`) — a different, orthogonal axis.

Each Dependency references another artifact by its `identifier` alone (never an embedded copy), and MAY point across publishers or catalogs — the Salesforce dependency lives under `salesforce.com`, not `acme-corp.com`. `type` and `versionConstraint` (a SemVer *range* matcher such as `>=3.0.0`, not a literal version) let a consumer reason about a dependency without dereferencing it.

!!! note "Dependencies enable pre-flight checks"
Because these edges are declared as discovery metadata, a consumer can answer *"can I use this agent — do I hold the credentials its required dependencies need?"* **before** invoking it, and *"what breaks if I decommission artifact X?"* for impact analysis. See [Consuming Catalogs](../guides/consuming-catalogs.md) for how a client reads and resolves these edges, and [Creating a Catalog](../guides/creating-a-catalog.md) for declaring them on your own entries. Consumers MUST handle an unresolvable `required` dependency gracefully and MUST NOT auto-fetch, install, or invoke a resolved dependency without applying the same trust verification they would apply to any artifact.

## When to use this pattern

Use the Dependencies extension when:

- An artifact relies on other artifacts to function — an agent that calls a downstream MCP server, a skill that requires a specific tool, or a plugin with a companion agent
- You want consumers to run pre-flight credential checks or filter discovery by the credentials they hold
- You need to express interchangeable alternatives (`anyOf`) for one capability slot, or degrade gracefully when an optional dependency is missing
- Governance or compliance tooling needs impact analysis and user- vs agent-attributed audit expectations

## Related guides

- [Dependencies Extension](../specification.md) — the canonical specification
- [Consuming Catalogs](../guides/consuming-catalogs.md) — resolving and verifying dependencies
- [Creating a Catalog](../guides/creating-a-catalog.md) — declaring dependencies on your entries
1 change: 1 addition & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ Serving the catalog at `/.well-known/ai-catalog.json` enables automated discover
- [**Serving Your Catalog**](guides/serving-your-catalog.md) — hosting options and `Content-Type` configuration
- [**Adding Trust**](guides/adding-trust.md) — reach Level 3 with verifiable identity and attestations
- [**Organizing Catalogs**](guides/organizing-catalogs.md) — nest catalogs for large inventories
- [**Declaring Dependencies**](examples/agent-with-dependencies.md) — use the Dependencies extension to declare downstream artifacts and enable consumer [pre-flight credential checks](guides/consuming-catalogs.md#pre-flight-dependency-check)
9 changes: 9 additions & 0 deletions docs/guides/adding-trust.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ All other fields are optional:
| `signature` | Detached JWS signature over the Trust Manifest content |
| `metadata` | Open map for custom trust metadata |

!!! note "Three distinct identity concepts — don't conflate them"
AI Catalog uses "identity" in three different senses. Keep them separate:

- **Publisher / Trust Manifest `identityType`** — a hint at the *scheme* of an identifier (`did`, `spiffe`, `dns`). It tells a client how to resolve and verify the `identity` URI. That is all it does.
- **`credentialPropagation`** (on the [dependencies extension](../specification.md)) — describes how an artifact authenticates *downstream* to the artifacts it depends on: `obo` (on-behalf-of, user identity propagated — a **user-attributed** audit trail), `agent` (the agent's own workload identity — an **agent-attributed** trail), `user` (a separate interactive user credential), or `none` (public). It is an advisory attribution hint, never a security control.
- **`provenance`** — artifact *lineage*: where the artifact was built or derived from (see [Adding provenance](#adding-provenance)).

So `identityType` is about *who published this*, `credentialPropagation` is about *how it calls its dependencies*, and `provenance` is about *where it came from*.

!!! tip "Attestation document format"
Attestation documents are not restricted to any particular format — they can be human-readable (e.g., a PDF audit report) or machine-readable for automated verification (e.g., JWTs, Verifiable Credentials).

Expand Down
Loading