Skip to content

fix: restore globalThis.Response after lazy-loading MCP SDK#148

Open
br-schneider wants to merge 1 commit intovercel:mainfrom
br-schneider:fix/restore-global-response
Open

fix: restore globalThis.Response after lazy-loading MCP SDK#148
br-schneider wants to merge 1 commit intovercel:mainfrom
br-schneider:fix/restore-global-response

Conversation

@br-schneider
Copy link

Problem

The @modelcontextprotocol/sdk has transitive dependencies (via undici) that replace globalThis.Response when imported. This breaks Next.js App Router route handlers which validate responses with instanceof Response, causing "No response is returned from route handler" errors for all routes in the same process — not just the MCP endpoint.

Why lazy-loading alone isn't enough

PR #141 proposed lazy-loading the SDK, which delays the import until the first MCP request. However, once the MCP endpoint is hit and the SDK loads, globalThis.Response is permanently replaced. All subsequent requests to any route handler in the same Node.js process then fail the instanceof Response check.

We verified this on Vercel production:

# Before MCP route is hit — works fine
curl /api/deployment-status  → 401 ✓

# Hit MCP route — triggers SDK load
curl /api/connect/sse        → 401 ✓

# After MCP SDK loaded — broken!
curl /api/deployment-status  → 500 ✗  "No response is returned"

Fix

This PR:

  1. Converts eager SDK value imports (McpServer, SSEServerTransport, StreamableHTTPServerTransport) to type-only imports
  2. Adds a loadSdk() function that dynamically imports SDK modules at first use
  3. Saves globalThis.Response before the import and restores it after, so that other route handlers continue to pass Next.js's instanceof Response validation
  4. Defers StreamableHTTPServerTransport creation to first request (instead of module load time)
  5. Calls loadSdk() at the start of mcpApiHandler

Key insight

The critical difference from #141 is step 3 — saving and restoring globalThis.Response. Without this, the SDK's side effect persists across the entire process.

Testing

  • All existing tests pass (29/30 — the 1 pre-existing failure in should read server capabilities is unrelated to this change)
  • Verified on Vercel production that routes return correct responses both before and after the MCP endpoint is called

Fixes #140

The @modelcontextprotocol/sdk has transitive dependencies (via undici)
that replace globalThis.Response when imported. This breaks Next.js App
Router route handlers which validate responses with instanceof Response,
causing "No response is returned from route handler" errors for ALL
routes in the same process — not just the MCP endpoint.

Lazy-loading alone (as proposed in vercel#141) only delays the problem. Once
the MCP endpoint is hit, the global Response is replaced and all
subsequent requests to any route handler fail.

This fix:
- Converts eager SDK imports to type-only imports
- Adds a loadSdk() function that dynamically imports SDK modules
- Saves globalThis.Response before import, restores it after
- Defers StreamableHTTPServerTransport creation to first request
- Calls loadSdk() at the start of mcpApiHandler

Fixes vercel#140
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.

🐛 Issue: App Route fails with “No response is returned” only when running inside Docker container

1 participant