Packages

@modelcontextprotocol/client

2.3.1 · 5 Oct 2026Apache-2.04.7M / weekRepositoryHomepagenpmAtom feed

5 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.

  1. 2.3.1

    5 Oct 2026
  2. 2.3.0

    2 Oct 2026
    • One server per request. Server.connect() now rejects while the instance is already connected, and a stateless Streamable HTTP transport handles one request. Create the McpServer and the transport inside the request handler (or in the createMcpHandler factory) instead of sharing one instance across requests. Creating a server is cheap since #2889.

    • Redirects stay on the same origin. The HTTP client transports now follow a redirect only when it stays on the same origin (same scheme, host and port; http to https on the same host is allowed). A deployment whose endpoint redirects to another host or port either configures the final URL or sets redirectPolicy: 'follow' on the transport. In browsers, a redirected request fails unless that option is set.

    • New options, both off unless you set them. maxToolInputElements on McpServer limits the number of array elements and object members in a tool call's arguments. expectedResource on requireBearerAuth / verifyBearerToken accepts only tokens issued for this server (the token's audience); with Express, upgrade @modelcontextprotocol/express together with @modelcontextprotocol/server. (#2926, #2929)

  3. 2.2.0

    28 Sep 2026
    • deprecated

      Pass expectedIssuer to the machine-to-machine OAuth providers. Constructing ClientCredentialsProvider, PrivateKeyJwtProvider, StaticPrivateKeyJwtProvider or CrossAppAccessProvider without it is deprecated and logs a warning. Set it to the issuer of the authorization server the credentials were registered with.

    • fetchToken() checks which authorization server the client information belongs to. It throws AuthorizationServerMismatchError before sending anything when the provider's client information is bound to a different authorization server.

    • List calls return the whole list. listTools(), listPrompts(), listResources() and listResourceTemplates() called without a cursor now follow nextCursor until the server stops sending one. listMaxPages still caps the walk.

  4. 2.1.0

    23 Sep 2026
    • deprecated

      SdkError and SdkHttpError accept standard ErrorOptions as an optional fourth constructor argument and forward it to Error, so a wrapped error is reachable through the standard Error.cause chain. Version-negotiation probe failures (SdkErrorCode.EraNegotiationFailed) now use it: the underlying TypeError: fetch failed and the DNS or socket error beneath it surface via error.cause, so pino, Sentry, and util.inspect render ENOTFOUND / ECONNREFUSED / ETIMEDOUT instead of stopping at the SdkError (#2657). The previous error.data.cause slot is still populated for compatibility but is deprecated and slated for removal; read error.cause instead.

    • Add DPoP (RFC 9449 / SEP-1932) sender-constrained access token support to the client.

    • Treat request id 0 as a real id. Two guards tested a RequestId for truthiness, so the legal JSON-RPC ids 0 and '' were read as absent. Id 0 is not a corner case: the outbound request counter is zero-based, so it is the first id every peer assigns, which on the server→client leg is the first sampling/createMessage, elicitation/create, or roots/list a server sends.

      Absent is now the only value that means "no id".

  5. 2.0.0

    27 Jul 2026
    • Export the Protocol base class and mergeCapabilities from the @modelcontextprotocol/client and @modelcontextprotocol/server package roots, restoring the v1 import for consumers that subclass Protocol (e.g. the MCP Apps SDK). The client and server packages each bundle their own compiled copy of the class, so import it from one package consistently within a process.

      The codemod now rewrites Protocol and mergeCapabilities imports from shared/protocol.js to the client or server package root, like the module's other symbols, instead of dropping them with an action-required marker.

    • ConnectOptions.prior accepts a cached era verdict — the new exported type PriorDiscovery. { kind: 'modern', discover } adopts a previously obtained DiscoverResult with zero round trips; { kind: 'legacy' } skips the server/discover probe and runs the plain initialize handshake directly, for servers known out-of-band to be legacy — without pinning the client to mode: 'legacy': stop supplying the verdict and connect() falls back to the configured versionNegotiation mode (under 'auto', it re-probes and rediscovers an upgraded server). Freshness is the supplying host's responsibility — a stale legacy verdict succeeds silently against an upgraded server, so hosts must date cached legacy verdicts in their own storage and stop supplying them past their policy horizon. Persisted-blob plumbing is hardened: prior: null is treated as absent, the modern arm's discover payload is schema-validated before any connection state changes, and an unrecognized shape rejects with a typed SdkError(EraNegotiationFailed) instead of a TypeError.

    • The response cache now stores results as JSON-serialized documents (serialize on write, parse on read) instead of live object graphs isolated with structuredClone. Same mutation isolation, but no dependency on the structuredClone global — whose absence (jest+jsdom, Node < 17) previously made every cache write throw into the store-error swallow, silently disabling caching and output-schema lookups for the session. A value without a JSON representation now fails the write loudly to the error sink, and an undecodable document in an external store is reported, dropped, and read as a miss.

      Migration for custom ResponseCacheStore implementations: CacheEntry.value (and the set() entry value) is now string — persist and return it verbatim, JSON.parse to inspect. Entries persisted by a previous SDK version fail decode once (reported, dropped) and are rewritten on the next fetch.