{
  "id": "mcp-2026-07-28-migration",
  "type": "loadout",
  "title": "Migrate an MCP server to protocol 2026-07-28",
  "summary": "Move an MCP server to the stateless 2026-07-28 revision on the official SDKs: no sessions or handshake, server/discover, input_required, then a raw stdio probe.",
  "description": "MCP revision 2026-07-28 removed the initialize handshake and sessions. Every request now carries its own _meta envelope, and a tool asks the user for input by returning input_required instead of pushing a request mid-call. Upgrading the SDK is not enough on its own: a TypeScript v2 server still speaks 2025 until it is served through serveStdio, and session-keyed state or elicitInput calls survive the official codemod. This loadout gives your agent the changelog as a fix list, plus a scanner for removed patterns and a dependency-free stdio probe. The probe sends server/discover and stateless calls, runs one input_required round trip, and checks -32022 for unknown versions and dual-era fallback. The verifier builds a TypeScript v2 server and probes it alongside a Python SDK 2.3.0 server.\n",
  "highlights": [
    "Changes checked against the official 2026-07-28 changelog and spec pages",
    "Scanner for sessions, pushed elicitation and sampling, setLevel, subscribe and v1 wiring",
    "stdio probe: server/discover, resultType, stable tools/list, -32022, dual-era check",
    "input_required round trip in place of elicitInput, with a working TypeScript v2 example",
    "Exact pins: @modelcontextprotocol/server 2.3.1, mcp 2.3.0, go-sdk v1.8.0"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "credits": [
    {
      "title": "MCP specification 2026-07-28: key changes",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/changelog"
    },
    {
      "title": "MCP specification 2026-07-28: versioning and backward compatibility",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning"
    },
    {
      "title": "MCP specification 2026-07-28: multi-round-trip requests",
      "url": "https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr"
    },
    {
      "title": "MCP TypeScript SDK: supporting protocol revision 2026-07-28",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration/support-2026-07-28.md"
    },
    {
      "title": "MCP TypeScript SDK: upgrade-to-v2 skill and codemod",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk/tree/main/skills/mcp-typescript-sdk-upgrade-to-v2"
    }
  ],
  "categories": [
    "ai-agents"
  ],
  "stacks": [
    "mcp",
    "typescript",
    "nodejs",
    "python"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Rules for moving an MCP server to the stateless 2026-07-28 revision through the official SDKs.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "antigravity-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/AGENTS.md",
      "bytes": 1732,
      "lang": "markdown",
      "content": "# AGENTS.md: MCP server migrating to protocol 2026-07-28\n\nThis MCP server is moving to protocol revision 2026-07-28: stateless requests with a per-request `_meta` envelope,\n`server/discover`, and `input_required` results instead of server-to-client requests. The spec at\nhttps://modelcontextprotocol.io/specification/2026-07-28 is the authority. Do not rely on memory of older revisions,\nwhich had sessions and an `initialize` handshake.\n\n## How to work\n- Start with the `mcp-2026-migration` skill. Scan first, then change code, then prove it with the skill's probe\n  against the built server.\n- Upgrade through the official SDK, never by hand-writing JSON-RPC: TypeScript `@modelcontextprotocol/server`\n  2.3.1, Python `mcp==2.3.0`, Go `github.com/modelcontextprotocol/go-sdk` v1.8.0. Pin exactly and commit the\n  lockfile.\n- TypeScript v1 (`@modelcontextprotocol/sdk`): run the official codemod first, then fix what it marks.\n- Keep 2025-era clients working (dual-era) unless the user decides otherwise. Ask before going modern-only.\n\n## Rules\n- No state keyed on a session or connection. Anything a later call needs goes back to the client as a handle or,\n  within one call, as signed `requestState`.\n- `inputResponses` and `requestState` come from the client: validate them, and never let them grant access.\n- stdout is the protocol on stdio. Logs go to stderr.\n- Register tools in a fixed order. Return `-32602` for an unknown resource.\n- Destructive tools ask for confirmation through `input_required`, not by assuming the client will prompt.\n\n## Done means\n- The skill's scanner is clean, or each remaining finding is explained.\n- The probe prints `RESULT: speaks 2026-07-28`.\n- Type-check, lint and the existing tests pass.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/CLAUDE.md",
      "bytes": 408,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n\n- Skills from this loadout live in `.claude/skills/`: `mcp-2026-migration`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Migrate an MCP server to protocol 2026-07-28\" v1.0.0 — https://loadout.jnw-it.com/loadouts/mcp-2026-07-28-migration/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/GEMINI.md",
      "bytes": 409,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n\n- Skills from this loadout live in `.agents/skills/`: `mcp-2026-migration`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Migrate an MCP server to protocol 2026-07-28\" v1.0.0 — https://loadout.jnw-it.com/loadouts/mcp-2026-07-28-migration/ -->\n"
    },
    {
      "path": "skills/mcp-2026-migration/SKILL.md",
      "role": "skill",
      "description": "Scan, upgrade, rewrite removed patterns and probe the result.",
      "skill": "mcp-2026-migration",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/mcp-2026-migration/SKILL.md",
        "codex": ".agents/skills/mcp-2026-migration/SKILL.md",
        "cursor": ".agents/skills/mcp-2026-migration/SKILL.md",
        "gemini-cli": ".agents/skills/mcp-2026-migration/SKILL.md",
        "antigravity-cli": ".agents/skills/mcp-2026-migration/SKILL.md",
        "copilot": ".agents/skills/mcp-2026-migration/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/skills/mcp-2026-migration/SKILL.md",
      "bytes": 4727,
      "lang": "markdown",
      "content": "---\nname: mcp-2026-migration\ndescription: Move an MCP server to protocol revision 2026-07-28 (stateless requests, server/discover, input_required) on the official TypeScript, Python or Go SDK, then prove it with a raw stdio probe. Use when upgrading an MCP SDK, removing sessions or initialize handling, or when a 2026 client cannot talk to the server.\n---\n\n# Migrate an MCP server to 2026-07-28\n\nRevision 2026-07-28 makes MCP stateless: no `initialize` handshake, no sessions, every request carries its own\n`_meta` envelope, and servers can no longer push requests to the client mid-call. `references/changes.md` lists\nevery change with its fix. Read it before editing.\n\n## 1. Find what has to change\n\n```bash\nnode scripts/scan_mcp_legacy.mjs .\n```\n\nEach finding names the line, what the revision changed and the fix. Lockfiles, `node_modules`, `dist` and comments\nare skipped. Work through the findings; do not delete a feature to make the scan clean without asking.\n\n## 2. Upgrade the SDK (exact pins)\n\n| SDK | 2026-07-28 from | Pin | Serving entry |\n| --- | --- | --- | --- |\n| TypeScript | v2 packages | `@modelcontextprotocol/server@2.3.1` | `serveStdio(() => buildServer())`, `createMcpHandler(factory)` for HTTP |\n| Python | `mcp` 2.x | `mcp==2.3.0` | `MCPServer(...).run()`, dual-era by default |\n| Go | go-sdk v1.7.0 | `github.com/modelcontextprotocol/go-sdk@v1.8.0` | `mcp.NewServer` + `server.Run(ctx, &mcp.StdioTransport{})` |\n\nTypeScript v1 (`@modelcontextprotocol/sdk`) never speaks 2026-07-28. Run the official codemod at the package root,\nthen fix what it marks:\n\n```bash\nnpx @modelcontextprotocol/codemod@2.3.1 v1-to-v2 .\ngrep -rn '@mcp-codemod-error' . --exclude-dir=node_modules\nnpx tsc --noEmit\n```\n\nOn TypeScript v2 a server wired with `server.connect(new StdioServerTransport())` still speaks only the 2025 era.\nSwitching to `serveStdio(buildServer)` is the opt-in. `references/server.ts` is a complete example.\n\n## 3. Rewrite the removed patterns\n\n- **Session state** becomes explicit: return a handle (an id the server minted) and take it back as a tool\n  argument. Inside one multi-round-trip call, carry state in `requestState`, and sign it (the TypeScript SDK's\n  `createRequestStateCodec`), because it comes back from the client.\n- **elicitInput / createMessage / listRoots** inside a tool become an `input_required` result. The handler returns\n  `inputRequired({ inputRequests: { key: inputRequired.elicit({...}) } })`, the client retries the same call with\n  `inputResponses`, and the handler runs again and reads the answer (`acceptedContent(...)` in TypeScript).\n  Treat `inputResponses` as untrusted and validate them.\n- **Logging**: write to stderr. `logging/setLevel` is gone; a modern request may carry\n  `io.modelcontextprotocol/logLevel`, and when it does not, protocol log notifications are not sent.\n- **Subscriptions**: `resources/subscribe` becomes `subscriptions/listen`.\n- **Errors**: unknown resource is `-32602`. Unsupported version is `-32022`, and the SDK sends it.\n- **Ordering**: register tools in a fixed order. `tools/list` must be deterministic.\n\nKeep 2025-era clients working unless the user says otherwise. All three SDKs serve both eras from one server by\ndefault. Modern-only is an opt-in (`{ legacy: 'reject' }` in TypeScript).\n\n## 4. Prove it on the wire\n\nBuild, then probe the real server command over stdio:\n\n```bash\nnode scripts/probe_mcp_2026.mjs -- node dist/server.js\nnode scripts/probe_mcp_2026.mjs --call delete_note '{\"id\":\"n1\"}' \\\n  --answer '{\"confirm\":{\"action\":\"accept\",\"content\":{\"confirm\":true}}}' -- node dist/server.js\n```\n\nThe probe sends `server/discover` and stateless `tools/list` calls with the `_meta` envelope. It checks `resultType`,\n`serverInfo`, stable tool order, `-32022` for an unknown version, and that stdout is pure JSON-RPC. It runs one\n`input_required` round trip if asked, and reports whether a legacy `initialize` still works (dual-era) or not. `FAIL`\nlines are MUST-level and the exit code is 1.\n\nOn an established stdio connection the official TypeScript and Go SDKs pin the era, so a request with an unknown\nversion mid-connection is served rather than rejected. The probe reports that as `WARN`. The Go SDK v1.8.0 also\nanswers an unknown-version opening `server/discover` with method-not-found, not `-32022`, and the probe fails it.\nThat is SDK behaviour: note it in the PR and check the go-sdk issue tracker. Do not hand-roll the wire format.\n\n## Done means\n\n- The scan is clean, or every remaining finding is explained in the PR.\n- The SDK is pinned exactly and the lockfile is committed.\n- The probe prints `RESULT: speaks 2026-07-28`.\n- The existing tests pass, including legacy-client tests if the server stays dual-era.\n"
    },
    {
      "path": "skills/mcp-2026-migration/references/changes.md",
      "role": "reference",
      "description": "Every 2026-07-28 change that affects servers, with the fix.",
      "skill": "mcp-2026-migration",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/mcp-2026-migration/references/changes.md",
        "codex": ".agents/skills/mcp-2026-migration/references/changes.md",
        "cursor": ".agents/skills/mcp-2026-migration/references/changes.md",
        "gemini-cli": ".agents/skills/mcp-2026-migration/references/changes.md",
        "antigravity-cli": ".agents/skills/mcp-2026-migration/references/changes.md",
        "copilot": ".agents/skills/mcp-2026-migration/references/changes.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/skills/mcp-2026-migration/references/changes.md",
      "bytes": 3364,
      "lang": "markdown",
      "content": "# MCP 2026-07-28: what changed for servers\n\nSource: the official changelog at https://modelcontextprotocol.io/specification/2026-07-28/changelog and the\nversioning, discover, multi-round-trip and stdio pages of the same spec. Check there before relying on a detail.\n\n## Removed or replaced\n\n| 2025-11-25 and earlier | 2026-07-28 | Server action |\n| --- | --- | --- |\n| `initialize` / `notifications/initialized` handshake | Every request carries `_meta` with `io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities` (clientInfo SHOULD) | Use the SDK's 2026 entry point; no handshake code |\n| Version negotiated once | Per request; unsupported version gets `UnsupportedProtocolVersionError` `-32022` with `data.supported` | Let the SDK answer |\n| (none) | `server/discover` returns `supportedVersions`, `capabilities`, `instructions`, serverInfo in result `_meta` | Servers MUST implement it (SDKs do) |\n| `Mcp-Session-Id` and session state | No sessions | Handles in tool arguments; `requestState` within one call |\n| Server-to-client `sampling/createMessage`, `elicitation/create`, `roots/list` | `InputRequiredResult` (`resultType: \"input_required\"`, `inputRequests`, optional `requestState`); client retries with `inputResponses` | Only on `tools/call`, `prompts/get`, `resources/read` |\n| `ping` | Removed | Delete handlers |\n| `logging/setLevel` | Per-request `io.modelcontextprotocol/logLevel`; logging deprecated | stderr or OpenTelemetry |\n| `notifications/roots/list_changed` | Removed (roots deprecated) | Paths as tool arguments |\n| `resources/subscribe`, `resources/unsubscribe`, HTTP GET stream | `subscriptions/listen` | Move listeners |\n| SSE resumability (`Last-Event-ID`) | Removed | Drop event stores |\n| Tasks in core | `io.modelcontextprotocol/tasks` extension | Advertise under `capabilities.extensions` |\n| `notifications/elicitation/complete` | Removed | Delete |\n\n## Changed\n\n- Every result has `resultType`: `\"complete\"` or `\"input_required\"`. Clients treat a missing field as complete.\n- `tools/list` (and other lists) must come back in a deterministic order.\n- List and read results may carry `ttlMs` and `cacheScope` cache hints.\n- Resource not found: `-32602` (was `-32002`).\n- New error codes: HeaderMismatch `-32020`, MissingRequiredClientCapability `-32021`, UnsupportedProtocolVersion\n  `-32022`.\n- Streamable HTTP: `MCP-Protocol-Version`, `Mcp-Method` and `Mcp-Name` headers; servers validate that they match\n  the body.\n- `capabilities.extensions` advertises optional extensions by prefixed identifier.\n- Auth: clients validate `iss` (RFC 9207); Client ID Metadata Documents are preferred over dynamic client\n  registration, which is deprecated.\n\n## Deprecated (still on the wire for now)\n\nRoots, sampling, protocol logging, the HTTP+SSE transport, `includeContext` values `thisServer`/`allServers`, and\ndynamic client registration.\n\n## Backward compatibility\n\nA dual-era server answers `initialize` with legacy semantics for that stdio process or HTTP session, and serves\nrequests that carry modern `_meta` statelessly. A modern-only server SHOULD name its supported versions in the error\nit returns to `initialize`, because legacy clients have no other diagnostic. On stdio, dual-era clients probe with\n`server/discover` first and fall back on any error that is not a recognised modern error.\n"
    },
    {
      "path": "skills/mcp-2026-migration/references/server.ts",
      "role": "reference",
      "description": "TypeScript v2 dual-era stdio server with an input_required tool.",
      "skill": "mcp-2026-migration",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/mcp-2026-migration/references/server.ts",
        "codex": ".agents/skills/mcp-2026-migration/references/server.ts",
        "cursor": ".agents/skills/mcp-2026-migration/references/server.ts",
        "gemini-cli": ".agents/skills/mcp-2026-migration/references/server.ts",
        "antigravity-cli": ".agents/skills/mcp-2026-migration/references/server.ts",
        "copilot": ".agents/skills/mcp-2026-migration/references/server.ts"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/skills/mcp-2026-migration/references/server.ts",
      "bytes": 3834,
      "lang": "markdown",
      "content": "// A stdio MCP server on the TypeScript SDK v2 that speaks protocol 2026-07-28\n// and still serves 2025-era clients from the same factory.\n// Before (v1): new McpServer(...) + server.connect(new StdioServerTransport())\n// After  (v2): serveStdio(() => buildServer())\nimport type { CallToolResult, InputRequiredResult } from \"@modelcontextprotocol/server\";\nimport { acceptedContent, inputRequired, McpServer } from \"@modelcontextprotocol/server\";\nimport { serveStdio } from \"@modelcontextprotocol/server/stdio\";\nimport * as z from \"zod/v4\";\n\nconst GLOSSARY: Record<string, { definition: string; related: string[] }> = {\n  loadout: { definition: \"A bundle of agent instructions, skills and config for one kind of project.\", related: [\"skill\", \"fixture\"] },\n  skill: { definition: \"A folder with a SKILL.md that an agent loads when the task matches.\", related: [\"loadout\"] },\n  fixture: { definition: \"A small sample project the verifier runs a loadout against.\", related: [\"loadout\"] },\n};\n\n// Notes live in the process for the demo. No state is keyed on a session:\n// 2026-07-28 has no sessions, so anything a later call needs is passed back in\n// the tool's own arguments (here, the note id).\nconst notes = new Map<string, string>([[\"n1\", \"Rotate the staging API key\"], [\"n2\", \"Book the retro\"]]);\n\nconst confirmSchema = z.object({ confirm: z.boolean().meta({ title: \"Delete this note?\" }) });\n\nfunction buildServer(): McpServer {\n  const server = new McpServer(\n    { name: \"notes\", version: \"1.0.0\" },\n    { capabilities: { tools: {} }, instructions: \"Glossary lookups and a small notes list.\" },\n  );\n\n  // Tools are registered in a fixed order; tools/list order must be deterministic.\n  server.registerTool(\n    \"lookup_term\",\n    {\n      description: \"Look up a glossary term. Returns the definition and related terms. Read-only.\",\n      inputSchema: z.object({ term: z.string().min(1).max(64).describe(\"Glossary term, case-insensitive\") }),\n      annotations: { readOnlyHint: true },\n    },\n    async ({ term }): Promise<CallToolResult> => {\n      const entry = GLOSSARY[term.toLowerCase()];\n      if (!entry) {\n        return { isError: true, content: [{ type: \"text\", text: `Unknown term \"${term}\". Known: ${Object.keys(GLOSSARY).join(\", \")}` }] };\n      }\n      return { content: [{ type: \"text\", text: JSON.stringify({ term, ...entry }) }] };\n    },\n  );\n\n  // v1 called ctx.mcpReq.elicitInput() here (a server-to-client request). That\n  // channel is gone in 2026-07-28: return input_required and let the client\n  // retry the same call with the answer (multi-round-trip requests).\n  server.registerTool(\n    \"delete_note\",\n    {\n      description: \"Delete a note by id after the user confirms. Destructive.\",\n      inputSchema: z.object({ id: z.string().min(1).max(16).describe(\"Note id, for example n1\") }),\n      annotations: { destructiveHint: true },\n    },\n    async ({ id }, ctx): Promise<CallToolResult | InputRequiredResult> => {\n      const text = notes.get(id);\n      if (text === undefined) {\n        return { isError: true, content: [{ type: \"text\", text: `No note \"${id}\". Ids: ${[...notes.keys()].join(\", \")}` }] };\n      }\n      const answer = acceptedContent(ctx.mcpReq.inputResponses, \"confirm\", confirmSchema);\n      if (answer === undefined) {\n        return inputRequired({\n          inputRequests: { confirm: inputRequired.elicit({ message: `Delete \"${text}\"?`, requestedSchema: confirmSchema }) },\n        });\n      }\n      if (!answer.confirm) return { content: [{ type: \"text\", text: `Kept ${id}.` }] };\n      notes.delete(id);\n      return { content: [{ type: \"text\", text: `Deleted ${id}.` }] };\n    },\n  );\n\n  return server;\n}\n\nconst handle = serveStdio(buildServer);\nconsole.error(\"notes MCP server on stdio (2026-07-28 and 2025-era clients)\");\nprocess.on(\"SIGINT\", () => void handle.close());\n"
    },
    {
      "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
      "role": "reference",
      "description": "Dependency-free stdio probe for 2026-07-28 behaviour.",
      "skill": "mcp-2026-migration",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
        "codex": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
        "cursor": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
        "gemini-cli": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
        "antigravity-cli": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
        "copilot": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
      "bytes": 8431,
      "lang": "markdown",
      "content": "#!/usr/bin/env node\n// Probe a stdio MCP server for protocol revision 2026-07-28 behaviour.\n// Usage: node probe_mcp_2026.mjs [--call <tool> '<json args>' [--answer '<inputResponses json>']] -- <command> [args...]\n// --answer: if the call returns input_required, retry once with these inputResponses\n// (the probe declares the elicitation capability on the call so the server may ask).\n// No dependencies. Exit 1 if a MUST-level check fails.\nimport { spawn } from \"node:child_process\";\n\nconst MODERN = \"2026-07-28\";\nconst argv = process.argv.slice(2);\nconst sep = argv.indexOf(\"--\");\nif (sep === -1 || sep === argv.length - 1) {\n  console.error(\"usage: probe_mcp_2026.mjs [--call <tool> '<json>'] -- <command> [args...]\");\n  process.exit(2);\n}\nconst opts = argv.slice(0, sep);\nconst cmd = argv.slice(sep + 1);\nlet call = null;\nconst ci = opts.indexOf(\"--call\");\nif (ci !== -1) call = { name: opts[ci + 1], args: JSON.parse(opts[ci + 2] ?? \"{}\") };\nconst ai = opts.indexOf(\"--answer\");\nconst answer = ai !== -1 ? JSON.parse(opts[ai + 1]) : null;\n\nconst envelope = (version = MODERN, caps = {}) => ({\n  \"io.modelcontextprotocol/protocolVersion\": version,\n  \"io.modelcontextprotocol/clientInfo\": { name: \"probe-mcp-2026\", version: \"1.0.0\" },\n  \"io.modelcontextprotocol/clientCapabilities\": caps,\n});\n\nlet failures = 0;\nconst pass = (m) => console.log(`PASS ${m}`);\nconst fail = (m) => { failures++; console.log(`FAIL ${m}`); };\nconst warn = (m) => console.log(`WARN ${m}`);\nconst info = (m) => console.log(`INFO ${m}`);\n\nfunction startServer() {\n  const child = spawn(cmd[0], cmd.slice(1), { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n  const pending = new Map();\n  const nonJson = [];\n  let buf = \"\";\n  child.stdout.setEncoding(\"utf8\");\n  child.stdout.on(\"data\", (chunk) => {\n    buf += chunk;\n    let i;\n    while ((i = buf.indexOf(\"\\n\")) !== -1) {\n      const line = buf.slice(0, i).trim();\n      buf = buf.slice(i + 1);\n      if (!line) continue;\n      let msg;\n      try { msg = JSON.parse(line); } catch { nonJson.push(line); continue; }\n      if (msg.id !== undefined && pending.has(msg.id)) {\n        pending.get(msg.id)(msg);\n        pending.delete(msg.id);\n      }\n    }\n  });\n  child.stderr.resume();\n  let next = 1;\n  const request = (method, params, timeoutMs = 15000) =>\n    new Promise((resolve) => {\n      const id = next++;\n      const timer = setTimeout(() => { pending.delete(id); resolve({ timeout: true }); }, timeoutMs);\n      pending.set(id, (msg) => { clearTimeout(timer); resolve(msg); });\n      child.stdin.write(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }) + \"\\n\");\n    });\n  const stop = () => { child.stdin.end(); setTimeout(() => child.kill(), 500).unref(); };\n  return { request, stop, nonJson };\n}\n\nfunction checkMismatch(label, msg, onMiss = fail) {\n  if (msg.error?.code === -32022 && Array.isArray(msg.error.data?.supported))\n    pass(`${label} -> -32022, supported ${JSON.stringify(msg.error.data.supported)}`);\n  else {\n    const got = msg.timeout ? \"no answer\" : JSON.stringify(msg.error ?? { result: Object.keys(msg.result ?? {}) });\n    onMiss(`${label}: expected error -32022 with data.supported, got ${got.slice(0, 160)}`);\n  }\n}\n\nconst names = (msg) => (msg.result?.tools ?? []).map((t) => t.name);\n\nasync function main() {\n  const s = startServer();\n\n  // 1. server/discover (servers MUST implement it)\n  const d = await s.request(\"server/discover\", { _meta: envelope() });\n  if (d.timeout) fail(\"server/discover: no answer (a legacy-only server, or it waits for initialize)\");\n  else if (d.error) fail(`server/discover: error ${d.error.code} ${d.error.message}`);\n  else {\n    const r = d.result;\n    if (r.resultType === \"complete\") pass(\"server/discover: resultType complete\");\n    else fail(`server/discover: resultType is ${JSON.stringify(r.resultType)}, expected \"complete\"`);\n    const versions = r.supportedVersions ?? [];\n    if (versions.includes(MODERN)) pass(`server/discover: supportedVersions ${JSON.stringify(versions)}`);\n    else fail(`server/discover: supportedVersions ${JSON.stringify(versions)} does not include ${MODERN}`);\n    const si = r._meta?.[\"io.modelcontextprotocol/serverInfo\"];\n    if (si?.name) pass(`server/discover: serverInfo ${si.name} ${si.version ?? \"\"}`.trim());\n    else warn(\"server/discover: no io.modelcontextprotocol/serverInfo in result _meta (SHOULD)\");\n    info(`capabilities: ${Object.keys(r.capabilities ?? {}).join(\", \") || \"none\"}`);\n  }\n\n  // 2. stateless tools/list, twice: no handshake, deterministic order\n  const l1 = await s.request(\"tools/list\", { _meta: envelope() });\n  const l2 = await s.request(\"tools/list\", { _meta: envelope() });\n  if (l1.timeout || l1.error) fail(`tools/list with _meta and no initialize: ${l1.error ? l1.error.message : \"no answer\"}`);\n  else {\n    if (l1.result.resultType === \"complete\") pass(`tools/list stateless: ${names(l1).join(\", \") || \"(no tools)\"}`);\n    else fail(`tools/list: resultType is ${JSON.stringify(l1.result.resultType)}, expected \"complete\"`);\n    if (JSON.stringify(names(l1)) === JSON.stringify(names(l2))) pass(\"tools/list order is stable across calls\");\n    else fail(\"tools/list order changed between two identical calls (MUST be deterministic)\");\n    if (\"ttlMs\" in l1.result || \"cacheScope\" in l1.result) info(`cache hints: ttlMs=${l1.result.ttlMs} cacheScope=${l1.result.cacheScope}`);\n    else info(\"no ttlMs/cacheScope cache hints on tools/list (optional)\");\n  }\n\n  // 3. optional tools/call, with one multi-round-trip retry if --answer is given\n  if (call) {\n    const caps = { elicitation: {} };\n    const params = { name: call.name, arguments: call.args, _meta: envelope(MODERN, caps) };\n    let c = await s.request(\"tools/call\", params);\n    if (!c.timeout && !c.error && c.result.resultType === \"input_required\") {\n      const keys = Object.keys(c.result.inputRequests ?? {});\n      pass(`tools/call ${call.name}: input_required asks for ${keys.join(\", \") || \"(requestState only)\"}`);\n      if (answer) {\n        const retry = { ...params, inputResponses: answer, _meta: envelope(MODERN, caps) };\n        if (c.result.requestState !== undefined) retry.requestState = c.result.requestState;\n        c = await s.request(\"tools/call\", retry);\n      }\n    }\n    if (c.timeout || c.error) fail(`tools/call ${call.name}: ${c.error ? c.error.message : \"no answer\"}`);\n    else if (c.result.resultType === \"input_required\") info(`tools/call ${call.name}: still input_required`);\n    else if (c.result.resultType === \"complete\" && !c.result.isError)\n      pass(`tools/call ${call.name}: complete: ${(c.result.content?.[0]?.text ?? \"\").slice(0, 80)}`);\n    else fail(`tools/call ${call.name}: resultType ${c.result.resultType} isError ${c.result.isError}`);\n  }\n\n  // 4. unknown version on an established connection. The spec says every request\n  // carries its version and an unsupported one MUST get -32022, but the official\n  // TypeScript and Go SDKs pin the era per stdio connection, so this is a warning.\n  const bad = await s.request(\"tools/list\", { _meta: envelope(\"1900-01-01\") });\n  checkMismatch(\"unknown version mid-connection\", bad, warn);\n  s.stop();\n  if (s.nonJson.length) fail(`stdout carried ${s.nonJson.length} non-JSON line(s), first: ${s.nonJson[0].slice(0, 80)}`);\n  else pass(\"stdout carried only JSON-RPC\");\n\n  // 5. legacy initialize in a fresh process: dual-era or modern-only?\n  const l = startServer();\n  const init = await l.request(\"initialize\", {\n    protocolVersion: \"2025-11-25\",\n    capabilities: {},\n    clientInfo: { name: \"probe-mcp-2026-legacy\", version: \"1.0.0\" },\n  }, 8000);\n  l.stop();\n  if (init.result?.protocolVersion) info(`era: dual-era (legacy initialize negotiated ${init.result.protocolVersion})`);\n  else if (init.error) {\n    info(\"era: modern-only (legacy initialize rejected)\");\n    if (!/20\\d\\d-\\d\\d-\\d\\d/.test(init.error.message + JSON.stringify(init.error.data ?? \"\"))) warn(\"initialize error does not name the supported versions (SHOULD)\");\n  } else warn(\"legacy initialize got no answer\");\n\n  // 6. unknown version on the opening probe of a fresh process MUST also be -32022\n  const f = startServer();\n  const fresh = await f.request(\"server/discover\", { _meta: envelope(\"1900-01-01\") }, 8000);\n  f.stop();\n  checkMismatch(\"unknown version on the opening server/discover\", fresh);\n\n  console.log(failures ? `RESULT: ${failures} failure(s)` : `RESULT: speaks ${MODERN}`);\n  process.exit(failures ? 1 : 0);\n}\n\nmain();\n"
    },
    {
      "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
      "role": "reference",
      "description": "Finds code using removed or changed protocol features.",
      "skill": "mcp-2026-migration",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
        "codex": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
        "cursor": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
        "gemini-cli": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
        "antigravity-cli": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
        "copilot": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/latest/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
      "bytes": 6003,
      "lang": "markdown",
      "content": "#!/usr/bin/env node\n// Find MCP server code that protocol revision 2026-07-28 removed or changed.\n// Usage: node scan_mcp_legacy.mjs [dir ...]   (default: .)\n// Prints file:line, what changed and what to do. Exit 1 if anything is found.\n// Patterns are plain strings joined at runtime so this file does not flag itself.\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, extname, basename } from \"node:path\";\n\nconst j = (...parts) => parts.join(\"\");\nconst RULES = [\n  { re: new RegExp(j(\"Mcp-Session\", \"-Id|session\", \"IdGenerator|\\\\bsession\", \"Id\\\\b\"), \"i\"),\n    what: \"Sessions are gone; there is no Mcp-Session-Id.\",\n    fix: \"Pass state the next call needs in tool arguments (a server-minted handle) or, inside one multi-round-trip call, in an integrity-protected requestState.\" },\n  { re: new RegExp(j(\"[\\\"']notifications/\", \"initialized[\\\"']|on\", \"initialized\\\\b|[\\\"']initi\", \"alize[\\\"']\")),\n    what: \"The initialize/initialized handshake is removed; every request carries _meta with protocolVersion and clientCapabilities.\",\n    fix: \"Let the SDK's 2026 entry point (serveStdio / createMcpHandler, MCPServer v2, go-sdk >= v1.7.0) handle eras. Do not hand-roll handshake handlers.\" },\n  { re: new RegExp(j(\"[\\\"']pi\", \"ng[\\\"']\")), what: \"ping is removed.\", fix: \"Delete ping handlers and keep-alive pings.\" },\n  { re: new RegExp(j(\"logging/set\", \"Level|set\", \"LoggingLevel|send\", \"LoggingMessage\")),\n    what: \"logging/setLevel is removed and protocol logging is deprecated.\",\n    fix: \"Log to stderr (stdio) or OpenTelemetry. If you keep protocol logs, read the per-request io.modelcontextprotocol/logLevel.\" },\n  { re: new RegExp(j(\"sampling/create\", \"Message|\\\\.create\", \"Message\\\\(|request\", \"Sampling\\\\(\")),\n    what: \"Server-to-client sampling requests are gone (sampling is deprecated).\",\n    fix: \"Return input_required with a sampling/createMessage entry in inputRequests, or drop the dependency on client-side LLM calls.\" },\n  { re: new RegExp(j(\"elicit\", \"Input\\\\(|(?<!input\", \"Required)\\\\.elicit\\\\(\")),\n    what: \"Server-pushed elicitation is gone.\",\n    fix: \"Return input_required with an elicitation/create entry in inputRequests and read inputResponses on the retry.\" },\n  { re: new RegExp(j(\"roots/li\", \"st|list\", \"Roots\\\\(|notifications/roots/list_\", \"changed\")),\n    what: \"Roots are deprecated and roots/list_changed is removed.\",\n    fix: \"Take the paths a tool needs as tool arguments, checked against an allowed root.\" },\n  { re: new RegExp(j(\"resources/sub\", \"scribe|resources/unsub\", \"scribe\")),\n    what: \"resources/subscribe and resources/unsubscribe are replaced.\",\n    fix: \"Use subscriptions/listen (the long-lived stream that also replaced the HTTP GET endpoint).\" },\n  { re: new RegExp(j(\"Last-Event\", \"-ID|event\", \"Store\\\\b|Event\", \"Store\\\\b\"), \"i\"),\n    what: \"SSE resumability (Last-Event-ID, event stores) is removed.\",\n    fix: \"Drop the event store; long work belongs in the tasks extension or a handle the client can poll.\" },\n  { re: new RegExp(j(\"-3200\", \"2\\\\b\")),\n    what: \"Resource not found is now -32602 (invalid params), not -32002.\",\n    fix: \"Return -32602 for an unknown resource URI.\" },\n  { re: new RegExp(j(\"SSEServer\", \"Transport|sse_\", \"app\\\\(\")),\n    what: \"The HTTP+SSE transport is deprecated.\",\n    fix: \"Serve Streamable HTTP (createMcpHandler in TypeScript v2).\" },\n  { re: new RegExp(j(\"Stdio\", \"ServerTransport\\\\(\")),\n    what: \"A v2 TypeScript McpServer connected straight to StdioServerTransport only speaks 2025-era protocol.\",\n    fix: \"Replace server.connect(new StdioServerTransport()) with serveStdio(() => buildServer()).\" },\n  { re: new RegExp(j(\"@modelcontext\", \"protocol/sdk\\\\b\")),\n    what: \"@modelcontextprotocol/sdk is the v1 TypeScript SDK; v1 never speaks 2026-07-28.\",\n    fix: \"Run npx @modelcontextprotocol/codemod@2.3.1 v1-to-v2 . then pin @modelcontextprotocol/server 2.3.1.\" },\n  { re: new RegExp(j(\"mcp\\\\.server\\\\.fast\", \"mcp|\\\\bmcp\\\\s*(==|~=|<)\\\\s*1\\\\.\")),\n    what: \"MCP Python SDK v1 (FastMCP) does not speak 2026-07-28.\",\n    fix: \"Pin mcp==2.3.0 and use from mcp.server import MCPServer.\" },\n  { re: new RegExp(j(\"modelcontextprotocol/go-\", \"sdk v(0\\\\.|1\\\\.[0-6]\\\\.)\")),\n    what: \"go-sdk before v1.7.0 does not speak 2026-07-28.\",\n    fix: \"go get github.com/modelcontextprotocol/go-sdk@v1.8.0\" },\n  { re: new RegExp(j(\"[\\\"']tasks/(get|list|cancel|res\", \"ult)[\\\"']\")),\n    what: \"Tasks moved out of core into the io.modelcontextprotocol/tasks extension.\",\n    fix: \"Advertise the extension under capabilities.extensions and follow its spec, or drop task support.\" },\n];\n\nconst EXT = new Set([\".ts\", \".mts\", \".cts\", \".js\", \".mjs\", \".cjs\", \".py\", \".go\", \".rs\", \".json\", \".toml\", \".txt\", \".mod\"]);\nconst SKIP = new Set([\"node_modules\", \".git\", \"dist\", \"build\", \".venv\", \"venv\", \"__pycache__\", \"vendor\", \"target\"]);\nconst self = basename(new URL(import.meta.url).pathname);\nconst lockfiles = new Set([\"package-lock.json\", \"pnpm-lock.yaml\", \"yarn.lock\", \"uv.lock\", \"go.sum\", \"Cargo.lock\"]);\n\nfunction* walk(p) {\n  const st = statSync(p);\n  if (st.isFile()) { yield p; return; }\n  for (const name of readdirSync(p)) {\n    if (SKIP.has(name)) continue;\n    yield* walk(join(p, name));\n  }\n}\n\nlet hits = 0;\nconst roots = process.argv.slice(2).length ? process.argv.slice(2) : [\".\"];\nfor (const root of roots) {\n  for (const file of walk(root)) {\n    const name = basename(file);\n    if (name === self || lockfiles.has(name)) continue;\n    if (!EXT.has(extname(file)) && name !== \"requirements.txt\") continue;\n    const lines = readFileSync(file, \"utf8\").split(\"\\n\");\n    lines.forEach((line, i) => {\n      if (/^\\s*(\\/\\/|#|\\*|\\/\\*)/.test(line)) return; // comments describe the old way; skip them\n      for (const r of RULES) {\n        if (r.re.test(line)) {\n          hits++;\n          console.log(`${file}:${i + 1}: ${r.what}\\n    fix: ${r.fix}`);\n        }\n      }\n    });\n  }\n}\nconsole.log(hits ? `${hits} finding(s)` : \"no 2025-era MCP patterns found\");\nprocess.exit(hits ? 1 : 0);\n"
    }
  ],
  "requires": {
    "tools": [
      "nodejs",
      "npm"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "antigravity-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": true,
    "scripts": [
      "probe_mcp_2026.mjs",
      "scan_mcp_legacy.mjs"
    ],
    "notes": "npm installs the official MCP SDK packages and npx downloads the official codemod. The probe only starts the server command you give it and talks to it over stdio; the scanner reads files and changes nothing."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-17",
  "published": "2026-10-17",
  "updated": "2026-10-17",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "antigravity-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 1,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/mcp-2026-07-28-migration/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/mcp-2026-07-28-migration.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-17T20:59:33Z",
    "commit": "08c3710727a4d6fc320a98ecf32bdc0a780eaa6c",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 8 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 8 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 6 agents; layouts re-discovered",
        "details": [
          "Claude Code: 7 files, 1 skill in .claude/skills/",
          "Codex CLI: 6 files, 1 skill in .agents/skills/",
          "Cursor: 6 files, 1 skill in .agents/skills/",
          "Gemini CLI: 7 files, 1 skill in .agents/skills/",
          "Antigravity CLI: 6 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 6 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network no (listing says yes)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "2 references between files resolve for every agent",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "8 files, 28.2 KB total; largest skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs 8.2 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "The mcp-2026-migration skill's TypeScript v2 server builds and passes the skill's 2026-07-28 stdio probe, including an input_required round trip and legacy initialize. The same probe passes a Python mcp 2.3.0 server. The scanner flags a v1 server and still flags it after the official codemod.",
        "details": [
          "Applied 13 loadout files at every agent's paths",
          "✓ place the skill's server and scripts: bash -c mkdir -p src && cp .agents/skills/mcp-2026-migration/references/server.ts src/server.ts && cp .agents/skills/mcp-2026-migration/scripts/*.mjs . && echo placed",
          "✓ install (exact pins): npm ci --no-audit --no-fund",
          "✓ type-check and build (tsc): npm run build",
          "✓ probe the TypeScript v2 server: node probe_mcp_2026.mjs --call delete_note {\"id\":\"n1\"} --answer {\"confirm\":{\"action\":\"accept\",\"content\":{\"confirm\":true}}} -- node dist/server.js",
          "✓ probe a Python mcp 2.3.0 server: node probe_mcp_2026.mjs --call lookup_term {\"term\":\"skill\"} -- uv run -q --no-project --python 3.12 --with mcp==2.3.0 python py_server.py",
          "✓ scanner is clean on the migrated server: node scan_mcp_legacy.mjs src",
          "✓ scanner flags the v1 server: bash -c node scan_mcp_legacy.mjs legacy; echo \"exit=$?\"",
          "✓ official codemod leaves the protocol work to do: bash -c mkdir -p cm/src && cp legacy/server.ts cm/src/ && printf '%s' '{\"name\":\"cm\",\"type\":\"module\",\"dependencies\":{\"@modelcontextprotocol/sdk\":\"1.32.1\"}}' > cm/package.json && npx -y @modelcontextprotocol/codemod@2.3.1 v1-to-v2 cm >/dev/null && node scan_mcp_legacy.mjs cm; echo \"exit=$?\"",
          "Covers: serveStdio(buildServer) on @modelcontextprotocol/server 2.3.1 answers server/discover and stateless tools/list with resultType and serverInfo",
          "Covers: A destructive tool asks through input_required and finishes on the retry with inputResponses",
          "Covers: Unknown protocol version gets -32022 with the supported list; legacy initialize still works (dual-era)",
          "Covers: The same probe passes the official Python SDK 2.3.0",
          "Covers: The scanner finds sessions, pushed elicitation, protocol logging, resources/subscribe and v1 stdio wiring, and the codemod alone does not fix them"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/mcp-2026-migration/SKILL.md",
          "target": ".claude/skills/mcp-2026-migration/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/mcp-2026-migration/references/changes.md",
          "target": ".claude/skills/mcp-2026-migration/references/changes.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/references/server.ts",
          "target": ".claude/skills/mcp-2026-migration/references/server.ts",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "target": ".claude/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "target": ".claude/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/mcp-2026-migration/SKILL.md",
          "target": ".agents/skills/mcp-2026-migration/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/mcp-2026-migration/references/changes.md",
          "target": ".agents/skills/mcp-2026-migration/references/changes.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/references/server.ts",
          "target": ".agents/skills/mcp-2026-migration/references/server.ts",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/mcp-2026-migration/SKILL.md",
          "target": ".agents/skills/mcp-2026-migration/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/mcp-2026-migration/references/changes.md",
          "target": ".agents/skills/mcp-2026-migration/references/changes.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/references/server.ts",
          "target": ".agents/skills/mcp-2026-migration/references/server.ts",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/mcp-2026-migration/SKILL.md",
          "target": ".agents/skills/mcp-2026-migration/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/mcp-2026-migration/references/changes.md",
          "target": ".agents/skills/mcp-2026-migration/references/changes.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/references/server.ts",
          "target": ".agents/skills/mcp-2026-migration/references/server.ts",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/gemini-cli.zip"
    },
    "antigravity-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/mcp-2026-migration/SKILL.md",
          "target": ".agents/skills/mcp-2026-migration/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/mcp-2026-migration/references/changes.md",
          "target": ".agents/skills/mcp-2026-migration/references/changes.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/references/server.ts",
          "target": ".agents/skills/mcp-2026-migration/references/server.ts",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/antigravity-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/mcp-2026-migration/SKILL.md",
          "target": ".agents/skills/mcp-2026-migration/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/mcp-2026-migration/references/changes.md",
          "target": ".agents/skills/mcp-2026-migration/references/changes.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/changes.md",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/references/server.ts",
          "target": ".agents/skills/mcp-2026-migration/references/server.ts",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/references/server.ts",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/probe_mcp_2026.mjs",
          "role": "reference"
        },
        {
          "path": "skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "target": ".agents/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-2026-07-28-migration/1.0.0/skills/mcp-2026-migration/scripts/scan_mcp_legacy.mjs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/mcp-2026-07-28-migration/source.zip"
}