Use AI Catalog for Server Card discovery (#42)
This commit is contained in:
parent
3b2d974dbb
commit
1491bd2471
2 changed files with 75 additions and 124 deletions
|
|
@ -16,6 +16,8 @@ A **Server Card** is a JSON document — hosted at any unreserved URI, with `GET
|
||||||
- Its remote transport endpoints (URLs, headers, variable templates, supported protocol versions)
|
- Its remote transport endpoints (URLs, headers, variable templates, supported protocol versions)
|
||||||
- Optional registry-style extension metadata (`_meta`)
|
- Optional registry-style extension metadata (`_meta`)
|
||||||
|
|
||||||
|
Clients can automatically discover Server Cards through [AI Catalog](https://github.com/Agent-Card/ai-catalog) entries advertised on domains present in agentic sessions.
|
||||||
|
|
||||||
Server Cards intentionally omit primitive listings (tools, resources, prompts) — those remain subject to runtime listing via the protocol's standard list operations. They also intentionally omit local installation metadata — see [Relationship to the MCP Registry](#relationship-to-the-mcp-registry).
|
Server Cards intentionally omit primitive listings (tools, resources, prompts) — those remain subject to runtime listing via the protocol's standard list operations. They also intentionally omit local installation metadata — see [Relationship to the MCP Registry](#relationship-to-the-mcp-registry).
|
||||||
|
|
||||||
## Relationship to the MCP Registry
|
## Relationship to the MCP Registry
|
||||||
|
|
|
||||||
|
|
@ -6,100 +6,65 @@ MCP defines a discovery mechanism that enables clients to find available MCP ser
|
||||||
domain without prior configuration. This mechanism answers _where_ to connect, before any
|
domain without prior configuration. This mechanism answers _where_ to connect, before any
|
||||||
protocol exchange establishes _how_ to communicate.
|
protocol exchange establishes _how_ to communicate.
|
||||||
|
|
||||||
## MCP Catalog
|
## AI Catalog
|
||||||
|
|
||||||
An **MCP Catalog** is a JSON document published by an organization to advertise the
|
An [AI Catalog](https://github.com/Agent-Card/ai-catalog) is a JSON document published by
|
||||||
|
an organization to advertise AI artifacts, including the
|
||||||
[MCP Server Cards](#mcp-server-cards) relevant to its services.
|
[MCP Server Cards](#mcp-server-cards) relevant to its services.
|
||||||
|
|
||||||
The catalog MAY reference servers on different domains than the catalog itself — for
|
The catalog MAY reference Server Cards on different domains than the catalog itself — for
|
||||||
example, `acme.org/.well-known/...` MAY advertise servers operated by
|
example, an AI Catalog on `acme.org` MAY advertise servers operated by
|
||||||
`mcp-server-host-saas.com` on Acme's behalf. Clients can fetch this document to discover
|
`mcp-server-host-saas.com` on Acme's behalf. Clients can fetch the catalog to discover
|
||||||
servers and then retrieve individual [Server Cards](#mcp-server-cards) for connection
|
servers and then retrieve individual Server Cards for connection details.
|
||||||
details.
|
|
||||||
|
|
||||||
The MCP Catalog format is a minimal, MCP-scoped subset of the
|
|
||||||
[AI Catalog](https://github.com/Agent-Card/ai-catalog) specification. This alignment
|
|
||||||
ensures that MCP Catalog entries can be used as-is within a full AI Catalog document,
|
|
||||||
enabling a smooth migration path when the cross-protocol AI Catalog standard is finalized.
|
|
||||||
|
|
||||||
### Well-Known URI
|
### Well-Known URI
|
||||||
|
|
||||||
Organizations offering services accessible via MCP SHOULD publish an MCP Catalog at the
|
An AI Catalog MAY be served from any URL. For automated domain-level discovery, hosts MAY
|
||||||
domain users associate with the service. The MCP Catalog should live at:
|
publish one at:
|
||||||
|
|
||||||
```
|
```
|
||||||
/.well-known/mcp/catalog.json
|
/.well-known/ai-catalog.json
|
||||||
```
|
```
|
||||||
|
|
||||||
This endpoint:
|
Clients performing domain-level discovery SHOULD attempt to retrieve this well-known URL.
|
||||||
|
When served over HTTP, the document SHOULD use the `application/ai-catalog+json` media
|
||||||
|
type.
|
||||||
|
|
||||||
- MUST be accessible via HTTPS (HTTP MAY be supported for local/development use)
|
### Server Card Entries
|
||||||
- MUST include appropriate CORS headers (see [CORS Requirements](#cors-requirements))
|
|
||||||
- SHOULD include appropriate caching headers (see [Caching](#caching))
|
|
||||||
|
|
||||||
### Catalog Format
|
The [AI Catalog specification](https://github.com/Agent-Card/ai-catalog) defines the full
|
||||||
|
catalog and entry formats. An entry for an MCP Server Card has:
|
||||||
|
|
||||||
An MCP Catalog document is a JSON object that MUST contain the following members:
|
| Member | Required | Description |
|
||||||
|
| :----------- | :------- | :---------------------------------------------------------------------------------------------------- |
|
||||||
|
| `identifier` | Yes | A logical discovery identifier for this server |
|
||||||
|
| `type` | Yes | MUST be `application/mcp-server-card+json` |
|
||||||
|
| `url` | One of | URL where the full [Server Card](#mcp-server-cards) can be retrieved |
|
||||||
|
| `data` | One of | The complete [Server Card](#mcp-server-cards) included inline; exactly one of `url` or `data` is used |
|
||||||
|
|
||||||
| Member | Type | Required | Description |
|
For open or federated systems, AI Catalog identifiers use the domain-anchored format:
|
||||||
| :------------ | :----- | :------- | :---------------------------------------------------------- |
|
|
||||||
| `specVersion` | string | Yes | The version of the MCP Catalog format (currently `"draft"`) |
|
|
||||||
| `entries` | array | Yes | An array of Catalog Entry objects. This array MAY be empty. |
|
|
||||||
|
|
||||||
#### Catalog Entry
|
|
||||||
|
|
||||||
Each entry in the `entries` array describes a single MCP server and MUST contain:
|
|
||||||
|
|
||||||
| Member | Type | Required | Description |
|
|
||||||
| :----------- | :----- | :------- | :------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `identifier` | string | Yes | A logical discovery URN for this server (e.g., `urn:air:example.com:weather`) |
|
|
||||||
| `type` | string | Yes | An identifier specifying the type of the referenced artifact. MUST be `application/mcp-server-card+json` |
|
|
||||||
| `url` | string | Yes | URL where the full [Server Card](#mcp-server-cards) can be retrieved |
|
|
||||||
|
|
||||||
An MCP Catalog entry carries no human-readable-name field. Every entry references a
|
|
||||||
[Server Card](#mcp-server-cards), and the Server Card's `title` is the source of truth for
|
|
||||||
a server's name — so a client reads the name from the card at `url` rather than from the
|
|
||||||
catalog entry. Carrying the name in the entry as well would only duplicate it and risk it
|
|
||||||
drifting out of sync with the card.
|
|
||||||
|
|
||||||
The [AI Catalog](https://github.com/Agent-Card/ai-catalog) specification defines an
|
|
||||||
OPTIONAL `displayName` on its catalog entries (see
|
|
||||||
[ADR 0016](https://github.com/Agent-Card/ai-catalog/pull/39)). Because that field is
|
|
||||||
optional upstream, an MCP Catalog entry that omits it is still a valid drop-in subset of
|
|
||||||
an AI Catalog entry: MCP simply does not use `displayName`, deferring in every case to the
|
|
||||||
referenced Server Card's `title`.
|
|
||||||
|
|
||||||
The `identifier` is a **logical discovery name** that follows the
|
|
||||||
[AI Catalog](https://github.com/Agent-Card/ai-catalog) domain-anchored URN convention
|
|
||||||
standardized in [ADR 0015](https://github.com/Agent-Card/ai-catalog/pull/36):
|
|
||||||
|
|
||||||
```
|
```
|
||||||
urn:air:{publisher}:{namespace}:{name}
|
urn:air:{publisher}:{namespace}:{name}
|
||||||
```
|
```
|
||||||
|
|
||||||
The segments are:
|
For example, a Server Card named `com.example/weather` can use the catalog identifier
|
||||||
|
`urn:air:example.com:mcp:weather`.
|
||||||
|
|
||||||
- **`publisher`** — the publisher's domain (forward DNS), e.g. `example.com`. ADR 0015
|
An entry does not need to repeat the Server Card's human-readable fields. Clients can read
|
||||||
anchors the identifier on this domain.
|
the server's `title`, `description`, and `version` from the card itself, avoiding duplicated
|
||||||
- **`namespace`** — optional, populate if you wish in accordance with the AI Catalog specification
|
values that could drift out of sync.
|
||||||
- **`name`** — the server's name suffix, i.e. the segment after the `/` in the referenced Server
|
|
||||||
Card's reverse-DNS `name`, e.g. `weather`.
|
|
||||||
|
|
||||||
So a Server Card named `com.example/weather`, can be referenced as
|
|
||||||
`urn:air:example.com:weather`. Anchoring the identifier on the publisher's domain keeps
|
|
||||||
it globally unique and stable across infrastructure changes, and lets an MCP Catalog entry
|
|
||||||
be indexed as-is within a full AI Catalog document.
|
|
||||||
|
|
||||||
### Example: Single Server
|
### Example: Single Server
|
||||||
|
|
||||||
A domain hosting a single MCP server:
|
A domain advertising a single MCP server:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"specVersion": "draft",
|
"specVersion": "1.0",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"identifier": "urn:air:example.com:weather",
|
"identifier": "urn:air:example.com:mcp:weather",
|
||||||
"type": "application/mcp-server-card+json",
|
"type": "application/mcp-server-card+json",
|
||||||
"url": "https://example.com/mcp/server-card"
|
"url": "https://example.com/mcp/server-card"
|
||||||
}
|
}
|
||||||
|
|
@ -109,24 +74,24 @@ A domain hosting a single MCP server:
|
||||||
|
|
||||||
### Example: Multiple Servers
|
### Example: Multiple Servers
|
||||||
|
|
||||||
A domain hosting several MCP servers, each with its own server card:
|
A domain advertising several MCP servers, each with its own Server Card:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"specVersion": "draft",
|
"specVersion": "1.0",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"identifier": "urn:air:acme.com:code-review",
|
"identifier": "urn:air:acme.com:mcp:code-review",
|
||||||
"type": "application/mcp-server-card+json",
|
"type": "application/mcp-server-card+json",
|
||||||
"url": "https://acme.com/code-review/server-card"
|
"url": "https://acme.com/code-review/server-card"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"identifier": "urn:air:acme.com:docs-search",
|
"identifier": "urn:air:acme.com:mcp:docs-search",
|
||||||
"type": "application/mcp-server-card+json",
|
"type": "application/mcp-server-card+json",
|
||||||
"url": "https://acme.com/docs-search/server-card"
|
"url": "https://acme.com/docs-search/server-card"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"identifier": "urn:air:acme.com:ci-cd",
|
"identifier": "urn:air:acme.com:mcp:ci-cd",
|
||||||
"type": "application/mcp-server-card+json",
|
"type": "application/mcp-server-card+json",
|
||||||
"url": "https://acme.com/ci-cd/server-card"
|
"url": "https://acme.com/ci-cd/server-card"
|
||||||
}
|
}
|
||||||
|
|
@ -140,23 +105,25 @@ Clients performing domain-level discovery SHOULD follow this procedure:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
A[Client wants to discover MCP servers on example.com] --> B[Fetch /.well-known/mcp/catalog.json]
|
A[Client wants to discover MCP servers on example.com] --> B[Fetch /.well-known/ai-catalog.json]
|
||||||
B --> C{Valid catalog returned?}
|
B --> C{Valid AI Catalog returned?}
|
||||||
C -->|Yes| D[Parse entries array]
|
C -->|No| D[Discovery unavailable for this domain]
|
||||||
C -->|No| E[Discovery unavailable for this domain]
|
C -->|Yes| E[Select entries with the MCP Server Card type]
|
||||||
D --> F[For each entry, fetch server card from url]
|
E --> F{Entry contains url or data?}
|
||||||
F --> G[Use server card to configure connection]
|
F -->|url| G[Fetch Server Card from url]
|
||||||
|
F -->|data| H[Read inline Server Card]
|
||||||
|
G --> I[Use Server Card to configure connection]
|
||||||
|
H --> I
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Fetch `https://{domain}/.well-known/mcp/catalog.json`
|
1. Fetch `https://{domain}/.well-known/ai-catalog.json`
|
||||||
2. If a valid MCP Catalog is returned, iterate over the `entries` array
|
2. If a valid AI Catalog is returned, select entries whose `type` is
|
||||||
3. For each entry, retrieve the server card from the entry's `url`, expressing the
|
`application/mcp-server-card+json`
|
||||||
Server Card media type via the `Accept` header (see
|
3. For an entry with `url`, retrieve the Server Card from that URL, expressing the Server
|
||||||
[Server Card Location](#server-card-location))
|
Card media type via the `Accept` header (see
|
||||||
4. Use the server card metadata to configure and establish an MCP connection
|
[Hosted Server Card Location](#hosted-server-card-location)); for an entry with `data`,
|
||||||
|
use the inline Server Card
|
||||||
Clients SHOULD validate that each entry has `type` set to `application/mcp-server-card+json`
|
4. Use the Server Card metadata to configure and establish an MCP connection
|
||||||
and ignore entries with unrecognized types.
|
|
||||||
|
|
||||||
## MCP Server Cards
|
## MCP Server Cards
|
||||||
|
|
||||||
|
|
@ -195,21 +162,20 @@ than binding. Accordingly:
|
||||||
- Clients SHOULD verify a Server Card's claims against the live connection, preferring the
|
- Clients SHOULD verify a Server Card's claims against the live connection, preferring the
|
||||||
runtime values where the two disagree.
|
runtime values where the two disagree.
|
||||||
|
|
||||||
### Server Card Location
|
### Hosted Server Card Location
|
||||||
|
|
||||||
The Catalog is the discovery entrypoint, and every Catalog Entry already carries the
|
An AI Catalog entry with `url` carries the exact location where its Server Card can be
|
||||||
`url` where its Server Card can be retrieved. Clients therefore never need to _guess_ a
|
retrieved. Clients therefore never need to _guess_ a hosted Server Card's location — they
|
||||||
Server Card's location — they follow the `url` the Catalog gives them. As a result, a
|
follow the `url` the catalog gives them. As a result, a Server Card MAY be hosted at any
|
||||||
Server Card MAY be hosted at any unreserved URI.
|
unreserved URI. An entry with `data` carries the Server Card inline instead.
|
||||||
|
|
||||||
To give servers a predictable default, MCP reserves one location:
|
To give servers a predictable default, MCP reserves one location:
|
||||||
|
|
||||||
> MCP Servers MAY host their Server Card at `GET <streamable-http-url>/server-card`,
|
> MCP Servers MAY host their Server Card at `GET <streamable-http-url>/server-card`,
|
||||||
> which we reserve for this purpose, though any unreserved URI (on any domain) is valid.
|
> which we reserve for this purpose, though any unreserved URI (on any domain) is valid.
|
||||||
> MCP Servers SHOULD respect the `application/mcp-server-card+json` media type wherever
|
> MCP Servers SHOULD respect the `application/mcp-server-card+json` media type wherever
|
||||||
> they choose to host it. After a client identifies a Server Card URL from an AI Catalog
|
> they choose to host it. After a client identifies a Server Card URL from an AI Catalog,
|
||||||
> or MCP Catalog, it SHOULD request that URL expressing the `application/mcp-server-card+json`
|
> it SHOULD request that URL expressing the `application/mcp-server-card+json` media type.
|
||||||
> media type.
|
|
||||||
|
|
||||||
Concretely:
|
Concretely:
|
||||||
|
|
||||||
|
|
@ -227,12 +193,11 @@ The following placements were considered and **not** recommended:
|
||||||
|
|
||||||
- **A `.well-known` URI** (e.g., `/.well-known/mcp/server-card`). `.well-known` is for
|
- **A `.well-known` URI** (e.g., `/.well-known/mcp/server-card`). `.well-known` is for
|
||||||
_site-wide_ metadata, whereas an individual server's card is _application-level_
|
_site-wide_ metadata, whereas an individual server's card is _application-level_
|
||||||
metadata. Because the Catalog is the discovery entrypoint and already provides each
|
metadata. Because the AI Catalog already provides each hosted card's `url`, hosting the
|
||||||
card's `url`, hosting the card under `.well-known` adds no value — the card can live
|
card under `.well-known` adds no value — the card can live anywhere the catalog points.
|
||||||
anywhere the Catalog points. (Note: `.well-known` remains correct for the **Catalog**
|
`.well-known` remains correct for the AI Catalog itself at
|
||||||
itself at `/.well-known/mcp/catalog.json` and for OAuth metadata such as
|
`/.well-known/ai-catalog.json` and for OAuth metadata such as
|
||||||
`/.well-known/oauth-protected-resource` — those are genuinely site-wide. This change
|
`/.well-known/oauth-protected-resource`; those are genuinely site-wide.
|
||||||
applies only to the single-server Server Card.)
|
|
||||||
- **The bare streamable-HTTP endpoint** (`GET <streamable-http-url>` with no suffix).
|
- **The bare streamable-HTTP endpoint** (`GET <streamable-http-url>` with no suffix).
|
||||||
In the Streamable HTTP transport a `GET` on the MCP endpoint already has a reserved
|
In the Streamable HTTP transport a `GET` on the MCP endpoint already has a reserved
|
||||||
meaning — it opens the SSE stream. Serving the card there overloads that endpoint and
|
meaning — it opens the SSE stream. Serving the card there overloads that endpoint and
|
||||||
|
|
@ -250,29 +215,12 @@ The following placements were considered and **not** recommended:
|
||||||
`<streamable-http-url>` + `/server-card` — the recommended convention — not a domain-root
|
`<streamable-http-url>` + `/server-card` — the recommended convention — not a domain-root
|
||||||
`/mcp/` metadata namespace.)
|
`/mcp/` metadata namespace.)
|
||||||
|
|
||||||
## Relationship to AI Catalog
|
|
||||||
|
|
||||||
The MCP Catalog is designed as a transitional mechanism. The
|
|
||||||
[AI Catalog](https://github.com/Agent-Card/ai-catalog) specification defines a
|
|
||||||
cross-protocol discovery standard (`/.well-known/ai-catalog.json`) capable of indexing
|
|
||||||
MCP servers, A2A agents, and other AI artifacts.
|
|
||||||
|
|
||||||
MCP Catalog entries are structurally compatible with AI Catalog entries. When the AI
|
|
||||||
Catalog standard is finalized and adopted by the MCP steering committee:
|
|
||||||
|
|
||||||
1. Domains MAY serve both `/.well-known/mcp/catalog.json` and `/.well-known/ai-catalog.json`
|
|
||||||
during a transition period
|
|
||||||
2. MCP Catalog entries can be included directly in an AI Catalog document without
|
|
||||||
modification
|
|
||||||
3. Domains that want richer metadata (trust manifests, publisher identity, collections)
|
|
||||||
can adopt the full AI Catalog format
|
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
### Information Disclosure
|
### Information Disclosure
|
||||||
|
|
||||||
MCP Catalogs are publicly accessible by design. Catalog entries MUST NOT include sensitive
|
AI Catalogs and Server Cards used for public discovery are publicly accessible by design.
|
||||||
information such as:
|
They MUST NOT include sensitive information such as:
|
||||||
|
|
||||||
- Authentication credentials or tokens
|
- Authentication credentials or tokens
|
||||||
- Internal network topology or private endpoints
|
- Internal network topology or private endpoints
|
||||||
|
|
@ -291,7 +239,8 @@ treat a Server Card as authoritative and reconcile it against the live connectio
|
||||||
|
|
||||||
### CORS Requirements
|
### CORS Requirements
|
||||||
|
|
||||||
Discovery endpoints MUST include appropriate CORS headers to allow browser-based clients:
|
Hosted Server Card endpoints MUST include appropriate CORS headers to allow browser-based
|
||||||
|
clients:
|
||||||
|
|
||||||
```
|
```
|
||||||
Access-Control-Allow-Origin: *
|
Access-Control-Allow-Origin: *
|
||||||
|
|
@ -299,11 +248,11 @@ Access-Control-Allow-Methods: GET
|
||||||
Access-Control-Allow-Headers: Content-Type
|
Access-Control-Allow-Headers: Content-Type
|
||||||
```
|
```
|
||||||
|
|
||||||
This is safe because MCP Catalogs contain only public metadata and are read-only.
|
This is safe because Server Cards contain only public metadata and are read-only.
|
||||||
|
|
||||||
### Caching
|
### Caching
|
||||||
|
|
||||||
Servers SHOULD include caching headers to reduce unnecessary requests:
|
Server Card hosts SHOULD include caching headers to reduce unnecessary requests:
|
||||||
|
|
||||||
```
|
```
|
||||||
Cache-Control: public, max-age=3600
|
Cache-Control: public, max-age=3600
|
||||||
|
|
@ -311,8 +260,8 @@ Cache-Control: public, max-age=3600
|
||||||
|
|
||||||
### Transport Security
|
### Transport Security
|
||||||
|
|
||||||
MCP Catalogs MUST be served over HTTPS (TLS 1.2 or later) in production. HTTP MAY be
|
Hosted Server Cards MUST be served over HTTPS (TLS 1.2 or later) in production. HTTP MAY
|
||||||
used for local development only.
|
be used for local development only.
|
||||||
|
|
||||||
### Denial of Service
|
### Denial of Service
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue