@@ -15,8 +15,8 @@ If you are already on v2 and want to adopt the **2026-07-28 protocol revision**,
1515
1616## TL;DR — quick path
1717
18- 1 . ** Prerequisites.** Node.js 20+ and ESM ( ` "type": "module" ` or ` .mts ` ). v2 ships ESM
19- only; CommonJS callers must use dynamic ` import() ` .
18+ 1 . ** Prerequisites.** Node.js 20+. v2 is ESM-first but ships a CommonJS build too, so
19+ both ` import ` and ` require('@modelcontextprotocol/…') ` resolve natively .
20202 . ** Run the codemod.**
2121 ``` bash
2222 npx @modelcontextprotocol/codemod@beta v1-to-v2 .
@@ -212,8 +212,9 @@ depends on `@hono/node-server` at runtime (Node HTTP ↔ Web Standard conversion
212212does **not** require the `hono` framework — your package manager may emit a harmless
213213unmet-peer warning for `hono` (upstream `@hono/node-server` declares it).
214214
215- v2 requires **Node.js 20+** and ships **ESM only**. If your project uses CommonJS
216- (`require()`), either migrate to ESM or use dynamic `import()`.
215+ v2 requires **Node.js 20+**. It is ESM-first but ships a **CommonJS build alongside
216+ ESM**, so CommonJS projects can `require(' @modelcontextprotocol/…' )` directly — no
217+ dynamic `import()` shim required.
217218
218219Repo-local tooling that encodes the literal v1 package name — dependency-pin lints,
219220version allowlists, CI checks, scripts — fails after the manifest swap and is invisible
@@ -225,13 +226,13 @@ directly, and a surviving cast keeps suppressing type checking that would otherw
225226catch real errors.
226227
227228Tooling that pins SDK ** dist text** (reading a constant out of a built file with
228- ` require.resolve` + a regex) breaks in three stacked ways: the v2 exports maps offer
229- nothing a CJS ` require.resolve` can find; the literal usually lives in a
229+ ` require.resolve` + a regex) breaks in two stacked ways: the literal usually lives in a
230230content-hashed sibling chunk (` dist/sse-< hash> .mjs` ), not the subpath' s entry module,
231231so fixed-path reads do not survive a rebuild — scan the package' s ` dist/` directory
232232for the literal instead; and the emitted quote style differs from v1, so a
233- quote-anchored pattern misses silently — match either quote. v2 also ships ESM only:
234- ` /dist/cjs/` ↔ ` /dist/esm/` flavor-pair path swaps have no equivalent.
233+ quote-anchored pattern misses silently — match either quote. The build layout also
234+ changed: v2 emits ` .mjs` /` .cjs` siblings in a flat ` dist/` , so v1' s `/dist/cjs/` ↔
235+ `/dist/esm/` flavor-pair path swaps have no equivalent.
235236
236237#### Registry availability during the beta
237238
@@ -250,41 +251,13 @@ window:
250251 (` pnpm install && pnpm build` , then ` pnpm pack` in the package directory) and
251252 reference it with a committed ` file:` dependency.
252253
253- #### CommonJS test runners (Jest) cannot resolve the v2 packages
254-
255- Every leaf of the v2 packages' ` exports` maps carries only the ` types` and ` import`
256- conditions — there is no ` require` or ` default` leaf — so the packages cannot be
257- resolved by CJS resolvers at all. Jest under its default CommonJS resolution (including
258- ` next/jest` setups) fails with ` Cannot find module ' @modelcontextprotocol/client' ` even
259- when a transform that handles ESM is configured: resolution fails before any transform
260- runs. Vitest and native Node ESM are unaffected.
261-
262- The interim recipe — interim because the packaging shape is still under discussion and
263- a later alpha may make it unnecessary — maps the bare specifiers straight to the dist
264- ESM files and lets the transform convert them (the dists contain no ` import.meta` , so
265- an ESM→CJS transform such as SWC or Babel handles them cleanly):
266-
267- ` ` ` js
268- // jest.config.js
269- transformIgnorePatterns: [], // or a pattern that still transforms @modelcontextprotocol/*
270- moduleNameMapper: {
271- ' ^@modelcontextprotocol/client$' : ' <rootDir>/node_modules/@modelcontextprotocol/client/dist/index.mjs' ,
272- ' ^@modelcontextprotocol/server$' : ' <rootDir>/node_modules/@modelcontextprotocol/server/dist/index.mjs' ,
273- // ` _shims` is the packages' internal runtime-selection self-reference;
274- // pin it to the Node build under jest.
275- ' ^@modelcontextprotocol/client/_shims$' : ' < rootDir> /node_modules/@modelcontextprotocol/client/dist/shimsNode.mjs' ,
276- ' ^@modelcontextprotocol/server/_shims$' : ' < rootDir> /node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs' ,
277- },
278- ```
254+ # ### CommonJS test runners (Jest)
279255
280- The entries are exact-anchored — add one per subpath you import (`/stdio` →
281- `dist/stdio.mjs`, `/validators/cf-worker` → `dist/validators/cfWorker.mjs`) and one for
282- `@modelcontextprotocol/core` (`dist/index.js`) if you import the raw schemas. The
283- `_shims` mappings are required whenever the matching root mapping is present: the dist
284- entry files import `@modelcontextprotocol/client/_shims` (a package self-reference)
285- internally, and that specifier fails CJS resolution the same way. In a hoisted
286- monorepo, point the paths at the `node_modules` directory your package manager actually
287- installs into.
256+ v2 ships a CommonJS build, so CJS test runners resolve the packages natively through the
257+ ` require` export condition — Jest (including ` next/jest` setups) no longer needs a
258+ ` moduleNameMapper` workaround to import ` @modelcontextprotocol/* ` . If you carried a
259+ v1-era mapping that pinned these packages to their ` dist/* .mjs` files, remove it. Vitest
260+ and native Node ESM are unaffected.
288261
289262# ### Bundlers: nested `zod` copies in zod@3-pinned monorepos
290263
0 commit comments