Skip to content

fix: isolate ext-apps from MCP role packages - #4

Draft
tonxxd wants to merge 1 commit into
khandrew1:codex/mcp-sdk-v2-beta4-migrationfrom
tonxxd:fix/core-only-v2-deps
Draft

fix: isolate ext-apps from MCP role packages#4
tonxxd wants to merge 1 commit into
khandrew1:codex/mcp-sdk-v2-beta4-migrationfrom
tonxxd:fix/core-only-v2-deps

Conversation

@tonxxd

@tonxxd tonxxd commented Jul 20, 2026

Copy link
Copy Markdown

Summary

This isolates @modelcontextprotocol/ext-apps from the v2 MCP role packages while preserving Andrew's v2 beta.4 migration.

PR modelcontextprotocol#710 moved the SDK to the official v2 Client and Server, but that made both role packages published peers. In addition, the ./server entry imported constants from app.ts, creating a server → app → client runtime path. A server-only consumer such as mcp-use/server therefore installed and could bundle the client package.

The Apps iframe channel now uses an ext-apps-owned, protocol-neutral JSON-RPC lifecycle built from public @modelcontextprotocol/core schemas. It keeps ui/initialize / ui/notifications/initialized as the only Apps initialization sequence and does not add a standard MCP client/server handshake.

Dependency graph

Before:

flowchart LR
  Consumer["mcp-use/server"] --> ExtServer["ext-apps/server"]
  ExtServer --> App["ext-apps app.ts (constants)"]
  App --> Client["@modelcontextprotocol/client"]
  ExtServer -. peer .-> Server["@modelcontextprotocol/server"]
  Client --> Core["@modelcontextprotocol/core"]
  Server --> Core
Loading

After:

flowchart LR
  Consumer["mcp-use/server"] --> ExtServer["ext-apps/server"]
  ExtServer --> Neutral["neutral constants + structural types"]
  Neutral --> Core["@modelcontextprotocol/core"]
  Consumer --> Server["@modelcontextprotocol/server (consumer-owned)"]
  Server --> Core

  AppConsumer["App consumer"] --> App["ext-apps App"]
  App --> Protocol["ext-apps JSON-RPC protocol"]
  Protocol --> Core

  Host["Host / AppBridge"] -. optional structural adapter .-> Client["@modelcontextprotocol/client (host-owned)"]
  Client --> Core
Loading

Implementation

  • Replace Client / Server inheritance with a local transport, request correlation, handler, cancellation, timeout, error, close, and reconnect lifecycle.
  • Keep Apps initialization authoritative and remove the extra standard MCP handshake.
  • Add public structural McpClientLike and McpServerLike contracts; compile tests prove the official v2 beta.4 classes satisfy them.
  • Move resource constants to a neutral module so ./server has no edge to app.ts.
  • Retain only core and Zod as MCP runtime peers. Role packages remain development dependencies for compatibility tests and are declared by role-specific examples/consumers.
  • Emit production-only declarations so test/example imports cannot leak role-package type references into the published tarball.
  • Add packed-install and esbuild-metafile assertions for app-only and server-only consumers.

Compatibility impact

The supported Apps APIs and wire behavior are preserved. App and AppBridge are no longer nominal subclasses of the official MCP Client and Server; code relying on instanceof or unrelated inherited role APIs should use the Apps APIs instead. Official v2 clients can still be passed to AppBridge, and official v2 McpServer instances remain accepted by the server registration helpers through structural typing.

Verification

  • npm test: 398 passed, 1 skipped, 0 failed
  • Protocol/App/AppBridge/compatibility focused suite after final lifecycle fix: 171 passed, 0 failed
  • npm run build: passed, including full TypeScript checking and production declarations
  • npm run build:all: all examples and the reference host passed
  • npm exec typedoc -- --treatValidationWarningsAsErrors --emit none: passed
  • node scripts/check-dependency-isolation.mjs: passed
    • app-only packed consumer installs and bundles no server package
    • server-only packed consumer installs and bundles no client package
    • published JS and declarations contain no client/server import edge
  • Functional integration-server E2E: 5 passed, 0 failed

The broader integration-server E2E run passed 6 of 7 checks; only the golden screenshot comparison differed by 7% against a 6% threshold on this machine. All functional initialization and interaction checks passed, and no snapshot was updated.

This PR targets Andrew's migration branch and does not modify or merge PR modelcontextprotocol#710 directly.


Summary by cubic

Isolates @modelcontextprotocol/ext-apps from MCP role packages by replacing role inheritance with a local, protocol‑neutral JSON‑RPC layer. Server‑only installs no longer pull the client, and app‑only installs no longer pull the server, while keeping v2 beta.4 compatibility via structural typing.

  • Refactors

    • Added Protocol lifecycle (request/notification routing, correlation, cancel, timeouts, close/reconnect).
    • App and AppBridge now use this layer; only Apps handshake runs (ui/initializeui/notifications/initialized).
    • Introduced mcp-types with structural McpClientLike/McpServerLike; official v2 classes satisfy them.
    • Updated server helpers to accept McpServerLike; proxied tool/resource/prompt flows via provided client when present.
    • Moved RESOURCE_URI_META_KEY and RESOURCE_MIME_TYPE to src/constants; removed ./serverapp.ts import edge.
    • Generated schemas now reference local types to avoid role-package leakage; tests adjusted.
  • Dependencies

    • Removed @modelcontextprotocol/client and @modelcontextprotocol/server from published peers; keep @modelcontextprotocol/core and zod.
    • Added scripts/check-dependency-isolation.mjs (+ npm run test:dependency-isolation) to assert no client/server edges in published JS/d.ts and app‑only/server‑only bundles.
    • Build now runs tsc --noEmit plus tsc -p tsconfig.build.json; build.bun.ts cleans dist before emit.

Written for commit 928dd83. Summary will update on new commits.

Review in cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant