normalizeHeaders references the bare global `HeadersInit`, breaking Node-only consumers with skipLibCheck: false

Open Beginner friendly
#2,568 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
typescript

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.

Description

bug P2 ready for work v1
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.

Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 12h
Merged PRs (30d)
3

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from modelcontextprotocol/typescript-sdk

All issues in modelcontextprotocol/typescript-sdk

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.