Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/cjs-support-v2-packages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@modelcontextprotocol/server': patch
'@modelcontextprotocol/client': patch
'@modelcontextprotocol/core': patch
'@modelcontextprotocol/server-legacy': patch
'@modelcontextprotocol/codemod': patch
'@modelcontextprotocol/express': patch
'@modelcontextprotocol/hono': patch
'@modelcontextprotocol/fastify': patch
'@modelcontextprotocol/node': patch
---
Comment on lines +1 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The changeset records all nine packages as patch, but this PR is a feat that adds new backwards-compatible public surface (a require export condition, .cjs/.d.cts artifacts, and a main field on every package), which conventionally warrants a minor bump. Since the packages are in beta pre mode the published version is the same either way, but the generated CHANGELOG will list this feature under "Patch Changes" — consider changing the changeset entries to minor.

Extended reasoning...

What the issue is. .changeset/cjs-support-v2-packages.md marks all nine packages (server, client, core, server-legacy, codemod, express, hono, fastify, node) as patch. The PR itself is titled feat(packaging) and adds new backwards-compatible functionality: every package gains a require condition on each export leaf, new .cjs/.d.cts build artifacts, and a new main field for bare-require fallback. Under semver conventions, a new backwards-compatible feature is a minor bump, not a patch.

Why this is a surface addition, not a fix. The repo's own topology pin test (packages/core-internal/test/packageTopologyPins.test.ts) describes a CJS build as "a new public surface" — i.e. the project already treats dual-format output as an addition to the public API contract, not an internal detail. Repo precedent also points to minor for feature changesets even during the prerelease cycle: the toWebRequest export (#2390), SdkHttpError, and the auth-iss changesets were all recorded as minor, and packages/server/CHANGELOG.md already has "Minor Changes" sections from the 2.0.0-alpha/beta cycle (e.g. #2286, #1689). patch in this repo is used for docs and fixes.

Concrete walk-through of the effect. The workspace is in changesets pre mode (.changeset/pre.json, tag beta), and all nine packages are at 2.0.0-beta.1. When the release PR is generated: (1) changesets reads this file and sees patch for each package; (2) because semver-inc of a prerelease version yields the next prerelease regardless of bump type, the published version is 2.0.0-beta.2 whether the entry says patch or minor — so no version-number harm; (3) however, the changelog generator groups entries by bump type, so the "Ship CommonJS builds alongside ESM…" entry lands under a "Patch Changes" heading in each package's CHANGELOG.md. Consumers scanning the changelog for new capabilities would not expect a new require() entry point to be listed as a patch, and the release notes would misrepresent the nature of the change after the prerelease exits.

Why nothing else catches it. changeset-bot only verifies that a changeset exists and reports the declared bump types (its comment on this PR faithfully lists all nine as "Patch"); nothing validates that the bump type matches the semantic nature of the change.

How to fix. Change each frontmatter entry in .changeset/cjs-support-v2-packages.md from patch to minor (nine one-word edits). No other change is needed; the changeset body text is accurate.

Impact assessment. This is purely a changelog-categorization/convention issue — no build, runtime, or version-resolution behavior is affected, and in pre mode the published version is identical either way. It should not block merge; it's just worth a quick edit so the release notes categorize the CJS support as the feature it is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meh doesn't matter imo


Ship CommonJS builds alongside ESM. Each package now emits both `.mjs`/`.d.mts`
and `.cjs`/`.d.cts` (via tsdown `format: ['esm', 'cjs']`), and its `exports` map
adds a `require` condition so `require('@modelcontextprotocol/…')` works from
CommonJS consumers. Output extensions are normalized across all packages
(`@modelcontextprotocol/core` moves from `.js`/`.d.ts` to `.mjs`/`.d.mts`); the
public import paths are unchanged.
2 changes: 1 addition & 1 deletion docs/behavior-surface-pins.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CI pass — that reopens the silent-drift hole the pin exists to close.
| --- | --- |
| Wire error-code tables, error classes, version constants | `packages/core-internal/test/types/errorSurfacePins.test.ts` |
| Schema strict/strip/loose boundaries, key existence | `packages/core-internal/test/types/schemaBoundaryPins.test.ts` |
| Published package set, export maps, ESM-only topology | `packages/core-internal/test/packageTopologyPins.test.ts` |
| Published package set, export maps | `packages/core-internal/test/packageTopologyPins.test.ts` |
| stdio environment-inheritance safelist | `packages/client/test/client/stdioEnvPins.test.ts` |
| 2025-11-25 wire method-registry membership, schema identity | `packages/core-internal/test/types/registryPins.test.ts` |

Expand Down
81 changes: 65 additions & 16 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,89 @@
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
Comment thread
claude[bot] marked this conversation as resolved.
},
"./stdio": {
"types": "./dist/stdio.d.mts",
"import": "./dist/stdio.mjs"
"import": {
"types": "./dist/stdio.d.mts",
"default": "./dist/stdio.mjs"
},
"require": {
"types": "./dist/stdio.d.cts",
"default": "./dist/stdio.cjs"
}
},
"./validators/ajv": {
"types": "./dist/validators/ajv.d.mts",
"import": "./dist/validators/ajv.mjs"
"import": {
"types": "./dist/validators/ajv.d.mts",
"default": "./dist/validators/ajv.mjs"
},
"require": {
"types": "./dist/validators/ajv.d.cts",
"default": "./dist/validators/ajv.cjs"
}
},
"./validators/cf-worker": {
"types": "./dist/validators/cfWorker.d.mts",
"import": "./dist/validators/cfWorker.mjs"
"import": {
"types": "./dist/validators/cfWorker.d.mts",
"default": "./dist/validators/cfWorker.mjs"
},
"require": {
"types": "./dist/validators/cfWorker.d.cts",
"default": "./dist/validators/cfWorker.cjs"
}
},
"./_shims": {
"workerd": {
"types": "./dist/shimsWorkerd.d.mts",
"import": "./dist/shimsWorkerd.mjs"
"import": {
"types": "./dist/shimsWorkerd.d.mts",
"default": "./dist/shimsWorkerd.mjs"
},
"require": {
"types": "./dist/shimsWorkerd.d.cts",
"default": "./dist/shimsWorkerd.cjs"
}
},
"browser": {
"types": "./dist/shimsBrowser.d.mts",
"import": "./dist/shimsBrowser.mjs"
"import": {
"types": "./dist/shimsBrowser.d.mts",
"default": "./dist/shimsBrowser.mjs"
},
"require": {
"types": "./dist/shimsBrowser.d.cts",
"default": "./dist/shimsBrowser.cjs"
}
},
"node": {
"types": "./dist/shimsNode.d.mts",
"import": "./dist/shimsNode.mjs"
"import": {
"types": "./dist/shimsNode.d.mts",
"default": "./dist/shimsNode.mjs"
},
"require": {
"types": "./dist/shimsNode.d.cts",
"default": "./dist/shimsNode.cjs"
}
},
"default": {
"types": "./dist/shimsNode.d.mts",
"import": "./dist/shimsNode.mjs"
"import": {
"types": "./dist/shimsNode.d.mts",
"default": "./dist/shimsNode.mjs"
},
"require": {
"types": "./dist/shimsNode.d.cts",
"default": "./dist/shimsNode.cjs"
}
}
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"typesVersions": {
"*": {
Expand Down
3 changes: 2 additions & 1 deletion packages/client/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default defineConfig({
'src/validators/ajv.ts',
'src/validators/cfWorker.ts'
],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
11 changes: 9 additions & 2 deletions packages/codemod/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,17 @@
},
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"files": [
"dist"
],
Expand Down
3 changes: 2 additions & 1 deletion packages/codemod/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { defineConfig } from 'tsdown';
export default defineConfig({
failOnWarn: 'ci-only',
entry: ['src/cli.ts', 'src/index.ts'],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
13 changes: 0 additions & 13 deletions packages/core-internal/test/packageTopologyPins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,10 @@
});

test('export-map keys are pinned exactly', () => {
expect(Object.keys(manifest.exports ?? {})).toEqual(expected.exportKeys);
});

test('ships ESM only', () => {
expect(manifest.type).toBe('module');
// No entry may grow a 'require' condition: the v2 packages are
// ESM-only by design (a CJS build would be a new public surface).
const conditionsOf = (entry: unknown): string[] =>
entry !== null && typeof entry === 'object'
? Object.entries(entry).flatMap(([key, value]) => [key, ...conditionsOf(value)])
: [];
for (const entry of Object.values(manifest.exports ?? {})) {
expect(conditionsOf(entry)).not.toContain('require');
}
});

test('publishes only dist', () => {

Check warning on line 78 in packages/core-internal/test/packageTopologyPins.test.ts

View check run for this annotation

Claude / Claude Code Review

ESM-only pin deleted instead of replaced with dual ESM/CJS pin

The `ships ESM only` pin was deleted outright rather than rewritten to pin the new deliberate dual ESM/CJS surface — the `type: 'module'` assertion is gone and nothing now pins the newly added `require` branches, `.cjs` artifacts, or `main` field, so a future accidental regression back to ESM-only would pass CI silently. Consider replacing the deleted test with one asserting each export leaf has both `import` and `require` branches, `main` is present, and `type` stays `'module'`, per the mainten
Comment thread
claude[bot] marked this conversation as resolved.
expect(manifest.files).toEqual(['dist']);
});

Expand Down
13 changes: 10 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"types": "./dist/index.d.ts",
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"files": [
"dist"
],
Expand Down
3 changes: 2 additions & 1 deletion packages/core/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { defineConfig } from 'tsdown';
export default defineConfig({
failOnWarn: 'ci-only',
entry: ['src/index.ts'],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
11 changes: 9 additions & 2 deletions packages/middleware/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"files": [
"dist"
Expand Down
3 changes: 2 additions & 1 deletion packages/middleware/express/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { defineConfig } from 'tsdown';
export default defineConfig({
failOnWarn: 'ci-only',
entry: ['src/index.ts'],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
11 changes: 9 additions & 2 deletions packages/middleware/fastify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"files": [
"dist"
Expand Down
3 changes: 2 additions & 1 deletion packages/middleware/fastify/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { defineConfig } from 'tsdown';
export default defineConfig({
failOnWarn: 'ci-only',
entry: ['src/index.ts'],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
11 changes: 9 additions & 2 deletions packages/middleware/hono/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"files": [
"dist"
Expand Down
3 changes: 2 additions & 1 deletion packages/middleware/hono/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { defineConfig } from 'tsdown';
export default defineConfig({
failOnWarn: 'ci-only',
entry: ['src/index.ts'],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
11 changes: 9 additions & 2 deletions packages/middleware/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"files": [
"dist"
Expand Down
3 changes: 2 additions & 1 deletion packages/middleware/node/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export default defineConfig({
entry: ['src/index.ts'],

// 2. Output Configuration
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true, // Recommended: Cleans 'dist' before building
sourcemap: true,
Expand Down
31 changes: 25 additions & 6 deletions packages/server-legacy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,37 @@
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./sse": {
"types": "./dist/sse/index.d.mts",
"import": "./dist/sse/index.mjs"
"import": {
"types": "./dist/sse/index.d.mts",
"default": "./dist/sse/index.mjs"
},
"require": {
"types": "./dist/sse/index.d.cts",
"default": "./dist/sse/index.cjs"
}
},
"./auth": {
"types": "./dist/auth/index.d.mts",
"import": "./dist/auth/index.mjs"
"import": {
"types": "./dist/auth/index.d.mts",
"default": "./dist/auth/index.mjs"
},
"require": {
"types": "./dist/auth/index.d.cts",
"default": "./dist/auth/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.mts",
"typesVersions": {
"*": {
Expand Down
3 changes: 2 additions & 1 deletion packages/server-legacy/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { defineConfig } from 'tsdown';
export default defineConfig({
failOnWarn: 'ci-only',
entry: ['src/index.ts', 'src/sse/index.ts', 'src/auth/index.ts'],
format: ['esm'],
format: ['esm', 'cjs'],
fixedExtension: true,
outDir: 'dist',
clean: true,
sourcemap: true,
Expand Down
Loading
Loading