Root cause of the production MCP 500 spike (incident #47) — separate from the scoped-key 401 in #10667. This is the ESM/CJS module-graph issue Rahul identified.
The setup. apps/apollo is a CommonJS build (no type field). @composiohq/db and @composiohq/lib are type: module and, since e6166ba68d ("publish db and lib as dist packages", live in prod via the Jun 11 master→prod), ship a dual ESM/CJS dist whose exports resolved import and default to the ESM dist/index.js (only require to dist/index.cjs). Before that commit they were consumed as source (import → ./src/index.ts, tsup --format cjs), transpiled into Apollo's CJS bundle — one deterministic format.
The failure mechanism. Pulling the ESM build into Apollo's CJS server graph makes Next/Turbopack's ESM↔CJS interop linking order nondeterministic across Vercel's serverless instances (depends on cold-start, what's already loaded, the code path). On unlucky instances the live bindings corrupt and a co-bundled route module's GET/POST export resolves to garbage → No response is returned from route handler '…[serverId]/route.ts'. Expected a Response object but received 'd' → 500. Notably the failing route doesn't even import db/lib directly — it's shared module-graph corruption. @composiohq/db / @composiohq/lib are imported by virtually every MCP path (validateRequest.ts, mcp_server/dbUtils, zpp), so when these heavyweights became ESM the failure probability jumped — the spike (~2-3% of MCP requests, because it's per-instance load-order dependent).
#10665 (config-level redirect for the transport-less [serverId] URL) only band-aided that one route's symptom; the 500s persisted on it post-deploy because the underlying interop hazard was untouched.
The fix. Point every exports condition (require / import / default) at the CJS dist/index.cjs, and drop the module field. The ESM build can no longer be loaded into a CJS bundle, so the interop nondeterminism is eliminated. This is the "make it fully CJS" direction from the incident discussion, done at the resolution layer with minimal blast radius.
Notes:
moduleResolution: node (classic), which ignores exports and reads the unchanged types field (dist/index.d.ts). Only the bundler/runtime resolution (which reads exports) changes.workspace:*, so it builds from source on deploy and picks up the new exports immediately. The GHP-published tarballs are for external consumers; republishing with the CJS-only exports is an optional follow-up.tsup build still emits dist/index.js, but nothing references it now. Dropping esm from the build (--format cjs) is a safe follow-up cleanup (left out here to avoid touching the .d.ts emission / types field under classic resolution).production once merged.apps/apollo both require.resolve('@composio/db') and import.meta.resolve('@composio/db') now resolve to packages/db/dist/index.cjs (same for @composio/lib). Before, import/default resolved to the ESM dist/index.js.@composiohq/db dts step fails identically with and without this change (a local-only quirk — @composiohq/lib isn't symlinked into packages/db/node_modules in this checkout; CI links it). Confirmed via a stash/rebuild counterfactual.moduleResolution is classic node, which reads the types field (unchanged), not exports — so this change cannot alter Apollo type-checking, only the bundler's module-format choice.requires lib's CJS build); no genuine ESM consumer needs the ESM entry.