From 0798bccfe02838e06ec22017f4d4eaef91237b9d Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Mon, 8 Jun 2026 01:10:32 +0000 Subject: [PATCH 1/4] spec: make Catalog Entry displayName optional, not required Move `displayName` from the required members of a Catalog Entry to the OPTIONAL members, and drop it from the Minimal Catalog (Level 1) conformance requirement. Add guidance that `displayName` SHOULD be set only when the referenced artifact does not already carry its own canonical human-readable name (e.g. a raw dataset, model blob, or skill bundle), and SHOULD be omitted when the artifact carries one (A2A Agent Card `name`, MCP Server Card `title`) so the catalog does not duplicate a value that can drift out of sync. When both are present and disagree, the artifact's own name is authoritative. - Examples: card-backed entries (A2A cards, MCP Server Cards, registry server.json, plugin manifests) omit displayName; opaque artifacts (parquet datasets, skill zips) and nested catalogs keep it, since they have no embedded canonical name. HostInfo and Publisher displayName are unaffected -- they name the host/publisher org. - Conceptual-mapping tables (MCP registry, Claude plugins) updated so the human-readable name maps to the artifact, with displayName as the fallback only when the artifact lacks a name. An earlier draft of this change removed `displayName` from the entry level entirely; that was dismissed because opaque artifacts (e.g. the Parquet dataset in examples/ai-catalog.json) have no other home for a human-readable name. Draft for discussion -- see PR description. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Tadas Antanavicius --- specification/ai-catalog.md | 43 ++++++++++---------------- specification/examples/ai-catalog.json | 1 - 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/specification/ai-catalog.md b/specification/ai-catalog.md index 619f9bd..2aaaea5 100644 --- a/specification/ai-catalog.md +++ b/specification/ai-catalog.md @@ -122,13 +122,11 @@ For example, a minimal catalog listing three AI artifacts: }, { "identifier": "urn:example:mcp:weather", - "displayName": "Weather Service", "mediaType": "application/mcp-server-card+json", "url": "https://api.example.com/.well-known/mcp/server-card.json" }, { "identifier": "urn:example:a2a:research", - "displayName": "Research Assistant", "mediaType": "application/a2a-agent-card+json", "url": "https://agents.example.com/researchAssistant" } @@ -194,9 +192,6 @@ It MUST contain the following members: See [Multi-Version Entries](#multi-version-entries) for uniqueness rules when multiple versions are present. -`displayName` -: A string containing a human-readable name for the artifact. - `mediaType` : A string containing the media type that identifies the type of the referenced artifact. This is the mechanism by which clients @@ -224,6 +219,20 @@ provide the artifact content: The following members are OPTIONAL: +`displayName` +: A string containing a human-readable name for the artifact. + This field SHOULD be set only when the referenced artifact does not + already carry its own canonical human-readable name — for example a + raw dataset (`application/parquet`), a model blob, or a skill bundle + (`application/agentskill+zip`), none of which embed a self-describing + name. When the referenced artifact does carry such a name — for + example the `name` field of an A2A Agent Card or the `title` field of + an MCP Server Card — that artifact is the authoritative source and + `displayName` SHOULD be omitted to avoid duplicating a value that can + drift out of sync. When `displayName` is present and disagrees with a + name carried by the referenced artifact, consumers SHOULD treat the + artifact's own name as authoritative. + `description` : A string containing a short description of the artifact. @@ -279,7 +288,6 @@ For example, a catalog listing two versions of the same agent: "entries": [ { "identifier": "urn:acme:agent:finance", - "displayName": "Acme Finance Agent", "version": "2.1.0", "mediaType": "application/a2a-agent-card+json", "url": "https://api.acme-corp.com/agents/finance/v2.1.json", @@ -287,7 +295,6 @@ For example, a catalog listing two versions of the same agent: }, { "identifier": "urn:acme:agent:finance", - "displayName": "Acme Finance Agent", "version": "2.0.0", "mediaType": "application/a2a-agent-card+json", "url": "https://api.acme-corp.com/agents/finance/v2.0.json", @@ -896,7 +903,7 @@ A conformant Minimal Catalog is a JSON document with media type - `specVersion` — the specification version string - `entries` — an array of Catalog Entry objects, each containing at - minimum `identifier`, `displayName`, `mediaType`, and exactly one of `url` or + minimum `identifier`, `mediaType`, and exactly one of `url` or `data` All other fields (`host`, `publisher`, `trustManifest`, @@ -1300,7 +1307,6 @@ artifact types including a nested catalog packaging related artifacts: "entries": [ { "identifier": "urn:acme:agent:finance-a2a", - "displayName": "Acme Finance A2A Agent", "version": "2.1.0", "mediaType": "application/a2a-agent-card+json", "url": "https://api.acme-corp.com/agents/finance.json", @@ -1333,7 +1339,6 @@ artifact types including a nested catalog packaging related artifacts: }, { "identifier": "urn:acme:server:finance-mcp", - "displayName": "Acme Finance MCP Server", "version": "1.4.0", "mediaType": "application/mcp-server-card+json", "url": "https://api.acme-corp.com/.well-known/mcp/server-card.json", @@ -1352,13 +1357,11 @@ artifact types including a nested catalog packaging related artifacts: "entries": [ { "identifier": "urn:acme:agent:finance-a2a", - "displayName": "Finance A2A Agent", "mediaType": "application/a2a-agent-card+json", "url": "https://api.acme-corp.com/agents/finance.json" }, { "identifier": "urn:acme:server:finance-mcp", - "displayName": "Finance MCP Server", "mediaType": "application/mcp-server-card+json", "url": "https://api.acme-corp.com/.well-known/mcp/server-card.json" }, @@ -1407,7 +1410,6 @@ document: "entries": [ { "identifier": "urn:acme:agent:assistant", - "displayName": "Acme Corporate Assistant", "version": "3.0.0", "mediaType": "application/a2a-agent-card+json", "url": "https://api.acme-corp.com/agents/assistant.json", @@ -1468,13 +1470,11 @@ containing both protocol-specific entries: "entries": [ { "identifier": "urn:acme:agent:finance:mcp", - "displayName": "Acme Finance MCP Server", "mediaType": "application/mcp-server-card+json", "url": "https://api.acme-corp.com/.well-known/mcp/server-card.json" }, { "identifier": "urn:acme:agent:finance:a2a", - "displayName": "Acme Finance A2A Agent", "mediaType": "application/a2a-agent-card+json", "url": "https://api.acme-corp.com/agents/finance" } @@ -1734,7 +1734,7 @@ does not address. |:---|:---| | `server.json` document (whole file) | Artifact content via entry `url` or `data` | | `name` (reverse-DNS identifier) | Entry `identifier` (mapped to URI form) | -| `title` | Entry `displayName` | +| `title` | Stays in the artifact (`server.json` carries its own `title`); entry `displayName` is omitted unless the artifact lacks a name | | `description` | Entry `description` | | `version` | Entry `version` | | `repository` | Entry `metadata.repository` | @@ -1772,7 +1772,6 @@ reflects the Registry format: ```json { "identifier": "urn:mcp:io.modelcontextprotocol.anonymous/brave-search", - "displayName": "Brave Search", "version": "1.0.2", "mediaType": "application/json", "url": "https://registry.modelcontextprotocol.io/servers/brave-search/server.json", @@ -1835,7 +1834,6 @@ agents, skills, and other artifacts: "entries": [ { "identifier": "urn:mcp:io.modelcontextprotocol.anonymous/brave-search", - "displayName": "Brave Search", "version": "1.0.2", "mediaType": "application/json", "url": "https://registry.modelcontextprotocol.io/servers/brave-search/server.json", @@ -1844,7 +1842,6 @@ agents, skills, and other artifacts: }, { "identifier": "urn:mcp:io.github.modelcontextprotocol/filesystem", - "displayName": "Filesystem", "version": "1.0.2", "mediaType": "application/json", "url": "https://registry.modelcontextprotocol.io/servers/filesystem/server.json", @@ -1853,7 +1850,6 @@ agents, skills, and other artifacts: }, { "identifier": "urn:mcp:io.github.example/weather-mcp", - "displayName": "Weather", "version": "0.5.0", "mediaType": "application/json", "url": "https://registry.modelcontextprotocol.io/servers/weather/server.json", @@ -1939,7 +1935,6 @@ server can reference the Server Card as its artifact content: ```json { "identifier": "urn:mcp:example.com:finance-server", - "displayName": "Acme Finance MCP Server", "mediaType": "application/mcp-server+json", "url": "https://api.acme-corp.com/.well-known/mcp/server-card.json", "description": "MCP server for financial data and trading tools", @@ -2023,7 +2018,7 @@ plugins/ | Marketplace `description` | Catalog `metadata.description` | | Marketplace `owner` | Catalog `host` (with `identifier` derived from owner) | | `plugins[]` array | Catalog `entries[]` array | -| Plugin `name` | Entry `displayName` and `identifier` (derived as URN) | +| Plugin `name` | Entry `identifier` (derived as URN); the plugin manifest carries its own name, so entry `displayName` is omitted | | Plugin `description` | Entry `description` | | Plugin `category` | Entry `tags[]` (first tag) | | Plugin `tags` | Entry `tags[]` (merged with category) | @@ -2071,7 +2066,6 @@ maps to an AI Catalog where each plugin is an entry: "entries": [ { "identifier": "urn:claude-plugin:anthropic:agent-sdk-dev", - "displayName": "agent-sdk-dev", "mediaType": "application/vnd.anthropic.claude-plugin+json", "url": "https://github.com/anthropics/claude-plugins-official/tree/main/plugins/agent-sdk-dev", "description": "Development kit for working with the Claude Agent SDK", @@ -2086,7 +2080,6 @@ maps to an AI Catalog where each plugin is an entry: }, { "identifier": "urn:claude-plugin:adspirer:ads-agent", - "displayName": "adspirer-ads-agent", "mediaType": "application/vnd.anthropic.claude-plugin+json", "url": "https://github.com/amekala/adspirer-mcp-plugin.git", "description": "Cross-platform ad management for Google Ads, Meta Ads, TikTok Ads, and LinkedIn Ads.", @@ -2107,7 +2100,6 @@ maps to an AI Catalog where each plugin is an entry: }, { "identifier": "urn:claude-plugin:aikido:security", - "displayName": "aikido", "mediaType": "application/vnd.anthropic.claude-plugin+json", "url": "https://github.com/AikidoSec/aikido-claude-plugin.git", "description": "Aikido Security scanning — SAST, secrets, and IaC vulnerability detection.", @@ -2154,7 +2146,6 @@ contains multiple artifact types: "entries": [ { "identifier": "urn:claude-plugin:anthropic:example-plugin:mcp", - "displayName": "Example Plugin MCP Server", "mediaType": "application/mcp-server-card+json", "url": "https://github.com/anthropics/claude-plugins-official/blob/main/plugins/example-plugin/server-card.json" }, diff --git a/specification/examples/ai-catalog.json b/specification/examples/ai-catalog.json index a59b7e1..22d1274 100644 --- a/specification/examples/ai-catalog.json +++ b/specification/examples/ai-catalog.json @@ -8,7 +8,6 @@ "entries": [ { "identifier": "urn:example:agent-finance-001", - "displayName": "Acme Finance Agent", "mediaType": "application/a2a-agent-card+json", "description": "Multi-protocol finance agent.", "tags": [ From a9e970292d8b447c910d8c4e791e5490f9e221ef Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Thu, 18 Jun 2026 14:36:29 +0000 Subject: [PATCH 2/4] docs(adr): add ADR-0016 recording the displayName-optional decision Documents the decision already implemented by this PR: displayName moves from a REQUIRED to an OPTIONAL member of a Catalog Entry and is dropped from the Minimal Catalog (Level 1) required-at-minimum set. Pairs each substantive spec PR with an ADR, matching ADR-0014 (#37) / ADR-0015 (#36). Status is Proposed pending the 2026-06-18 working-group call. Signed-off-by: Tadas Antanavicius Co-Authored-By: Claude Opus 4.8 --- adr/0016-displayname-optional.md | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 adr/0016-displayname-optional.md diff --git a/adr/0016-displayname-optional.md b/adr/0016-displayname-optional.md new file mode 100644 index 0000000..11dbbd9 --- /dev/null +++ b/adr/0016-displayname-optional.md @@ -0,0 +1,43 @@ +# ADR-0016: displayName Is Optional on a Catalog Entry + +## Status +Proposed + +## Date +2026-06-18 (Proposed) + +## Context +A Catalog Entry is a thin pointer: it carries an `identifier`, a `mediaType`, and exactly one of `url` or `data` locating the full artifact. `displayName` (a human-readable name for the artifact) is a REQUIRED member of every Catalog Entry, and is listed among the required-at-minimum members of a Minimal Catalog (Level 1). + +For the card-shaped artifact types this spec calls out by media type, a human-readable name already lives inside the referenced artifact: + +- An **A2A Agent Card** has a REQUIRED, human-readable `name`. +- An **MCP Server Card** carries a human-readable `title` (and a required human-readable `description`); its `name` is a reverse-DNS machine identifier. + +Requiring `displayName` on the entry therefore forces every catalog publisher to copy a value that already exists in the document the client is about to fetch, and to keep the two in sync indefinitely. When they drift, a consumer is left with two conflicting human-readable names and no principled way to pick. + +Not all artifacts self-name, however. Opaque artifacts referenced by a catalog — a raw dataset (`application/parquet`), a model blob, a skill bundle (`application/agentskill+zip`), or a nested catalog — have no embedded canonical name. For those, the entry is the only place a human-readable name can live. + +## Decision +`displayName` is an **OPTIONAL** member of a Catalog Entry, not a required one, and is removed from the Minimal Catalog (Level 1) required-at-minimum set. + +Guidance: a publisher SHOULD set `displayName` only when the referenced artifact does not already carry its own canonical human-readable name. When the artifact does carry one, that artifact is the authoritative source and `displayName` SHOULD be omitted. When `displayName` is present and disagrees with a name carried by the referenced artifact, consumers SHOULD treat the artifact's own name as authoritative. + +This decision concerns only the Catalog Entry. `displayName` on `HostInfo` and `Publisher` is unchanged — those name the catalog host and publishing organization, which is genuinely catalog-authored metadata with no other home. + +## Rationale +- For self-naming artifacts (A2A Agent Cards, MCP Server Cards), requiring an entry-level name guarantees permanent duplication and a standing sync burden. +- Keeping the name authoritative at its source keeps it fresh and avoids the "two conflicting names" failure mode. +- The entry stays a thin pointer, consistent with the catalog's role as a lightweight directory of links (ADR-0013: authoring/discovery format). +- The field is preserved (not removed) so opaque, nameless artifacts still have a place to carry a human-readable name. +- Mirrors the project's existing "optional, not mandatory" posture for discovery mechanics (ADR-0011). + +## Alternatives Considered +- **Keep `displayName` required.** The strongest argument is browsing UX: a UI listing N entries shouldn't have to fetch N artifacts just to show names. Rejected as a permanent duplication/sync cost; the optional form still lets any publisher populate `displayName` on every entry when a self-describing list view is wanted — it just isn't forced on card-backed entries. +- **Remove `displayName` from the entry entirely.** Rejected because opaque artifacts (datasets, model blobs, skill bundles, nested catalogs) embed no self-describing name and would be left nameless in a catalog. + +## Open Question +An MCP Server Card's `title` is itself OPTIONAL. The guidance above is keyed on whether the artifact *carries a canonical name*, not on its media type: if a referenced Server Card omits `title`, the entry SHOULD keep `displayName` so the artifact is not left with only a reverse-DNS identifier and a prose description. + +## Meeting Reference +Slated for discussion at the 2026-06-18 AI Catalog bi-weekly working-group call; this ADR records the proposal ahead of that discussion. Update the Status and Date (and note who raised concerns and the agreed outcome, as in ADR-0011) once the working group ratifies it. From 757528bcf7d8737a483ee4286e7cfdbbb87ca3be Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Thu, 18 Jun 2026 22:37:50 +0000 Subject: [PATCH 3/4] spec: make displayName a master override + add consumer resolution order Refines the displayName-optional change with the consumer-vs-producer guidance agreed at the 2026-06-18 AI Catalog TSC working-group call. - Catalog Entry displayName is now an explicit *master override*: when a publisher sets it, it is authoritative for display and a consumer renders it as given, even if it differs from the artifact's own name. - Add a "Resolving an Artifact's Display Name" section with the consumer fallback order the WG agreed on: displayName -> the referenced artifact's own canonical name (A2A `name` / MCP Server Card `title`) -> the trailing segment of the `identifier` URN (the last-segment hack raised on the call). - Steer name resolution to ingestion-time caching rather than render-time dereferencing, reflecting the point that registries already normalize/cache at ingestion. - Update ADR-0016 to match. Co-Authored-By: Claude Opus 4.8 --- adr/0016-displayname-optional.md | 4 +++- specification/ai-catalog.md | 40 +++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/adr/0016-displayname-optional.md b/adr/0016-displayname-optional.md index 11dbbd9..4e1a608 100644 --- a/adr/0016-displayname-optional.md +++ b/adr/0016-displayname-optional.md @@ -21,7 +21,9 @@ Not all artifacts self-name, however. Opaque artifacts referenced by a catalog ## Decision `displayName` is an **OPTIONAL** member of a Catalog Entry, not a required one, and is removed from the Minimal Catalog (Level 1) required-at-minimum set. -Guidance: a publisher SHOULD set `displayName` only when the referenced artifact does not already carry its own canonical human-readable name. When the artifact does carry one, that artifact is the authoritative source and `displayName` SHOULD be omitted. When `displayName` is present and disagrees with a name carried by the referenced artifact, consumers SHOULD treat the artifact's own name as authoritative. +Guidance: a publisher SHOULD set `displayName` only when the referenced artifact does not already carry its own canonical human-readable name. When the artifact does carry one, that artifact is the authoritative source and `displayName` SHOULD be omitted. When `displayName` *is* present, however, it takes precedence and is authoritative for display: a consumer SHOULD render it as given, even if it differs from a name carried by the referenced artifact — setting `displayName` is how a publisher deliberately overrides the artifact's own name. + +For an entry without `displayName`, a consumer SHOULD resolve a name in order: (1) the entry's `displayName` if present, (2) the referenced artifact's own canonical name (e.g. an A2A Agent Card `name` or MCP Server Card `title`) if already fetched or cached, then (3) the trailing segment of the entry's `identifier` URN. See [Resolving an Artifact's Display Name](../specification/ai-catalog.md#resolving-an-artifacts-display-name) in the specification. This decision concerns only the Catalog Entry. `displayName` on `HostInfo` and `Publisher` is unchanged — those name the catalog host and publishing organization, which is genuinely catalog-authored metadata with no other home. diff --git a/specification/ai-catalog.md b/specification/ai-catalog.md index 2aaaea5..2d1e515 100644 --- a/specification/ai-catalog.md +++ b/specification/ai-catalog.md @@ -229,9 +229,13 @@ The following members are OPTIONAL: example the `name` field of an A2A Agent Card or the `title` field of an MCP Server Card — that artifact is the authoritative source and `displayName` SHOULD be omitted to avoid duplicating a value that can - drift out of sync. When `displayName` is present and disagrees with a - name carried by the referenced artifact, consumers SHOULD treat the - artifact's own name as authoritative. + drift out of sync. When `displayName` *is* present, however, it takes + precedence: it is the authoritative value for display, and a consumer + SHOULD render it as given even when it differs from a name carried by + the referenced artifact. Setting `displayName` is how a publisher + deliberately overrides the artifact's own name. See + [Resolving an Artifact's Display Name](#resolving-an-artifacts-display-name) + for the full consumer resolution order. `description` : A string containing a short description of the artifact. @@ -263,6 +267,36 @@ The following members are OPTIONAL: providing verifiable identity and trust metadata for this artifact. See [Trust Manifest](#trust-manifest) for details. +### Resolving an Artifact's Display Name + +Because `displayName` is OPTIONAL, a consumer rendering a catalog entry +cannot assume it is present. To obtain a human-readable name, a consumer +SHOULD resolve one in the following order: + +1. **`displayName` on the entry**, if present. A publisher-supplied + `displayName` always wins, even when it differs from a name carried by + the referenced artifact. +2. **The referenced artifact's own canonical name**, if the consumer has + already fetched or cached the artifact — for example the `name` field + of an A2A Agent Card or the `title` field of an MCP Server Card. +3. **The trailing segment of the entry's `identifier`** as a last + resort — the portion after its final `:` or `/` delimiter. For + example, `urn:example:mcp:weather` yields `weather` and + `urn:mcp:io.modelcontextprotocol.anonymous/brave-search` yields + `brave-search`. + +A consumer SHOULD NOT dereference an artifact at render time solely to +obtain a name. A registry, directory, or other service built on top of a +catalog SHOULD resolve the name once at ingestion — alongside any other +derived metadata it attaches, such as relevance scores or tags — and +cache the result, rather than fetching artifacts on the rendering path. + +This order also covers a referenced MCP Server Card whose `title` is +itself absent: step 2 yields no name, so the consumer falls through to +the `identifier` segment in step 3. A publisher MAY still set +`displayName` on such an entry to provide a better name than the bare +identifier segment. + ## Multi-Version Entries A catalog MAY contain multiple entries with the same `identifier` and From d004d49d31f91a3789e91b3a30b5ac333bb18ea8 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Thu, 18 Jun 2026 22:39:20 +0000 Subject: [PATCH 4/4] adr: fix resolution-order lead-in (drop self-contradictory 'without displayName') MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lead-in said 'For an entry without displayName' but step (1) is 'displayName if present' — reword to the general rendering case. Co-Authored-By: Claude Opus 4.8 --- adr/0016-displayname-optional.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adr/0016-displayname-optional.md b/adr/0016-displayname-optional.md index 4e1a608..282372c 100644 --- a/adr/0016-displayname-optional.md +++ b/adr/0016-displayname-optional.md @@ -23,7 +23,7 @@ Not all artifacts self-name, however. Opaque artifacts referenced by a catalog Guidance: a publisher SHOULD set `displayName` only when the referenced artifact does not already carry its own canonical human-readable name. When the artifact does carry one, that artifact is the authoritative source and `displayName` SHOULD be omitted. When `displayName` *is* present, however, it takes precedence and is authoritative for display: a consumer SHOULD render it as given, even if it differs from a name carried by the referenced artifact — setting `displayName` is how a publisher deliberately overrides the artifact's own name. -For an entry without `displayName`, a consumer SHOULD resolve a name in order: (1) the entry's `displayName` if present, (2) the referenced artifact's own canonical name (e.g. an A2A Agent Card `name` or MCP Server Card `title`) if already fetched or cached, then (3) the trailing segment of the entry's `identifier` URN. See [Resolving an Artifact's Display Name](../specification/ai-catalog.md#resolving-an-artifacts-display-name) in the specification. +When rendering an entry, a consumer SHOULD resolve a name in order: (1) the entry's `displayName` if present, (2) the referenced artifact's own canonical name (e.g. an A2A Agent Card `name` or MCP Server Card `title`) if already fetched or cached, then (3) the trailing segment of the entry's `identifier` URN. See [Resolving an Artifact's Display Name](../specification/ai-catalog.md#resolving-an-artifacts-display-name) in the specification. This decision concerns only the Catalog Entry. `displayName` on `HostInfo` and `Publisher` is unchanged — those name the catalog host and publishing organization, which is genuinely catalog-authored metadata with no other home.