modelcontextprotocol / modelcontextprotocol/typescript-sdk
normalizeHeaders references the bare global `HeadersInit`, breaking Node-only consumers with skipLibCheck: false
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Summary
shared/transport.d.ts references the bare global HeadersInit, which is a DOM lib type. Node-only consumers compiling with "skipLibCheck": false and no "DOM" in lib fail to build.
File/line: dist/esm/shared/transport.d.ts:7
export declare function normalizeHeaders(headers: HeadersInit | undefined): Record<string, string>;
Reproduction
@modelcontextprotocol/sdk@1.30.0 + @types/node@22.20.1, with "lib": ["ES2023"], "types": ["node"], "skipLibCheck": false:
node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.d.ts(7,51):
error TS2304: Cannot find name 'HeadersInit'.
Affects every published 1.x from 1.23.0-beta.0 through 1.30.0 (current latest).
Why @types/node cannot fix this
@types/node@22's fetch-globals shim (node_modules/@types/node/web-globals/fetch.d.ts) binds Headers, Request, RequestInit, Response and ResponseInit globally from undici-types — but deliberately not HeadersInit.
Per the review thread on DefinitelyTyped PR #74414 (@Renegade334): HeadersInit is a web-IDL typedef, which maps to a TypeScript type alias, and type aliases cannot be declaration-merged — unlike the dictionary/interface-shaped Headers/Request/RequestInit/Response, which can. So @types/node structurally cannot bind this global the way it binds the others.
That leaves the fix on the SDK side: don't reference the bare global.
Suggested fix
Change the signature to avoid the DOM-only global:
export declare function normalizeHeaders(headers: RequestInit['headers'] | undefined): Record<string, string>;
This is same-behaviour and depends only on a global @types/node does bind. It's also exactly what the v2 rewrite already does (#1279).
Blast radius is small: src/shared/transport.ts and its compiled output — 2 files.
Why a 1.x backport matters
The v2 rewrite already fixes this, but v2 is outside the peer range that @anthropic-ai/claude-agent-sdk pins (^1.29.0), so consumers coming through the Agent SDK can't upgrade to it today. Without a 1.x backport, the only options are a local ambient shim, adding "DOM" to lib (which drags every browser global into a Node service), or skipLibCheck: true (which stops type-checking all declaration files).
Happy to open a PR for the one-line change if that's useful.
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
Inspect normalizeHeaders in src/shared/transport.ts and its compiled declaration at dist/esm/shared/transport.d.ts. Verify the Node-only reproduction with skipLibCheck false and no DOM lib, then confirm the published declaration no longer requires the unresolved global HeadersInit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100