Commit graph ext-server-card/schema.json
Author SHA1 Message Date
Tadas Antanavicius
92999cb3f5
docs: drop references to the MCP initialization handshake (SEP-2575) (#37) 2026-06-27 08:37:45 +02:00
David Soria Parra
56ab90b19c
Make the extension card-only: remove registry Server/package types (#28) 2026-06-18 19:11:55 +02:00
Tadas Antanavicius
9222cf6527
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>
2026-06-08 13:36:19 -07:00
Tadas Antanavicius
10e958fa82
Server Cards: recommend /server-card over .well-known (resolves #12) (#22)
Resolves #12.

## What & why

Moves the **single-server Server Card** off a `.well-known` path. We
reserve `GET <streamable-http-url>/server-card` as the recommended
location while allowing **any unreserved URI** — the Catalog
(`/.well-known/mcp/catalog.json`) is the discovery entrypoint and
already carries each card's `url`, so clients never need to guess where
a card lives.

Adopts wording close to the resolution discussed in #12 (credit: Darrel
Miller's point that `.well-known` is for *site-wide* metadata, not
application-level metadata like an individual server's card; proposed
wording from @SamMorrowDrums and @tadasant):

> MCP Servers MAY host their Server Card at `GET
<streamable-http-url>/server-card`, which we reserve for this purpose,
though any unreserved URI is valid. MCP Servers SHOULD respect the
agreed media type wherever 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 Server Card media type.

### Content negotiation

A client fetching a Server Card requests its preferred representation
with the `Accept` header (`Accept: application/mcp-server-card+json`) —
the request-side content-negotiation header for a GET. The server echoes
the type it actually served back in the response's `Content-Type`.

> **Note on media type:** this PR is scoped to *placement* only and does
**not** perform the `application/mcp-server+json` →
`application/mcp-server-card+json` rename — that is handled in a
separate PR. So the two coexist deliberately here:
> - **Pre-existing strings** (catalog-entry description, the four
example `mediaType` values, validation prose, and the "Server Cards use
the media type" line) are left as `application/mcp-server+json`,
untouched, for the rename PR to sweep.
> - **New prose introduced by this PR** (the reserved-location
blockquote and the `Accept`-header example in the "Server Card Location"
section) names the type explicitly as the **post-rename
`application/mcp-server-card+json`**, since the rename PR's diff is
against `main` and would not touch text that doesn't yet exist there.
>
> The temporary mix resolves to a single consistent type once both PRs
land.

## Rationale for `/server-card` (also captured permanently in
`docs/discovery.md` → "Alternatives considered")

- **Not `.well-known`:** site-wide vs. application-level metadata; the
Catalog already provides the `url`, so `.well-known` adds no value for
the card.
- **Not the bare streamable-HTTP endpoint** (`GET <url>` with no
suffix): a GET there already opens the SSE stream in Streamable HTTP.
Hosting the card there overloads the connection-establishing endpoint
and forces content negotiation to disambiguate. Spec-*allowed*,
explicitly **not recommended** — this is the primary motivation for a
distinct suffix.
- **Not domain-root `/mcp/` nesting:** `/mcp` denotes the *transport
endpoint itself* in MCP's canonical-URI examples; there's no precedent
for `/mcp/` as a metadata sub-namespace, and it collides conceptually
with "the JSON-RPC endpoint."
- **Elegant consequence:** because the suffix is appended to the
streamable-HTTP URL, a server at `https://host/mcp` naturally yields
`https://host/mcp/server-card` — path-namespacing for free.

## Scope guardrail

This applies **only to the single-server Server Card**. `.well-known` is
unchanged and remains correct for the **Catalog**
(`/.well-known/mcp/catalog.json`) and OAuth metadata
(`/.well-known/oauth-protected-resource`, etc.) — those are genuinely
site-wide. The doc calls this out explicitly so it doesn't read as
"abandon `.well-known` everywhere."

## Files changed

- **`docs/discovery.md`** — new "Server Card Location" section with the
reserved convention + `Accept`-header guidance; permanent "Alternatives
considered" subsection; rewritten single- and multi-server example URLs
(`https://example.com/mcp/server-card`;
`https://acme.com/code-review/server-card`, `/docs-search/server-card`,
`/ci-cd/server-card`).
- **`schema.ts`** — updated the two `.well-known` doc comments
(`ServerCard` and `Server`) to the new convention.
- **`schema.json`** — regenerated from `schema.ts` (`npm run generate`).
- **`README.md`** — updated the "What is a Server Card?" line to the new
convention, normalizing the pre-existing README-vs-schema `.well-known`
mismatch.

## Verification

- [x] `npm run check` passes (schema.json in sync with schema.ts + `tsc
--noEmit` clean)
- [x] `npm run generate` run and regenerated `schema.json` committed in
the same change
- [x] `npm run validate` passes — all 7 examples validate against the
regenerated schema
- [x] `npm run format` run (prettier clean)
- [x] No single-server-card `.well-known` references remain in
`README.md`, `schema.ts`, `schema.json`, or `docs/discovery.md`
(verified via grep); remaining `.well-known` references are
Catalog/OAuth/the "Alternatives considered" explainer only
- [x] Pre-existing media-type strings left untouched — `git diff
origin/main` shows zero changes to existing
`application/mcp-server+json` strings (rename owned by a separate PR);
only the new placement prose names the post-rename
`application/mcp-server-card+json`
- [x] Examples updated and internally consistent with the new prose
- [x] Fresh-eyes subagent review performed; its one cosmetic finding was
addressed and CI re-confirmed green
- [x] CI green (see checks on this PR)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Agent (Claude) <peter@tadasant.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:40:51 -07:00
Tadas Antanavicius
7a3ff5d452 Bootstrap repo with TypeScript source-of-truth and generated JSON Schema
Tracks SEP-2127 (MCP Server Cards) as an experimental extension while the SEP
itself is under review. The schema.ts is structured to be lifted directly into
schema/draft/schema.ts of the main spec when Server Cards graduate -- only the
inlined MetaObject and Icon definitions need to be removed during migration.

- schema.ts: ServerCard, Server, Repository, Remote, Package, Argument, Input,
  KeyValueInput, etc. plus inlined MetaObject and Icon
- scripts/generate-schema.ts: typescript-json-schema with 2020-12 transforms
- scripts/validate-examples.ts: ajv validator over examples/{ServerCard,Server}/
- examples/: 2 valid + 4 invalid Server Card cases, 1 valid Server case
- README: status, layout, schema URL conventions, graduation plan
- CI workflow: check + validate + format on every PR
2026-04-27 19:47:01 +00:00