Skip to content

[v2] Declaration source maps (.d.cts.map/.d.mts.map) reference unshipped src/ with empty sourcesContent — dangling for consumers #2491

Description

@zelinewang

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* and types* maps in server/client) do carry a
    non-empty sourcesContent, but it only holds the bundled third-party sources
    (ajv / json-schema-typed from node_modules) — their first-party ../src /
    ../../core-internal/src entries are still null.

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:

  1. Ship src/ (add "src" to each package's files) 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.
  2. Stop emitting declaration maps (dts: { sourcemap: false }) — drops the
    feature but removes ~0.9 MB of maps that currently resolve to nothing.
  3. Rewrite/strip the core-internal source refs in the emitted declaration
    maps as part of the build (the same bundling seam PR fix(middleware): stop bundling workspace type graphs into .d.ts (avoids dts-gen OOM) #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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    v2Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions