modelcontextprotocol / modelcontextprotocol/typescript-sdk
[v2] Declaration source maps (.d.cts.map/.d.mts.map) reference unshipped src/ with empty sourcesContent — dangling for consumers
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
What happened?
Every declaration source map in the published v2-beta packages points at source
files that aren't in the tarball, and carries no inline source content, so the
maps resolve to nothing on a consumer's machine.
Checked @modelcontextprotocol/core, @modelcontextprotocol/server, and
@modelcontextprotocol/client at 2.0.0-beta.4 (current latest/beta). Across
the 38 declaration maps (.d.cts.map/.d.mts.map — 4 core + 16 server + 18
client, ~0.9 MB), the sources fields hold 180 first-party references —
../src/*.ts (own package) and, in server/client, ../../core-internal/src/*.ts —
and 176 of those 180 have no embedded source content:
- 30 of the 38 maps have
sourcesContent: [](empty); - the other 8 (the
ajvProvider*andtypes*maps in server/client) do carry a
non-emptysourcesContent, but it only holds the bundled third-party sources
(ajv/json-schema-typedfromnode_modules) — their first-party../src/
../../core-internal/srcentries are stillnull.
Meanwhile package.json is "files": ["dist"] (no src/ shipped) and
@modelcontextprotocol/core-internal is a "private": true workspace package
that is never published (404 on the registry). So those 176 references resolve to
nothing on a consumer's machine. The .d.ts files reference these maps
(//# sourceMappingURL=…d.mts.map on the last line), so declaration-map-aware
tooling will try to load them.
Cleanest example — @modelcontextprotocol/core, dist/internal.d.mts.map
(one of the 30 maps with an empty sourcesContent):
{ "version": 3, "file": "internal.d.mts",
"sources": ["../src/constants.ts"],
"sourcesContent": [] }
../src/constants.ts resolved from dist/ is @modelcontextprotocol/core/src/constants.ts,
which does not exist in the installed package. The larger first-party maps have
the same shape — e.g. @modelcontextprotocol/server's createMcpHandler-*.d.cts.map
(29 ../src sources, sourcesContent: []) and @modelcontextprotocol/client's
index-*.d.mts.map (22 ../src sources, sourcesContent: []).
The JS maps (.mjs.map/.cjs.map) are fine — rolldown embeds sourcesContent
for first-party code. It's specifically the tsc-emitted declaration maps that dangle.
What did you expect?
"Go to Definition" that lands in a .d.ts (or any tool that reads declaration
maps) to resolve to real source: either the referenced .ts is present in the
package, or the source is embedded in the map. As shipped, the declaration maps
are ~0.9 MB of dead weight that can only produce "source not found."
Code to reproduce
# published tarballs, no build needed
npm pack @modelcontextprotocol/core@2.0.0-beta.4
tar -xzf modelcontextprotocol-core-2.0.0-beta.4.tgz
cat package/dist/internal.d.mts.map
# sources: ["../src/constants.ts"], sourcesContent: []
ls package/src 2>&1
# No such file or directory ("files": ["dist"])
# real install: the referenced sources have neither embedded content nor a file
npm i @modelcontextprotocol/server@2.0.0-beta.4 # succeeds
node -e '
const fs=require("fs"),path=require("path");
const d="node_modules/@modelcontextprotocol/server/dist";
const m=JSON.parse(fs.readFileSync(d+"/index.d.mts.map","utf8"));
console.log("sourcesContent:", JSON.stringify(m.sourcesContent)); // []
for (const s of m.sources.slice(0,3))
console.log(s, "->", fs.existsSync(path.resolve(d,s)) ? "EXISTS" : "MISSING");
'
# sourcesContent: []
# ../src/server/completable.ts -> MISSING
# ../src/server/middleware/bearerAuth.ts -> MISSING
# (server/client index & createMcpHandler maps also reference
# ../../core-internal/src/* — a private, unpublished package)
SDK version
@modelcontextprotocol/core, @modelcontextprotocol/server,
@modelcontextprotocol/client — all 2.0.0-beta.4. server-legacy and
codemod are also published at 2.0.0-beta.4 and use the same tsdown build, so
they're likely affected too (I only opened core/server/client).
Root cause
The v2 build uses tsdown (packages/*/tsdown.config.ts) with sourcemap: true
and dts: { resolver: 'tsc' }. rolldown emits the JS bundles and inlines their
sourcesContent; tsc emits the .d.*.map files, and those reference external
.ts sources with no content. Declaration maps inherently point at external
source — tsc can't inline sources into a .d.ts.map: inlineSources only
affects the JS sourcemap and errors under --emitDeclarationOnly with TS5051
(checked on tsc 5.6.3), and the shipped declaration maps confirm the dts step
doesn't embed sources either. Combined with files: ["dist"] and the
../../core-internal/src references into a private, unpublished package, there's
no path on the consumer side to the sources.
Worth flagging: this is a different bug from #2233. That one is v1.x and about
dist/**/*.js.map; here it's the v2 .d.*.map files, and the inlineSources
approach that fixes the v1.x JS maps does not carry over to declaration maps
(I checked — it's a no-op for .d.ts.map).
Possible directions (would like a maintainer call before sending a PR)
CONTRIBUTING says to open an issue first for anything beyond a few-line fix, and
this needs a packaging decision rather than a mechanical patch:
- Ship
src/(add"src"to each package'sfiles) so../src/*.ts
resolves. Clean for own-package maps, but the server/client maps also point
into../../core-internal/src, which is private and bundled — those refs stay
broken unless core-internal's sources are also shipped/bundled or rewritten. - Stop emitting declaration maps (
dts: { sourcemap: false }) — drops the
feature but removes ~0.9 MB of maps that currently resolve to nothing. - Rewrite/strip the
core-internalsource refs in the emitted declaration
maps as part of the build (the same bundling seam PR #2339 already touches).
Happy to send the PR once you point at the option you want — I've already got the
repro and the before/after harness set up.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/*/tsdown.config.ts and each package's package.json, then reproduce the published behavior with the npm pack commands and declaration maps named in the issue. Compare the available packaging directions and validate that the selected approach leaves declaration maps resolvable for consumers, or removes the dangling maps, using the existing before/after harness.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100