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:
Tadas Antanavicius 2026-06-08 13:36:19 -07:00 • committed by GitHub
commit 9222cf6527
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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: