From d1b183cec46f684c75e71d082cbe25e135d18824 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Mon, 22 Jun 2026 20:30:54 +0000 Subject: [PATCH 1/2] fix(middleware): stop bundling workspace type graphs into .d.ts (avoids dts-gen OOM) The four middleware packages were inlining the entire @modelcontextprotocol/server (and transitively core) type graph into their bundled .d.mts instead of emitting an external `import` for the peerDependency. On main this is ~14s and a 540 KB sourcemap for a 200-line wrapper; once core's public surface grows past a fairly low threshold the tsdown dts step OOMs at the default Node heap. Root cause: rolldown-plugin-dts decides external-vs-bundle by checking the resolved path for `node_modules`. With pnpm workspace symlinks the realpath is `packages/server/...` (no `node_modules`), and the dev tsconfig `paths` map the specifier to source, so the peer dep is treated as local and inlined. Fix: clear `paths` for dts emit and set `preserveSymlinks: true` so the resolved path keeps `node_modules` and the plugin externalises it. The emitted .d.mts now references `@modelcontextprotocol/server` directly (correct, since it's a peer dep). Also switch the @modelcontextprotocol/node source import from the private `core` package to `server`, which re-exports the same types. Note: middleware dts emit now reads server's built `dist/*.d.mts` rather than its source. `pnpm -r build` orders this correctly; a standalone middleware build needs server built first. Claude-Session: https://claude.ai/code/session_01PHVERaA4Sw7Ui3zk88xSWC --- packages/middleware/express/tsdown.config.ts | 8 ++++---- packages/middleware/fastify/tsdown.config.ts | 8 ++++---- packages/middleware/hono/tsdown.config.ts | 8 ++++---- packages/middleware/node/src/streamableHttp.ts | 10 ++++++++-- packages/middleware/node/tsdown.config.ts | 15 ++++++++++----- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/packages/middleware/express/tsdown.config.ts b/packages/middleware/express/tsdown.config.ts index 64ed143a86..c67ee585cd 100644 --- a/packages/middleware/express/tsdown.config.ts +++ b/packages/middleware/express/tsdown.config.ts @@ -12,11 +12,11 @@ export default defineConfig({ shims: true, dts: { resolver: 'tsc', + // Keep workspace deps as external imports in the bundled .d.ts instead of + // inlining their type graph — see ../node/tsdown.config.ts for the rationale. compilerOptions: { - baseUrl: '.', - paths: { - '@modelcontextprotocol/server': ['../server/src/index.ts'] - } + paths: {}, + preserveSymlinks: true } } }); diff --git a/packages/middleware/fastify/tsdown.config.ts b/packages/middleware/fastify/tsdown.config.ts index 64ed143a86..c67ee585cd 100644 --- a/packages/middleware/fastify/tsdown.config.ts +++ b/packages/middleware/fastify/tsdown.config.ts @@ -12,11 +12,11 @@ export default defineConfig({ shims: true, dts: { resolver: 'tsc', + // Keep workspace deps as external imports in the bundled .d.ts instead of + // inlining their type graph — see ../node/tsdown.config.ts for the rationale. compilerOptions: { - baseUrl: '.', - paths: { - '@modelcontextprotocol/server': ['../server/src/index.ts'] - } + paths: {}, + preserveSymlinks: true } } }); diff --git a/packages/middleware/hono/tsdown.config.ts b/packages/middleware/hono/tsdown.config.ts index 64ed143a86..c67ee585cd 100644 --- a/packages/middleware/hono/tsdown.config.ts +++ b/packages/middleware/hono/tsdown.config.ts @@ -12,11 +12,11 @@ export default defineConfig({ shims: true, dts: { resolver: 'tsc', + // Keep workspace deps as external imports in the bundled .d.ts instead of + // inlining their type graph — see ../node/tsdown.config.ts for the rationale. compilerOptions: { - baseUrl: '.', - paths: { - '@modelcontextprotocol/server': ['../server/src/index.ts'] - } + paths: {}, + preserveSymlinks: true } } }); diff --git a/packages/middleware/node/src/streamableHttp.ts b/packages/middleware/node/src/streamableHttp.ts index 68a0c224f0..c101fcd0af 100644 --- a/packages/middleware/node/src/streamableHttp.ts +++ b/packages/middleware/node/src/streamableHttp.ts @@ -10,8 +10,14 @@ import type { IncomingMessage, ServerResponse } from 'node:http'; import { getRequestListener } from '@hono/node-server'; -import type { AuthInfo, JSONRPCMessage, MessageExtraInfo, RequestId, Transport } from '@modelcontextprotocol/core'; -import type { WebStandardStreamableHTTPServerTransportOptions } from '@modelcontextprotocol/server'; +import type { + AuthInfo, + JSONRPCMessage, + MessageExtraInfo, + RequestId, + Transport, + WebStandardStreamableHTTPServerTransportOptions +} from '@modelcontextprotocol/server'; import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server'; /** diff --git a/packages/middleware/node/tsdown.config.ts b/packages/middleware/node/tsdown.config.ts index 7d90f6517f..c444314fdc 100644 --- a/packages/middleware/node/tsdown.config.ts +++ b/packages/middleware/node/tsdown.config.ts @@ -21,12 +21,17 @@ export default defineConfig({ // Bundles d.ts files into a single output dts: { resolver: 'tsc', - // override just for DTS generation: + // The dev tsconfig.json maps @modelcontextprotocol/* to workspace source via + // `paths` so typecheck/IDE work without a prior build. For declaration emit we + // need the opposite: resolve workspace deps through node_modules and keep them + // as *external* imports in the bundled .d.ts (server is a peerDependency, so + // consumers already have its types). `paths: {}` disables the dev source + // mappings; `preserveSymlinks` keeps `node_modules` in the resolved path so + // rolldown-plugin-dts recognises the dep as external instead of inlining the + // whole upstream type graph (which OOMs once core's surface gets large enough). compilerOptions: { - baseUrl: '.', - paths: { - '@modelcontextprotocol/core': ['../core/src/index.ts'] - } + paths: {}, + preserveSymlinks: true } } }); From acd22fe8abb029ffd331e2c92b0dc158c7addbf0 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Mon, 22 Jun 2026 21:12:31 +0000 Subject: [PATCH 2/2] chore(middleware): note upstream rolldown-plugin-dts fix in dts override TODO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rolldown-plugin-dts@0.21.0 (03998d41) honours rolldown's `external` resolution before the node_modules path test, which makes this override unnecessary once tsdown is bumped past ^0.18. The bump also removes `dts.resolve`, so it needs to land together with a server/client config rework — tracked separately. Claude-Session: https://claude.ai/code/session_01PHVERaA4Sw7Ui3zk88xSWC --- packages/middleware/node/tsdown.config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/middleware/node/tsdown.config.ts b/packages/middleware/node/tsdown.config.ts index c444314fdc..f953bc65de 100644 --- a/packages/middleware/node/tsdown.config.ts +++ b/packages/middleware/node/tsdown.config.ts @@ -29,6 +29,12 @@ export default defineConfig({ // mappings; `preserveSymlinks` keeps `node_modules` in the resolved path so // rolldown-plugin-dts recognises the dep as external instead of inlining the // whole upstream type graph (which OOMs once core's surface gets large enough). + // + // TODO: drop this override once tsdown pulls rolldown-plugin-dts >=0.21.0 + // (sxzz/rolldown-plugin-dts@03998d41 honours rolldown `external` before the + // node_modules path test). Bumping also requires reworking the `dts.resolve` + // usage in packages/{server,client}/tsdown.config.ts — that option was removed + // in the same release. compilerOptions: { paths: {}, preserveSymlinks: true