docs: require Server Card to be consistent with runtime behavior (#25)
Closes #23. ## What Adds the missing normative requirement that a Server Card's declared values must not contradict the server's actual runtime behavior. The schema already encodes the *omission* half of the threat model (primitives are deliberately excluded because a static manifest drifts from runtime); this adds the *complementary* requirement for the fields the card **does** declare. ### `docs/discovery.md` - **New "Consistency with Runtime Behavior" subsection** under "MCP Server Cards": - A Server Card SHOULD accurately reflect the server's actual runtime behavior. The `serverInfo` (`name`, `version`) and `supportedVersions` returned by [`server/discover`](https://modelcontextprotocol.io/specification/draft/server/discover), plus the transport actually served at each `remotes[]` endpoint (and descriptive `title`/`description`/`icons`), SHOULD NOT contradict the equivalent values declared in the Server Card. - Clients MUST NOT treat Server Card contents as authoritative for security or access-control decisions, and SHOULD verify claims against the live connection — the same posture the spec takes toward primitives, generalized to the declared fields. - **New "Server Card Accuracy" subsection** under "Security Considerations": frames an inaccurate (stale or crafted) card as a mild confusion/downgrade vector, so the consistency requirement is partly a security property, not just correctness. It is explanatory and points back to the single normative statement above rather than re-asserting the MUST NOT. ### `schema.ts` - Short pointer comment in the `ServerCard` JSDoc referencing the discovery-doc requirement, complementing the existing primitives-omission rationale. `schema.json` regenerated via `npm run generate` in the same commit (CI `check` enforces sync). RFC 2119 keywords are used consistently with the rest of `docs/discovery.md`. The runtime reference is phrased around the draft spec's [`server/discover`](https://modelcontextprotocol.io/specification/draft/server/discover) operation — the pre-`initialize` exchange that actually returns `serverInfo`/`supportedVersions` — rather than SEP-2127's `DiscoverResultResponse`, which has no term in this repo. ## Security rationale Because a Server Card is consumed *before* the client connects, an inaccurate card can steer a client toward a weaker configuration (downgrade) or a misidentified server (confusion) before it has observed the server's actual `server/discover` response. Treating the card as advisory rather than authoritative contains this risk. ## Scope Small and focused strictly on the card-vs-runtime consistency requirement. Distinct from #10 (the inverse — primitives the card omits) and #13/#9/#11/#12/#14/#21 (none touch card-vs-runtime consistency). No unrelated changes. ## Verification - [x] `npm run check` passes — `schema.json` is in sync with `schema.ts` and `tsc --noEmit` is clean - [x] `npm run validate` passes — all 7 examples validate against the regenerated schema - [x] `npm run format` clean — no formatting changes left - [x] `schema.json` regenerated and committed alongside the `schema.ts` change (CI `check` enforces this) - [x] Fresh-eyes review by a separate agent — feedback addressed (full-term "Server Card" wording for consistency with the rest of the doc; de-duplicated the MUST NOT so the security section is explanatory and points to the single normative statement) - [x] Maintainer review (Sam) — substance approved; applied the requested brevity pass, tightening both new subsections with no change to any normative statement - [x] `server/discover` spec link verified to resolve to a real draft-spec page documenting `serverInfo` / `supportedVersions` - [x] CI green on the final commit (`0d0389b`) — build, Analyze (actions), and CodeQL all pass This is a docs/comment-only change (no runtime code paths), so the verification is the build/schema toolchain rather than e2e tests. Local toolchain output: ``` ✓ Generated schema.json ✓ schema.json is up to date (npm run check) All 7 example(s) passed. (npm run validate) ``` Opened for human review — **not** merged, per repo policy. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bd9bcce280
commit
9222cf6527
3 changed files with 37 additions and 1 deletions
|
|
@ -150,6 +150,25 @@ A Server Card includes:
|
|||
For the full Server Card specification, see
|
||||
[SEP-2127: MCP Server Cards](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127).
|
||||
|
||||
### Consistency with Runtime Behavior
|
||||
|
||||
A Server Card is fetched _before_ the client connects, so its contents are unverified when
|
||||
read. A Server Card SHOULD accurately reflect the server's runtime behavior: the values a
|
||||
client observes once connected — the `serverInfo` (`name`, `version`) and `supportedVersions`
|
||||
from [`server/discover`](https://modelcontextprotocol.io/specification/draft/server/discover),
|
||||
the transport served at each `remotes[]` endpoint, and descriptive fields (`title`,
|
||||
`description`, `icons`) — SHOULD NOT contradict the equivalent values declared in the Server
|
||||
Card.
|
||||
|
||||
As with the deliberately omitted primitives (tools, resources, prompts), a static manifest
|
||||
can drift from runtime, so even the fields a Server Card does declare are advisory rather
|
||||
than binding. Accordingly:
|
||||
|
||||
- Clients MUST NOT treat Server Card contents as authoritative for security or
|
||||
access-control decisions.
|
||||
- Clients SHOULD verify a Server Card's claims against the live connection, preferring the
|
||||
runtime values where the two disagree.
|
||||
|
||||
### Server Card Location
|
||||
|
||||
The Catalog is the discovery entrypoint, and every Catalog Entry already carries the
|
||||
|
|
@ -233,6 +252,17 @@ information such as:
|
|||
- Internal network topology or private endpoints
|
||||
- Proprietary business logic
|
||||
|
||||
### Server Card Accuracy
|
||||
|
||||
A Server Card is consumed before the client connects, so an inaccurate one — stale or
|
||||
deliberately crafted — is a mild confusion or downgrade vector: one that overstates
|
||||
transport or protocol-version support, or misrepresents the server's identity, can steer a
|
||||
client toward a weaker configuration or the wrong server before it observes the actual
|
||||
`server/discover` response. This makes the consistency requirement partly a security
|
||||
property, not merely a matter of correctness. The normative protections live in
|
||||
[Consistency with Runtime Behavior](#consistency-with-runtime-behavior): clients do not
|
||||
treat a Server Card as authoritative and reconcile it against the live connection.
|
||||
|
||||
### CORS Requirements
|
||||
|
||||
Discovery endpoints MUST include appropriate CORS headers to allow browser-based clients:
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@
|
|||
"type": "object"
|
||||
},
|
||||
"ServerCard": {
|
||||
"description": "A static metadata document describing a remote MCP server, suitable for\npre-connection discovery. A Server Card MAY be hosted at any unreserved URI;\nMCP reserves `GET <streamable-http-url>/server-card` as the recommended\nlocation. Clients learn a card's URL from an MCP/AI Catalog rather than\nguessing it.\n\nServer Cards intentionally describe only what is needed to discover and\nconnect to a remote server: identity, transport, and protocol versions.\nThey do not enumerate primitives (tools, resources, prompts) — those remain\nsubject to runtime listing via the protocol's standard list operations.\n\nThe companion {@link Server} shape is a strict superset that adds local\npackage metadata for use cases like the MCP Registry's `server.json`.",
|
||||
"description": "A static metadata document describing a remote MCP server, suitable for\npre-connection discovery. A Server Card MAY be hosted at any unreserved URI;\nMCP reserves `GET <streamable-http-url>/server-card` as the recommended\nlocation. Clients learn a card's URL from an MCP/AI Catalog rather than\nguessing it.\n\nServer Cards intentionally describe only what is needed to discover and\nconnect to a remote server: identity, transport, and protocol versions.\nThey do not enumerate primitives (tools, resources, prompts) — those remain\nsubject to runtime listing via the protocol's standard list operations.\n\nThe fields a Server Card does declare (identity, transport, protocol\nversions) are advisory, not authoritative: they SHOULD be consistent with\nthe server's `server/discover` response, and clients MUST NOT treat them as\nauthoritative for security decisions. See \"Consistency with Runtime\nBehavior\" in docs/discovery.md for the normative requirement.\n\nThe companion {@link Server} shape is a strict superset that adds local\npackage metadata for use cases like the MCP Registry's `server.json`.",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"description": "The Server Card JSON Schema URI that this document conforms to. Required.\n\nMust be a `/v1/` URL under `static.modelcontextprotocol.io/schemas/`,\nnaming a Server Card / `server.json` schema (e.g.,\n`https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json`\nor `https://static.modelcontextprotocol.io/schemas/v1/server.schema.json`).\nSchema URLs are versioned by the `vN` segment rather than by date so that\nminor, additive revisions of the v1 shape don't bump every published\ndocument's `$schema` URL.",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@
|
|||
* They do not enumerate primitives (tools, resources, prompts) — those remain
|
||||
* subject to runtime listing via the protocol's standard list operations.
|
||||
*
|
||||
* The fields a Server Card does declare (identity, transport, protocol
|
||||
* versions) are advisory, not authoritative: they SHOULD be consistent with
|
||||
* the server's `server/discover` response, and clients MUST NOT treat them as
|
||||
* authoritative for security decisions. See "Consistency with Runtime
|
||||
* Behavior" in docs/discovery.md for the normative requirement.
|
||||
*
|
||||
* The companion {@link Server} shape is a strict superset that adds local
|
||||
* package metadata for use cases like the MCP Registry's `server.json`.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue