Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions .changeset/legacy-module-resolution-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@modelcontextprotocol/client': patch
'@modelcontextprotocol/server': patch
'@modelcontextprotocol/node': patch
'@modelcontextprotocol/express': patch
---

Add top-level `types` field (and `typesVersions` on server for its subpath) so consumers on legacy `moduleResolution: "node"` can resolve type declarations. The `exports` map remains the source of truth for `nodenext`/`bundler` resolution.
3 changes: 2 additions & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@
"typescript-eslint": "catalog:devTools",
"vitest": "catalog:devTools",
"tsdown": "catalog:devTools"
}
},
"types": "./dist/index.d.mts"

Check failure on line 96 in packages/client/package.json

View check run for this annotation

Claude / Claude Code Review

Client package missing typesVersions for validators/cf-worker subpath

The client package also exports `./validators/cf-worker` (identical to server), but only server got a `typesVersions` mapping here. Under legacy `moduleResolution: "node"`, `import ... from '@modelcontextprotocol/client/validators/cf-worker'` will still fail with TS2307. The client package needs the same `typesVersions` block.
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 client package also exports ./validators/cf-worker (identical to server), but only server got a typesVersions mapping here. Under legacy moduleResolution: "node", import ... from '@modelcontextprotocol/client/validators/cf-worker' will still fail with TS2307. The client package needs the same typesVersions block.

Extended reasoning...

What's happening

This PR's stated goal is to make the v2 packages resolvable under TypeScript's legacy moduleResolution: "node" (Node10), which ignores the exports map. It does so by adding a top-level types field to each leaf package, plus a narrow typesVersions map "where subpaths exist". The server package gets:

"typesVersions": {
    "*": {
        "validators/cf-worker": ["dist/validators/cfWorker.d.mts"]
    }
}

However, packages/client/package.json has the exact same subpath in its exports map:

"./validators/cf-worker": {
    "types": "./dist/validators/cfWorker.d.mts",
    "import": "./dist/validators/cfWorker.mjs"
}

…yet client only received the top-level types field, not the typesVersions mapping.

Why legacy resolution can't find it without typesVersions

Under moduleResolution: "node":

  1. The exports map is ignored entirely.
  2. For the bare specifier @modelcontextprotocol/client, TS falls back to the top-level types field — which now works thanks to this PR.
  3. For the subpath @modelcontextprotocol/client/validators/cf-worker, TS consults typesVersions first; if absent, it tries to resolve the literal path node_modules/@modelcontextprotocol/client/validators/cf-worker(.d.ts|/index.d.ts).
  4. That path does not exist on disk — the actual file is dist/validators/cfWorker.d.mts (different directory, camelCase filename, .d.mts extension). Resolution fails with TS2307: Cannot find module '@modelcontextprotocol/client/validators/cf-worker'.

Step-by-step proof

Given a consumer with:

// tsconfig.json
{ "compilerOptions": { "moduleResolution": "node" } }
import { cfWorkerValidator } from '@modelcontextprotocol/client/validators/cf-worker';
  • TS reads @modelcontextprotocol/client/package.json, ignores exports.
  • No typesVersions field is present → no remapping.
  • TS looks for .../client/validators/cf-worker.d.ts, .../client/validators/cf-worker/index.d.ts, etc. → none exist (only dist/ is shipped per the files field).
  • Result: TS2307.

The same import against @modelcontextprotocol/server/validators/cf-worker does resolve after this PR, because server's typesVersions remaps it. So the PR fixes server but leaves client with the very bug it set out to address — a partial migration.

Impact

Any consumer on legacy moduleResolution: "node" who imports the client-side cf-worker validator will still hit TS2307. Since the PR description and changeset explicitly claim to cover "subpaths where they exist", this is an incomplete fix rather than an intentional omission.

Fix

Add the identical block to packages/client/package.json:

"typesVersions": {
    "*": {
        "validators/cf-worker": [
            "dist/validators/cfWorker.d.mts"
        ]
    }
}

The changeset text should also be updated to mention client (it currently says "and typesVersions on server for its subpath").

}
3 changes: 2 additions & 1 deletion packages/middleware/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@
"typescript": "catalog:devTools",
"typescript-eslint": "catalog:devTools",
"vitest": "catalog:devTools"
}
},
"types": "./dist/index.d.mts"

Check failure on line 67 in packages/middleware/express/package.json

View check run for this annotation

Claude / Claude Code Review

Fastify and Hono middleware packages missing types field

The sibling `@modelcontextprotocol/fastify` and `@modelcontextprotocol/hono` packages have the same `exports`-only shape as `express`/`node` but were not given a top-level `types` field (and are missing from the changeset). Consumers on legacy `moduleResolution: "node"` importing those two will still hit TS2307 — the fix should be applied uniformly across all four packages under `packages/middleware/`.
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 sibling @modelcontextprotocol/fastify and @modelcontextprotocol/hono packages have the same exports-only shape as express/node but were not given a top-level types field (and are missing from the changeset). Consumers on legacy moduleResolution: "node" importing those two will still hit TS2307 — the fix should be applied uniformly across all four packages under packages/middleware/.

Extended reasoning...

The stated goal of this PR is to make every published v2 package resolvable under TypeScript's legacy moduleResolution: "node" (Node10) by adding a top-level types field, since that resolution mode ignores the exports map. The PR applies this to client, server, middleware/express, and middleware/node, but skips two siblings that are structurally identical.

packages/middleware/fastify/package.json and packages/middleware/hono/package.json are both published ("private": false, listed in the pkg.pr.new output) and both declare types only via the conditional exports map:

"exports": {
    ".": {
        "types": "./dist/index.d.mts",
        "import": "./dist/index.mjs"
    }
}

Neither has a top-level "types" field. Under moduleResolution: "node", TypeScript does not read exports, so it falls back to looking for a top-level types/typings field, then index.d.ts at the package root. None of those exist, so the import fails.

Step-by-step proof:

  1. A consumer has tsconfig.json with "moduleResolution": "node".
  2. They write import { ... } from "@modelcontextprotocol/fastify".
  3. TypeScript ignores exports (Node10 mode), checks package.json for types/typings → absent.
  4. TypeScript probes node_modules/@modelcontextprotocol/fastify/index.d.ts → absent (only dist/ is shipped).
  5. Result: error TS2307: Cannot find module '@modelcontextprotocol/fastify' or its corresponding type declarations.
    The exact same path applies to @modelcontextprotocol/hono. Meanwhile @modelcontextprotocol/express and @modelcontextprotocol/node now resolve fine because this PR added "types": "./dist/index.d.mts" to them.

Nothing else covers this gap. The changeset-bot lists fastify/hono only because they get a transitive bump via the @modelcontextprotocol/server peer dep — the changeset file itself (.changeset/legacy-module-resolution-types.md) does not include them, and their package.json files are untouched in the diff.

Fix: add "types": "./dist/index.d.mts" to packages/middleware/fastify/package.json and packages/middleware/hono/package.json (same one-line change as express/node), and add both packages to the changeset frontmatter so they receive an explicit patch entry.

}
3 changes: 2 additions & 1 deletion packages/middleware/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@
"typescript": "catalog:devTools",
"typescript-eslint": "catalog:devTools",
"vitest": "catalog:devTools"
}
},
"types": "./dist/index.d.mts"
}
8 changes: 8 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,13 @@
"typescript": "catalog:devTools",
"typescript-eslint": "catalog:devTools",
"vitest": "catalog:devTools"
},
"types": "./dist/index.d.mts",
"typesVersions": {
"*": {
"validators/cf-worker": [
"dist/validators/cfWorker.d.mts"
]
}
}
}
Loading