@@ -40,6 +40,63 @@ const WRANGLER_BIN = (() => {
4040 return path . resolve ( path . dirname ( pkgPath ) , bin ) ;
4141} ) ( ) ;
4242
43+ /**
44+ * Create an installable tarball of `@modelcontextprotocol/server` without mutating the workspace.
45+ *
46+ * Running `pnpm pack` inside `packages/server` is not an option here: its `prepack` hook rebuilds
47+ * the package in place (tsdown with `clean: true`), deleting and rewriting `packages/server/dist`
48+ * while the rest of the test run is still going. Anything that node-resolves the workspace
49+ * packages at that moment — most notably suites that spawn child processes importing
50+ * `@modelcontextprotocol/server` — sees a half-written dist and fails. Instead, the bundle is
51+ * built into a staging directory under the test's temp dir and the tarball is created from that
52+ * staging copy, so shared, node-resolvable state never changes while tests are running.
53+ *
54+ * Returns the tarball's file name; the tarball itself is written into `tempDir`.
55+ */
56+ function packServerPackage ( tempDir : string ) : string {
57+ const serverPkgPath = path . resolve ( __dirname , '../../../../packages/server' ) ;
58+ const stagingDir = path . join ( tempDir , 'package-staging' ) ;
59+ fs . mkdirSync ( stagingDir , { recursive : true } ) ;
60+
61+ // Build the publishable bundle with its output redirected away from the workspace's
62+ // packages/server/dist (the CLI flag overrides `outDir` from tsdown.config.ts).
63+ execSync ( `pnpm exec tsdown --out-dir "${ path . join ( stagingDir , 'dist' ) } "` , {
64+ cwd : serverPkgPath ,
65+ stdio : 'pipe' ,
66+ timeout : 60_000
67+ } ) ;
68+
69+ // Write a publish-shaped manifest into the staging dir: drop lifecycle scripts and
70+ // devDependencies, and resolve pnpm-only `catalog:`/`workspace:` specifiers to the versions
71+ // installed in the workspace — the same substitution `pnpm pack` performs when publishing.
72+ const manifest = JSON . parse ( fs . readFileSync ( path . join ( serverPkgPath , 'package.json' ) , 'utf8' ) ) as {
73+ scripts ?: unknown ;
74+ devDependencies ?: unknown ;
75+ dependencies ?: Record < string , string > ;
76+ } ;
77+ delete manifest . scripts ;
78+ delete manifest . devDependencies ;
79+ const dependencies = manifest . dependencies ?? { } ;
80+ for ( const [ name , spec ] of Object . entries ( dependencies ) ) {
81+ if ( spec . startsWith ( 'catalog:' ) || spec . startsWith ( 'workspace:' ) ) {
82+ const installed = JSON . parse ( fs . readFileSync ( path . join ( serverPkgPath , 'node_modules' , name , 'package.json' ) , 'utf8' ) ) as {
83+ version : string ;
84+ } ;
85+ dependencies [ name ] = installed . version ;
86+ }
87+ }
88+ fs . writeFileSync ( path . join ( stagingDir , 'package.json' ) , JSON . stringify ( manifest , null , 2 ) ) ;
89+
90+ // Pack the staging copy. The staged manifest carries no scripts, so this is a pure tar step;
91+ // npm is used because the staging dir lives outside the pnpm workspace.
92+ const packOutput = execSync ( `npm pack --pack-destination "${ tempDir } "` , {
93+ cwd : stagingDir ,
94+ encoding : 'utf8' ,
95+ timeout : 60_000
96+ } ) ;
97+ return path . basename ( packOutput . trim ( ) . split ( '\n' ) . pop ( ) ! ) ;
98+ }
99+
43100/** Ask the kernel for a currently-free port instead of hardcoding one. */
44101async function getFreePort ( ) : Promise < number > {
45102 return new Promise ( ( resolve , reject ) => {
@@ -212,14 +269,9 @@ describe('Cloudflare Workers compatibility (no nodejs_compat)', () => {
212269 } ;
213270
214271 try {
215- // Pack server package
216- const serverPkgPath = path . resolve ( __dirname , '../../../../packages/server' ) ;
217- const packOutput = execSync ( `pnpm pack --pack-destination "${ tempDir } "` , {
218- cwd : serverPkgPath ,
219- encoding : 'utf8' ,
220- timeout : 60_000
221- } ) ;
222- const tarballName = path . basename ( packOutput . trim ( ) . split ( '\n' ) . pop ( ) ! ) ;
272+ // Pack the server package into the temp dir without touching the workspace's own
273+ // dist/ — see packServerPackage for why the plain `pnpm pack` route is unsafe here.
274+ const tarballName = packServerPackage ( tempDir ) ;
223275
224276 // Write package.json
225277 const pkgJson = {
0 commit comments