mcp-deprecate
v0.1.0
Published
Scan an MCP server for protocol features deprecated or removed by the 2026-07-28 spec release candidate, and emit a dated migration checklist. CLI + GitHub Action + MCP server.
Maintainers
Readme
mcp-deprecate
Scan an MCP server for protocol features deprecated or removed by the 2026-07-28 specification release candidate, and get a dated migration checklist.
The 2026-07-28 RC is the largest revision of the Model Context Protocol since launch — it makes the protocol stateless, removes the initialize handshake and Mcp-Session-Id, and deprecates Roots, Sampling, and Logging. The spec ships the deprecation list as prose with a 12-month feature-lifecycle policy, but no tooling to find where your code is affected. mcp-deprecate is that tooling.
It ships as three surfaces over one rule engine: a CLI, a GitHub Action CI gate, and an MCP server.
What it flags
Two severities, and the difference matters:
| Severity | Meaning | Examples |
|---|---|---|
| 🔴 error | Removed in the RC — breaking against a final-spec server | Mcp-Session-Id, the initialize handshake, logging/setLevel, resources/subscribe, tasks/list, error code -32002, server-initiated sampling/createMessage (and others — run mcp-deprecate --list-rules) |
| 🟡 warning | Deprecated — still works, removable no sooner than 12 months out (HTTP+SSE transport is shorter — 3 months after the RC reaches Final) | Roots, Sampling, Logging, the HTTP+SSE transport, includeContext: "thisServer"/"allServers" |
It also prints a manual-review checklist for changes that have no reliable static signature (e.g. the new required resultType field, server/discover, CacheableResult fields). Every rule cites its originating SEP.
Install
npm install -g mcp-deprecate
# or run without installing:
npx mcp-deprecate ./src1. CLI
mcp-deprecate [path] [options]$ mcp-deprecate ./src # line:column and migration text below are illustrative
...
ERROR 12:3 initialize / notifications/initialized handshake [mcp-2575-initialize · SEP-2575]
MCP is now stateless: the initialize / notifications/initialized handshake is removed.
→ Carry protocol version, client identity, and capabilities in _meta on every request.
Summary: 1 error(s), 0 warning(s) across 1 file(s).| Option | Default | Description |
|---|---|---|
| -f, --format <fmt> | text | text, md, or json |
| --fail-on <lvl> | error | Non-zero-exit threshold: error (default), warning (errors count too), or none (always exits 0) |
| --no-advisories | | Hide the manual-review checklist |
| --list-rules | | Print the full rule table and exit |
| -v, --version | | Print version and exit |
| -h, --help | | Print this help and exit |
Generate a migration document:
mcp-deprecate . --format md --fail-on none > MIGRATION.md # --fail-on none = always exit 0Exit codes: 0 clean · 1 findings at/above --fail-on · 2 usage/scan error.
2. GitHub Action
Gate pull requests so no one reintroduces a removed feature:
# .github/workflows/mcp-compat.yml
name: MCP compat
on: [pull_request]
jobs:
mcp-deprecate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shurugiken/mcp-deprecate@v1 # pin to a released tag
with:
path: "src"
fail-on: "error" # use "warning" for a strict gateInputs: path (default .), fail-on (error/warning/none, default error), format (text/md/json, default text), version (npm dist-tag or version, default latest), advisories (true/false, default true).
3. MCP server
Security: the server reads any file path its client requests — it has no path allow-list. Connect it only to trusted MCP clients.
Run mcp-deprecate itself as an MCP server so an agent can lint other MCP servers:
{
"mcpServers": {
"mcp-deprecate": {
"command": "npx",
"args": ["-y", "--package", "mcp-deprecate", "mcp-deprecate-server"]
}
}
}Tools: scan_source(path, format?) (format defaults to md), check_spec_compat(path), list_rules().
Library
import { scanPath, render, summarize } from "mcp-deprecate";
const findings = await scanPath("./src");
console.log(render(findings, { format: "md" }));
console.log(summarize(findings)); // { errors, warnings, total, filesWithFindings }How it works & limitations
Detection is signature-based: distinctive protocol method strings (logging/setLevel), headers (Mcp-Session-Id), error codes (-32002), and official TypeScript SDK schema symbols (InitializeRequestSchema). This favors precision over recall — a clean scan is not a proof of compatibility, only the absence of known high-signal markers. Scans .ts/.tsx/.js/.jsx/.mjs/.cjs/.py/.json.
Known limits: comments are stripped before matching, but only single-line ones — Python triple-quoted docstrings and multi-line JS/TS template literals aren't tracked (tokens mentioned inside them are still matched); method values passed through a variable rather than a string literal, and values split across lines (includeContext: with the value on the next line), are not detected; rules keyed to TypeScript SDK symbols (e.g. InitializeRequestSchema) won't fire on Python servers or hand-built raw JSON-RPC strings; symlinks to files are followed but symlinked directories are not; an explicitly-passed file over 2 MB errors, and files over 2 MB are skipped during directory walks; the directory walk also skips node_modules, dist, build, out, coverage, vendor, __pycache__, and any hidden directory (names starting with ., e.g. .next, .turbo); the programmatic library entrypoint is ESM-only (require() works on Node 22.12+; on Node 20 import it as ESM or via dynamic import()) — the CLI and GitHub Action are unaffected and run on Node ≥20. The MCP server reads whatever path its client asks for, so grant it only to trusted clients.
Roadmap: AST-based call-site analysis (ts-morph), deeper Python SDK coverage, and a runtime prober that boots a server and checks advertised capabilities against the RC.
Sources
- MCP 2026-07-28 release candidate announcement
- Draft specification changelog
- Deprecated features registry
License
MIT © Kwashawn Warren
