Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,52 @@ npx mcp-server-diff -c servers.json -o diff

---

## Migration to 3.0

Version 3.0 is a maintenance release that refreshes the dependency tree and prepares the probe for the upcoming MCP draft spec.

**Dependency upgrades (breaking only at the install layer)**

- `zod` v3 → v4 (signature of `z.record` now requires explicit key + value schemas)
- `undici` v6 → v8 (also updates the `overrides` block, resolving the v6 WebSocket advisories)
- `diff` v8 → v9
- `@actions/core`, `@actions/exec`, `@actions/io` v1 → v3
- `eslint` / `@eslint/js` v9 → v10, `typescript` v5 → v6, `jest` / `@types/jest` v29 → v30
- `@types/node` v22 → v24 (the action and CLI are still tested on Node 20 + 22)
- `@vercel/ncc` 0.38 → 0.44

If you consume `mcp-server-diff` as a GitHub Action or via `npx`, nothing in your workflow should change.

**MCP draft-spec forward-compat**

The probe now strips the `ttlMs` and `cacheScope` cache hints (CacheableResult, [SEP-2461](https://modelcontextprotocol.io/specification/draft/changelog)) from the top level of `tools/list`, `prompts/list`, `resources/list`, and `resources/templates/list` results before snapshotting. These freshness hints are intended to vary between runs, so stripping them keeps diffs focused on real interface changes. Nested `ttlMs` / `cacheScope` fields (e.g. inside a tool description) are preserved.

The `initialize` snapshot is unchanged. The draft renames it to `server/discover` ([SEP-2575](https://modelcontextprotocol.io/specification/draft/changelog)), but we will adopt the new method name in a follow-up release once `@modelcontextprotocol/sdk` v2 ships.

### Cross-spec-version diffing

A common situation during the draft-spec rollout is that the base ref runs against MCP spec `2025-06-18` or `2025-11-25` while the branch runs against the draft. The tool is designed so that a server upgrading its SDK without changing its public surface produces a **clean diff**.

What gets normalized away before snapshotting:

- **CacheableResult hints** — top-level `ttlMs` / `cacheScope` on `tools/list`, `prompts/list`, `resources/list`, and `resources/templates/list` ([SEP-2461](https://modelcontextprotocol.io/specification/draft/changelog)).
- **`_meta` protocol plumbing** — a specific, exact-key denylist is stripped from every `_meta` object, at any depth: `io.modelcontextprotocol/protocolVersion`, `io.modelcontextprotocol/clientInfo`, `io.modelcontextprotocol/clientCapabilities`, `io.modelcontextprotocol/subscriptionId`, `io.modelcontextprotocol/logLevel`. An emptied `_meta` is dropped entirely. **We do not strip by prefix**: the `io.modelcontextprotocol/*` namespace is reserved by the spec but is also where official extensions live (MCP Apps' `_meta.ui`, Tasks' `io.modelcontextprotocol/related-task`, etc.) — those surfaces are exactly what this tool exists to diff, so they round-trip untouched.
- **W3C trace context inside `_meta`** — `traceparent`, `tracestate`, and `baggage` (transport-injected for OTel propagation) are stripped from `_meta`.
- **`initialize` envelope churn** — `protocolVersion` and `capabilities.experimental` are excluded from the `initialize` snapshot body. The negotiated protocol version is captured separately and surfaced by the reporter (see below).
- **Endpoint names are canonicalized** — see `CANONICAL_SNAPSHOT_NAMES` in `src/probe.ts`. When the spec eventually renames `initialize` → `server/discover`, both will map to the same `initialize` snapshot file so a spec upgrade shows up as a content diff on one file, not a "removed + added" pair.

What is **not** normalized (intentionally):

- `serverInfo.version` (the SDK version is a legitimate signal worth tracking).
- Nested `ttlMs` / `cacheScope` that live inside a tool/prompt/resource definition (those would be part of the public surface, not envelope hints).
- Any `_meta` key not on the exact denylist above — including the entire MCP Apps surface (`_meta.ui` and friends, [SEP-1865](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1865)) and vendor extensions under non-reserved namespaces (`x.acme/*`, etc.).

When the base and branch probes negotiate different MCP protocol versions, the report (and the PR summary) emit a banner like:

> ℹ️ **MCP protocol version changed:** `2025-11-25` → `draft`. Protocol-level plumbing is normalized away; any diff below reflects real public-surface changes.

If the public surface is identical the diff is empty even when the protocol version moved — the banner is the only signal.

## License

MIT License. See [LICENSE](LICENSE) for details.
Expand Down
Loading