docs: replace the typedoc site with VitePress (v2 + v1 reface) (#2395)

This commit is contained in:
Felix Weinberger 2026-06-30 16:03:19 +01:00 • committed by GitHub
commit 732abe0ac4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1996 additions and 270 deletions

View file

@ -37,8 +37,11 @@ jobs:
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Generate multi-version docs
run: bash scripts/generate-multidoc.sh tmp/docs-combined
- name: Install dependencies
run: pnpm install
- name: Build multi-version docs site
run: bash scripts/build-docs-site.sh tmp/docs-combined
- name: Configure Pages
uses: actions/configure-pages@v6

9
.gitignore vendored
View file

@ -50,6 +50,15 @@ dist/
# Git worktrees for local doc generation
.worktrees/
# Generated docs-site artifacts
docs/index.md
docs/api/
docs/.vitepress/cache/
docs/.vitepress/dist/
docs/v1/content/
docs/v1/.vitepress/cache/
docs/v1/.vitepress/dist/
# Conformance test results
results/

View file

@ -150,14 +150,16 @@ The complete code for each tutorial is in [`examples/server-quickstart/`](https:
### Building docs locally
To generate the API reference documentation locally:
To work on the documentation site locally:
```bash
pnpm docs # Generate V2 docs only (output: tmp/docs/)
pnpm docs:multi # Generate combined V1 + V2 docs (output: tmp/docs-combined/)
pnpm docs:api # Generate the API reference markdown (output: docs/api/)
pnpm docs:dev # Start the VitePress dev server for the V2 site
pnpm docs:build # Build the V2 site (output: docs/.vitepress/dist/)
pnpm docs:multi # Build the combined V1 + V2 site (output: tmp/docs-combined/)
```
The `docs:multi` script checks out both the `v1.x` and `main` branches via git worktrees, builds each, and produces a combined site with V1 docs at the root and V2 docs under `/v2/`.
The `docs:multi` script builds the V2 site from the current checkout, checks out the `v1.x` branch via a git worktree to build the V1 site, and produces a combined site with V1 docs at the root and V2 docs under `/v2/`.
## v1 (legacy) documentation and fixes

View file

@ -0,0 +1,89 @@
import { existsSync, readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig, type DefaultTheme } from 'vitepress';
const docsDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
/**
* The API Reference sidebar is generated by `pnpm docs:api` (typedoc + typedoc-vitepress-theme)
* into docs/api/typedoc-sidebar.json. When it is missing (e.g. `docs:dev` before `docs:api`),
* fall back to an empty group with a hint instead of crashing.
*/
function apiSidebarItems(): DefaultTheme.SidebarItem[] {
const sidebarPath = resolve(docsDir, 'api/typedoc-sidebar.json');
if (!existsSync(sidebarPath)) {
console.warn(`[docs] ${sidebarPath} not found — run \`pnpm docs:api\` first to populate the API Reference section.`);
return [];
}
return JSON.parse(readFileSync(sidebarPath, 'utf8'));
}
export default defineConfig({
title: 'MCP TypeScript SDK',
description: 'The TypeScript SDK implementation of the Model Context Protocol specification.',
base: '/v2/',
srcExclude: ['v1/**', 'behavior-surface-pins.md'],
sitemap: { hostname: 'https://ts.sdk.modelcontextprotocol.io/v2/' },
markdown: {
config(md) {
// Spec-generated JSDoc (packages/core-internal/src/types/spec.types.*.ts) carries
// site-root-relative links like /specification/draft/basic/index#meta that are meant
// to resolve on modelcontextprotocol.io. Rewrite them at render time so they work on
// this site (and so the dead-link check sees them as external).
const orig = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const href = tokens[idx].attrGet('href');
if (href?.startsWith('/specification/')) {
tokens[idx].attrSet('href', `https://modelcontextprotocol.io${href}`);
}
return orig(tokens, idx, options, env, self);
};
}
},
themeConfig: {
nav: [
{ text: 'Guide', link: '/server-quickstart', activeMatch: '^/(server|client|faq)' },
{ text: 'Migration', link: '/migration/', activeMatch: '^/migration/' },
{ text: 'API Reference', link: '/api/', activeMatch: '^/api/' },
{ text: 'V1 Docs', link: 'https://ts.sdk.modelcontextprotocol.io/' }
],
sidebar: [
{
text: 'Getting started',
items: [
{ text: 'Server Quickstart', link: '/server-quickstart' },
{ text: 'Client Quickstart', link: '/client-quickstart' }
]
},
{
text: 'Guides',
items: [
{ text: 'Server', link: '/server' },
{ text: 'Client', link: '/client' }
]
},
{
text: 'Migration',
items: [
{ text: 'Overview', link: '/migration/' },
{ text: 'Upgrade to v2', link: '/migration/upgrade-to-v2' },
{ text: '2026-07-28 protocol support', link: '/migration/support-2026-07-28' }
]
},
{
text: 'FAQ',
items: [{ text: 'FAQ', link: '/faq' }]
},
{
text: 'API Reference',
collapsed: true,
items: apiSidebarItems()
}
],
outline: { level: [2, 3] },
search: { provider: 'local' },
socialLinks: [{ icon: 'github', link: 'https://github.com/modelcontextprotocol/typescript-sdk' }]
}
});

View file

@ -0,0 +1,6 @@
<template>
<div class="version-banner">
You are viewing the in-development v2 documentation. The stable v1 docs are at
<a href="https://ts.sdk.modelcontextprotocol.io/">ts.sdk.modelcontextprotocol.io</a>.
</div>
</template>

View file

@ -0,0 +1,148 @@
/**
* Shared theme for the MCP TypeScript SDK docs (v2 and v1 sites).
*
* Visual pass modeled on the Python SDK docs (py.sdk.modelcontextprotocol.io),
* which use mkdocs-material with a monochrome palette: primary/accent black on
* light, white on slate dark. We map that onto VitePress brand variables.
*/
/* ---------------------------------------------------------------- branding */
:root {
/* Monochrome brand: near-black on light backgrounds. */
--vp-c-brand-1: #1a1a1a;
--vp-c-brand-2: #333333;
--vp-c-brand-3: #1f1f1f;
--vp-c-brand-soft: rgba(0, 0, 0, 0.08);
/* Restrained accent for links — dark with a faint blue cast, like
material's black-primary link color. */
--vp-c-text-link: #21409a;
/* Neutral grays. */
--vp-c-gray-1: #dddde3;
--vp-c-gray-2: #e4e4e9;
--vp-c-gray-3: #ebebef;
/* Buttons: solid black, like material's primary buttons. */
--vp-button-brand-bg: #1a1a1a;
--vp-button-brand-border: #1a1a1a;
--vp-button-brand-text: #ffffff;
--vp-button-brand-hover-bg: #333333;
--vp-button-brand-hover-border: #333333;
--vp-button-brand-hover-text: #ffffff;
--vp-button-brand-active-bg: #000000;
--vp-button-brand-active-border: #000000;
--vp-button-brand-active-text: #ffffff;
/* Home hero: plain text, no gradient. */
--vp-home-hero-name-color: var(--vp-c-text-1);
/* Height reserved for the fixed version banner (layout-top slot). */
--vp-layout-top-height: 36px;
}
.dark {
/* Monochrome brand: near-white on dark backgrounds (material "slate"). */
--vp-c-brand-1: #e8e8e8;
--vp-c-brand-2: #cccccc;
--vp-c-brand-3: #dddddd;
--vp-c-brand-soft: rgba(255, 255, 255, 0.12);
--vp-c-text-link: #9db4e8;
--vp-button-brand-bg: #e8e8e8;
--vp-button-brand-border: #e8e8e8;
--vp-button-brand-text: #1a1a1a;
--vp-button-brand-hover-bg: #ffffff;
--vp-button-brand-hover-border: #ffffff;
--vp-button-brand-hover-text: #1a1a1a;
--vp-button-brand-active-bg: #cccccc;
--vp-button-brand-active-border: #cccccc;
--vp-button-brand-active-text: #1a1a1a;
}
/* Links: restrained accent, underline on hover only (material-ish). */
.vp-doc a {
color: var(--vp-c-text-link);
text-decoration: none;
font-weight: 500;
}
.vp-doc a:hover {
text-decoration: underline;
}
/* ------------------------------------------------------------- admonitions */
/*
* Material-style admonitions: left accent border + tinted background.
* Covers VitePress custom containers and GitHub-style alerts (> [!NOTE] …),
* which VitePress renders with the same .custom-block classes.
*/
.vp-doc .custom-block {
border: none;
border-left: 4px solid var(--vp-c-divider);
border-radius: 2px;
padding: 12px 16px;
}
.vp-doc .custom-block .custom-block-title {
font-weight: 700;
}
.vp-doc .custom-block.info,
.vp-doc .custom-block.note {
border-left-color: #448aff;
background-color: rgba(68, 138, 255, 0.08);
}
.vp-doc .custom-block.tip,
.vp-doc .custom-block.important {
border-left-color: #00bfa5;
background-color: rgba(0, 191, 165, 0.08);
}
.vp-doc .custom-block.warning,
.vp-doc .custom-block.caution {
border-left-color: #ff9100;
background-color: rgba(255, 145, 0, 0.08);
}
.vp-doc .custom-block.danger {
border-left-color: #ff5252;
background-color: rgba(255, 82, 82, 0.08);
}
.vp-doc .custom-block.details {
border-left-color: var(--vp-c-divider);
background-color: var(--vp-c-bg-soft);
}
/* -------------------------------------------------------------- the banner */
.version-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 35;
height: var(--vp-layout-top-height);
display: flex;
align-items: center;
justify-content: center;
padding: 0 16px;
overflow: hidden;
background-color: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-divider);
color: var(--vp-c-text-2);
font-size: 13px;
line-height: 1.3;
text-align: center;
}
.version-banner a {
color: var(--vp-c-text-1);
text-decoration: underline;
margin-left: 4px;
}

View file

@ -0,0 +1,15 @@
import type { Theme } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import { h } from 'vue';
import Banner from './Banner.vue';
import './custom.css';
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-top': () => h(Banner)
});
}
} satisfies Theme;

View file

@ -54,7 +54,7 @@ import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
### Streamable HTTP
For remote HTTP servers, use {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport}:
For remote HTTP servers, use `StreamableHTTPClientTransport`:
```ts source="../examples/guides/clientGuide.examples.ts#connect_streamableHttp"
const client = new Client({ name: 'my-client', version: '1.0.0' });
@ -68,7 +68,7 @@ For a full interactive client over Streamable HTTP, see [`repl/client.ts`](https
### stdio
For local, process-spawned servers (Claude Desktop, CLI tools), use {@linkcode @modelcontextprotocol/client!client/stdio.StdioClientTransport | StdioClientTransport}. The transport spawns the server process and communicates over stdin/stdout:
For local, process-spawned servers (Claude Desktop, CLI tools), use `StdioClientTransport`. The transport spawns the server process and communicates over stdin/stdout:
```ts source="../examples/guides/clientGuide.examples.ts#connect_stdio"
const client = new Client({ name: 'my-client', version: '1.0.0' });
@ -83,8 +83,7 @@ await client.connect(transport);
### SSE fallback for legacy servers
To support both modern Streamable HTTP and legacy SSE servers, try {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport} first and fall back to {@linkcode
@modelcontextprotocol/client!client/sse.SSEClientTransport | SSEClientTransport} on failure:
To support both modern Streamable HTTP and legacy SSE servers, try `StreamableHTTPClientTransport` first and fall back to `SSEClientTransport` on failure:
```ts source="../examples/guides/clientGuide.examples.ts#connect_sseFallback"
const baseUrl = new URL(url);
@ -125,13 +124,13 @@ client.getNegotiatedProtocolVersion(); // '2026-07-28' or '2025-11-25'
- **`mode: { pin: '2026-07-28' }`** — modern era at exactly that revision; no fallback. Against a 2025-only server `connect()` rejects with a typed error. Use `pin` where a silent downgrade would be worse than an error (tests, CI, servers you control).
Once a modern era is negotiated, the client automatically attaches the per-request `_meta` envelope (the reserved protocol-version / client-info / client-capabilities keys) to every outgoing request and notification. You can also configure negotiation pre-connect on an
already-constructed instance via {@linkcode @modelcontextprotocol/client!client/client.Client#setVersionNegotiation | client.setVersionNegotiation()}. See the [2026-07-28 support guide › Probe policy](./migration/support-2026-07-28.md#probe-policy) for the full failure semantics and probe-timeout behavior.
already-constructed instance via `client.setVersionNegotiation()`. See the [2026-07-28 support guide › Probe policy](./migration/support-2026-07-28.md#probe-policy) for the full failure semantics and probe-timeout behavior.
The version lists come from `ClientOptions.supportedProtocolVersions`: under `'auto'`, its 2026-era entries form the modern offer (default: the SDK's modern list), and a list with no 2025-era entry removes the legacy fallback; `connect()` rejects with `SdkError(EraNegotiationFailed)` instead of downgrading. The same modern subset bounds the overlap check of `connect({ prior })`.
#### Skipping the probe: `connect({ prior })`
A gateway, proxy, or worker fleet that already knows the server's `server/discover` advertisement can skip the probe entirely. Pass a previously-obtained {@linkcode @modelcontextprotocol/client!index.DiscoverResult | DiscoverResult} via
{@linkcode @modelcontextprotocol/client!client/client.ConnectOptions | ConnectOptions.prior} and `connect()` adopts it directly with **zero round trips** — the 2026-07-28 protocol is stateless on HTTP, so once the advertisement is known there is nothing left to negotiate.
A gateway, proxy, or worker fleet that already knows the server's `server/discover` advertisement can skip the probe entirely. Pass a previously-obtained `DiscoverResult` via
`ConnectOptions.prior` and `connect()` adopts it directly with **zero round trips** — the 2026-07-28 protocol is stateless on HTTP, so once the advertisement is known there is nothing left to negotiate.
```ts source="../examples/guides/clientGuide.examples.ts#Client_connect_prior"
// Probe once (here via the 'auto'-mode connect), persist the result …
@ -144,15 +143,15 @@ const worker = new Client({ name: 'worker', version: '1.0.0' });
await worker.connect(new StreamableHTTPClientTransport(url), { prior: JSON.parse(persisted) });
```
{@linkcode @modelcontextprotocol/client!client/client.Client#getDiscoverResult | client.getDiscoverResult()} returns the value that the `'auto'`/pinned probe path, an explicit {@linkcode @modelcontextprotocol/client!client/client.Client#discover | client.discover()} call, or a
`client.getDiscoverResult()` returns the value that the `'auto'`/pinned probe path, an explicit `client.discover()` call, or a
prior `connect({ prior })` recorded; it round-trips through `JSON.stringify`/`JSON.parse`. `connect({ prior })` is **2026-07-28+ only** — it rejects with `SdkError(EraNegotiationFailed)` when the supplied result and the client share no modern revision. Only reuse a persisted
`DiscoverResult` across clients that present the **same authorization context** as the one that obtained it. See the [`gateway/` example](../examples/gateway/README.md) for the full probe-once / connect-many pattern with a server-side proof.
`DiscoverResult` across clients that present the **same authorization context** as the one that obtained it. See the [`gateway/` example](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/gateway/README.md) for the full probe-once / connect-many pattern with a server-side proof.
Unlike an `'auto'`/pinned connect, `connect({ prior })` never auto-opens a `subscriptions/listen` stream. Workers on this path are assumed request-only. A configured `listChanged` option registers its handlers but stays silent. Call [`client.listen(filter)`](#subscription-streams-2026-07-28) yourself if a prior-connected client should observe changes.
### Disconnecting
Call {@linkcode @modelcontextprotocol/client!client/client.Client#close | await client.close() } to disconnect. Pending requests are rejected with a {@linkcode @modelcontextprotocol/client!index.SdkErrorCode.ConnectionClosed | CONNECTION_CLOSED} error.
Call `await client.close()` to disconnect. Pending requests are rejected with a `CONNECTION_CLOSED` error.
For Streamable HTTP, terminate the server-side session first (per the MCP specification):
@ -178,7 +177,7 @@ console.log(systemPrompt);
### Extension capabilities
The negotiated server capabilities include `extensions` — a map from extension identifier to that extension's settings object. Read it after connecting via {@linkcode @modelcontextprotocol/client!client/client.Client#getServerCapabilities | client.getServerCapabilities()}:
The negotiated server capabilities include `extensions` — a map from extension identifier to that extension's settings object. Read it after connecting via `client.getServerCapabilities()`:
```ts source="../examples/guides/clientGuide.examples.ts#extensionCapabilities_read"
const extensions = client.getServerCapabilities()?.extensions ?? {};
@ -192,13 +191,11 @@ See [Extension capabilities](./server.md#extension-capabilities) in the server g
## Authentication
MCP servers can require authentication before accepting client connections (see [Authorization](https://modelcontextprotocol.io/specification/latest/basic/authorization) in the MCP specification). Pass an {@linkcode @modelcontextprotocol/client!client/auth.AuthProvider |
AuthProvider} to {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport}. The transport calls `token()` before every request and `onUnauthorized()` (if provided) on 401, then retries once.
MCP servers can require authentication before accepting client connections (see [Authorization](https://modelcontextprotocol.io/specification/latest/basic/authorization) in the MCP specification). Pass an `AuthProvider` to `StreamableHTTPClientTransport`. The transport calls `token()` before every request and `onUnauthorized()` (if provided) on 401, then retries once.
### Bearer tokens
For servers that accept bearer tokens managed outside the SDK — API keys, tokens from a gateway or proxy, service-account credentials — implement only `token()`. With no `onUnauthorized()`, a 401 throws {@linkcode @modelcontextprotocol/client!client/auth.UnauthorizedError |
UnauthorizedError} immediately:
For servers that accept bearer tokens managed outside the SDK — API keys, tokens from a gateway or proxy, service-account credentials — implement only `token()`. With no `onUnauthorized()`, a 401 throws `UnauthorizedError` immediately:
```ts source="../examples/guides/clientGuide.examples.ts#auth_tokenProvider"
const authProvider: AuthProvider = { token: async () => getStoredToken() };
@ -210,7 +207,7 @@ See [`simpleTokenProvider.ts`](https://github.com/modelcontextprotocol/typescrip
### Client credentials
{@linkcode @modelcontextprotocol/client!client/authExtensions.ClientCredentialsProvider | ClientCredentialsProvider} handles the `client_credentials` grant flow for service-to-service communication:
`ClientCredentialsProvider` handles the `client_credentials` grant flow for service-to-service communication:
```ts source="../examples/guides/clientGuide.examples.ts#auth_clientCredentials"
const authProvider = new ClientCredentialsProvider({
@ -227,7 +224,7 @@ await client.connect(transport);
### Private key JWT
{@linkcode @modelcontextprotocol/client!client/authExtensions.PrivateKeyJwtProvider | PrivateKeyJwtProvider} signs JWT assertions for the `private_key_jwt` token endpoint auth method, avoiding a shared client secret:
`PrivateKeyJwtProvider` signs JWT assertions for the `private_key_jwt` token endpoint auth method, avoiding a shared client secret:
```ts source="../examples/guides/clientGuide.examples.ts#auth_privateKeyJwt"
const authProvider = new PrivateKeyJwtProvider({
@ -244,7 +241,7 @@ Server only implements `client_secret_basic`/`client_secret_post`, so there is n
### Full OAuth with user authorization
For user-facing applications, implement the {@linkcode @modelcontextprotocol/client!client/auth.OAuthClientProvider | OAuthClientProvider} interface to handle the full authorization code flow (redirects, code verifiers, token storage, dynamic client registration). Key persisted
For user-facing applications, implement the `OAuthClientProvider` interface to handle the full authorization code flow (redirects, code verifiers, token storage, dynamic client registration). Key persisted
client credentials by the `ctx.issuer` passed to `clientInformation()` / `saveClientInformation()` so credentials registered with one authorization server are never sent to another:
```ts source="../examples/guides/clientGuide.examples.ts#auth_oauthClientProvider"
@ -311,8 +308,8 @@ const transport = new StreamableHTTPClientTransport(new URL('http://localhost:30
});
```
The {@linkcode @modelcontextprotocol/client!client/client.Client#connect | connect()} call throws {@linkcode @modelcontextprotocol/client!client/auth.UnauthorizedError | UnauthorizedError} when authorization is needed — catch it, complete the browser flow, hand the callback query
to {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport#finishAuth | transport.finishAuth()}, and reconnect. Passing the whole `URLSearchParams` lets the SDK extract `code` and validate the RFC 9207 `iss` parameter for you:
The `connect()` call throws `UnauthorizedError` when authorization is needed — catch it, complete the browser flow, hand the callback query
to `transport.finishAuth()`, and reconnect. Passing the whole `URLSearchParams` lets the SDK extract `code` and validate the RFC 9207 `iss` parameter for you:
```ts source="../examples/guides/clientGuide.examples.ts#auth_finishAuth"
const client = new Client({ name: 'my-client', version: '1.0.0' });
@ -384,7 +381,7 @@ class PinnedResourceProvider extends MyOAuthProvider {
### Cross-App Access (Enterprise Managed Authorization)
{@linkcode @modelcontextprotocol/client!client/authExtensions.CrossAppAccessProvider | CrossAppAccessProvider} implements Enterprise Managed Authorization (SEP-990) for scenarios where users authenticate with an enterprise identity provider (IdP) and clients need to access
`CrossAppAccessProvider` implements Enterprise Managed Authorization (SEP-990) for scenarios where users authenticate with an enterprise identity provider (IdP) and clients need to access
protected MCP servers on their behalf.
This provider handles a two-step OAuth flow:
@ -436,7 +433,7 @@ For manual control over the token exchange steps, use the Layer 2 utilities from
Tools are callable actions offered by servers — discovering and invoking them is usually how your client enables an LLM to take action (see [Tools](https://modelcontextprotocol.io/docs/learn/server-concepts#tools) in the MCP overview).
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listTools | listTools()} to discover available tools, and {@linkcode @modelcontextprotocol/client!client/client.Client#callTool | callTool()} to invoke one. `listTools()` walks every page on your behalf and returns
Use `listTools()` to discover available tools, and `callTool()` to invoke one. `listTools()` walks every page on your behalf and returns
the complete list (pass an explicit `{ cursor }` for per-page control):
```ts source="../examples/guides/clientGuide.examples.ts#callTool_basic"
@ -453,8 +450,7 @@ const result = await client.callTool({
console.log(result.content);
```
The aggregate walk is capped at `ClientOptions.listMaxPages` pages (default 64; `0` disables the cap). If a server's pagination never terminates, the call rejects with {@linkcode @modelcontextprotocol/client!index.SdkError | SdkError} code {@linkcode
@modelcontextprotocol/client!index.SdkErrorCode.ListPaginationExceeded | LIST_PAGINATION_EXCEEDED}. The same applies to `listPrompts()`, `listResources()`, and `listResourceTemplates()`.
The aggregate walk is capped at `ClientOptions.listMaxPages` pages (default 64; `0` disables the cap). If a server's pagination never terminates, the call rejects with `SdkError` code `LIST_PAGINATION_EXCEEDED`. The same applies to `listPrompts()`, `listResources()`, and `listResourceTemplates()`.
Tool results may include a `structuredContent` field — a machine-readable JSON value (any JSON type per SEP-2106) for programmatic use by the client application, complementing `content` which is for the LLM:
@ -507,7 +503,7 @@ paths are unchanged.
Resources are read-only data — files, database schemas, configuration — that your application can retrieve from a server and attach as context for the model (see [Resources](https://modelcontextprotocol.io/docs/learn/server-concepts#resources) in the MCP overview).
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listResources | listResources()} and {@linkcode @modelcontextprotocol/client!client/client.Client#readResource | readResource()} to discover and read server-provided data. `listResources()` walks every page on your
Use `listResources()` and `readResource()` to discover and read server-provided data. `listResources()` walks every page on your
behalf and returns the complete list (pass an explicit `{ cursor }` for per-page control):
```ts source="../examples/guides/clientGuide.examples.ts#readResource_basic"
@ -523,11 +519,11 @@ for (const item of contents) {
}
```
To discover URI templates for dynamic resources, use {@linkcode @modelcontextprotocol/client!client/client.Client#listResourceTemplates | listResourceTemplates()}.
To discover URI templates for dynamic resources, use `listResourceTemplates()`.
### Subscribing to resource changes
If the server supports resource subscriptions, use {@linkcode @modelcontextprotocol/client!client/client.Client#subscribeResource | subscribeResource()} to receive notifications when a resource changes, then re-read it:
If the server supports resource subscriptions, use `subscribeResource()` to receive notifications when a resource changes, then re-read it:
```ts source="../examples/guides/clientGuide.examples.ts#subscribeResource_basic"
await client.subscribeResource({ uri: 'config://app' });
@ -551,7 +547,7 @@ await client.unsubscribeResource({ uri: 'config://app' });
Prompts are reusable message templates that servers offer to help structure interactions with models (see [Prompts](https://modelcontextprotocol.io/docs/learn/server-concepts#prompts) in the MCP overview).
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listPrompts | listPrompts()} and {@linkcode @modelcontextprotocol/client!client/client.Client#getPrompt | getPrompt()} to list available prompts and retrieve them with arguments. `listPrompts()` walks every page on
Use `listPrompts()` and `getPrompt()` to list available prompts and retrieve them with arguments. `listPrompts()` walks every page on
your behalf and returns the complete list (pass an explicit `{ cursor }` for per-page control):
```ts source="../examples/guides/clientGuide.examples.ts#getPrompt_basic"
@ -570,7 +566,7 @@ console.log(messages);
## Completions
Both prompts and resources can support argument completions. Use {@linkcode @modelcontextprotocol/client!client/client.Client#complete | complete()} to request autocompletion suggestions from the server as a user types:
Both prompts and resources can support argument completions. Use `complete()` to request autocompletion suggestions from the server as a user types:
```ts source="../examples/guides/clientGuide.examples.ts#complete_basic"
const { completion } = await client.complete({
@ -605,7 +601,7 @@ await client.readResource({ uri: 'config://app' }, { cacheMode: 'bypass' }); //
`'bypass'` leaves the cache byte-untouched, including the internal `tools/list` entry that [`x-mcp-header` parameter mirroring](#x-mcp-header-parameter-mirroring-2026-07-28-draft) and output-schema validation read. Cached entries are evicted automatically when the server
signals a change: a `list_changed` notification drops the matching list entries, and `notifications/resources/updated` drops the cached body for that URI (see [Notifications](#notifications)).
Three {@linkcode @modelcontextprotocol/client!client/client.ClientOptions | ClientOptions} fields tune the behavior:
Three `ClientOptions` fields tune the behavior:
- **`responseCacheStore`**: the backing store; defaults to a per-client `InMemoryResponseCacheStore` (at most 512 `resources/read` entries by default). Supply your own `ResponseCacheStore` implementation (the interface is async-ready, so a
Redis-style store fits) to persist entries or share one store across clients. Entries are keyed by connected-server identity, so co-tenants never collide.
@ -620,7 +616,7 @@ Three {@linkcode @modelcontextprotocol/client!client/client.ClientOptions | Clie
### Automatic list-change tracking
The {@linkcode @modelcontextprotocol/client!client/client.ClientOptions | listChanged} client option keeps a local cache of tools, prompts, or resources in sync with the server. It provides automatic server capability gating, debouncing (300 ms by default), auto-refresh, and
The `listChanged` client option keeps a local cache of tools, prompts, or resources in sync with the server. It provides automatic server capability gating, debouncing (300 ms by default), auto-refresh, and
error-first callbacks:
```ts source="../examples/guides/clientGuide.examples.ts#listChanged_basic"
@ -649,7 +645,7 @@ const client = new Client(
### Manual notification handlers
For full control — or for notification types not covered by `listChanged` (such as log messages) — register handlers directly with {@linkcode @modelcontextprotocol/client!client/client.Client#setNotificationHandler | setNotificationHandler()}:
For full control — or for notification types not covered by `listChanged` (such as log messages) — register handlers directly with `setNotificationHandler()`:
```ts source="../examples/guides/clientGuide.examples.ts#notificationHandler_basic"
// Server log messages (sent by the server during request processing)
@ -669,7 +665,7 @@ client.setNotificationHandler('notifications/resources/list_changed', async () =
> MCP logging (including `setLoggingLevel()` and `notifications/message`) is deprecated as of protocol version 2026-07-28 (SEP-2577); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). It remains fully functional on
> 2025-era connections during the deprecation window (at least twelve months); on the 2026-07-28 revision the log level travels per request instead (see below). Servers should migrate to stderr logging (STDIO) or OpenTelemetry.
To control the minimum severity of log messages the server sends, use {@linkcode @modelcontextprotocol/client!client/client.Client#setLoggingLevel | setLoggingLevel()}:
To control the minimum severity of log messages the server sends, use `setLoggingLevel()`:
```ts source="../examples/guides/clientGuide.examples.ts#setLoggingLevel_basic"
await client.setLoggingLevel('warning');
@ -688,14 +684,13 @@ const result = await client.callTool({
Messages arrive through the same `notifications/message` handler shown above. See the [2026-07-28 support guide](./migration/support-2026-07-28.md#ctxmcpreqlog-and-the-per-request-loglevel) for the server-side semantics.
> [!WARNING]
> `listChanged` and {@linkcode @modelcontextprotocol/client!client/client.Client#setNotificationHandler | setNotificationHandler()} resolve per notification type by last registration wins: `listChanged` installs its handler during `connect()`, so a manual handler registered
> `listChanged` and `setNotificationHandler()` resolve per notification type by last registration wins: `listChanged` installs its handler during `connect()`, so a manual handler registered
> after connecting silently disables `listChanged` for that type, and one registered before connecting is overwritten by it.
### Subscription streams (2026-07-28)
On a 2026-07-28 connection the server delivers change notifications only on a `subscriptions/listen` stream the client opens: nothing arrives unsolicited. The `listChanged` option handles this transparently: on a modern connection it auto-opens a stream whose filter is the
intersection of the configured sub-options and the server's advertised capabilities (the handle is exposed as {@linkcode @modelcontextprotocol/client!client/client.Client#autoOpenedSubscription | autoOpenedSubscription}). To open a stream explicitly, use {@linkcode
@modelcontextprotocol/client!client/client.Client#listen | listen()}:
intersection of the configured sub-options and the server's advertised capabilities (the handle is exposed as `autoOpenedSubscription`). To open a stream explicitly, use `listen()`:
```ts source="../examples/guides/clientGuide.examples.ts#listen_basic"
client.setNotificationHandler('notifications/tools/list_changed', async () => {
@ -738,7 +733,7 @@ runnable example of both watch styles.
## Handling server-initiated requests
MCP is bidirectional — servers can send requests _to_ the client during tool execution, as long as the client declares matching capabilities (see [Architecture](https://modelcontextprotocol.io/docs/learn/architecture) in the MCP overview). Declare the corresponding capability
when constructing the {@linkcode @modelcontextprotocol/client!client/client.Client | Client} and register a request handler:
when constructing the `Client` and register a request handler:
```ts source="../examples/guides/clientGuide.examples.ts#capabilities_declaration"
const client = new Client(
@ -755,7 +750,7 @@ const client = new Client(
On 2025-era connections these arrive as server→client JSON-RPC requests. On a 2026-07-28 connection there is no server→client request channel: the server answers `tools/call` / `prompts/get` / `resources/read` with an `input_required` result instead, and the client fulfils
the embedded requests automatically through the same handlers you register below, then retries the call with the collected responses and a byte-exact echo of the server's opaque `requestState`. `callTool()` and its siblings keep returning their plain result: the interactive
rounds happen inside the call, capped at `maxRounds` (default 10), after which the call rejects with a typed {@linkcode @modelcontextprotocol/client!index.SdkErrorCode.InputRequiredRoundsExceeded | INPUT_REQUIRED_ROUNDS_EXCEEDED} error. Configure or disable this via
rounds happen inside the call, capped at `maxRounds` (default 10), after which the call rejects with a typed `INPUT_REQUIRED_ROUNDS_EXCEEDED` error. Configure or disable this via
`ClientOptions.inputRequired` (`{ autoFulfill?: boolean; maxRounds?: number }`); see [Manual multi-round-trip handling](#manual-multi-round-trip-handling-2026-07-28) for the opt-out flow. Handlers are era-transparent: register once for both delivery paths.
### Sampling
@ -824,7 +819,7 @@ client.setRequestHandler('roots/list', async () => {
});
```
When the available roots change, notify the server with {@linkcode @modelcontextprotocol/client!client/client.Client#sendRootsListChanged | client.sendRootsListChanged()}.
When the available roots change, notify the server with `client.sendRootsListChanged()`.
### Manual multi-round-trip handling (2026-07-28)
@ -864,14 +859,13 @@ if (isInputRequiredResult(value)) {
}
```
The manual retry goes through `client.request()` rather than `callTool()`: `inputResponses` and `requestState` are not fields of the typed `CallToolRequest` params. On the explicit-schema `request()` path, wrap the result schema with {@linkcode
@modelcontextprotocol/client!index.withInputRequired | withInputRequired()} so both outcomes are typed and validated. For the full loop (including URL-mode elicitation), see [`mrtr/client.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/mrtr/client.ts).
The manual retry goes through `client.request()` rather than `callTool()`: `inputResponses` and `requestState` are not fields of the typed `CallToolRequest` params. On the explicit-schema `request()` path, wrap the result schema with `withInputRequired()` so both outcomes are typed and validated. For the full loop (including URL-mode elicitation), see [`mrtr/client.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/mrtr/client.ts).
## Error handling
### Tool errors vs protocol errors
{@linkcode @modelcontextprotocol/client!client/client.Client#callTool | callTool()} has two error surfaces: the tool can _run but report failure_ via `isError: true` in the result, or the _request itself can fail_ and throw an exception. Always check both:
`callTool()` has two error surfaces: the tool can _run but report failure_ via `isError: true` in the result, or the _request itself can fail_ and throw an exception. Always check both:
```ts source="../examples/guides/clientGuide.examples.ts#errorHandling_toolErrors"
try {
@ -899,15 +893,13 @@ try {
}
```
{@linkcode @modelcontextprotocol/client!index.ProtocolError | ProtocolError} represents JSON-RPC errors from the server (method not found, invalid params, internal error). {@linkcode @modelcontextprotocol/client!index.SdkError | SdkError} represents local SDK errors — {@linkcode
@modelcontextprotocol/client!index.SdkErrorCode.RequestTimeout | REQUEST_TIMEOUT}, {@linkcode @modelcontextprotocol/client!index.SdkErrorCode.ConnectionClosed | CONNECTION_CLOSED}, {@linkcode @modelcontextprotocol/client!index.SdkErrorCode.CapabilityNotSupported |
CAPABILITY_NOT_SUPPORTED}, and others. The {@linkcode @modelcontextprotocol/client!index.SdkErrorCode | SdkErrorCode} enum is the complete vocabulary; the [error mapping table](./migration/upgrade-to-v2.md#sdkerrorcode-enum-complete) in the upgrade guide describes when each
`ProtocolError` represents JSON-RPC errors from the server (method not found, invalid params, internal error). `SdkError` represents local SDK errors — `REQUEST_TIMEOUT`, `CONNECTION_CLOSED`, `CAPABILITY_NOT_SUPPORTED`, and others. The `SdkErrorCode` enum is the complete vocabulary; the [error mapping table](./migration/upgrade-to-v2.md#sdkerrorcode-enum-complete) in the upgrade guide describes when each
code is raised.
### Connection lifecycle
Set {@linkcode @modelcontextprotocol/client!client/client.Client#onerror | client.onerror} to catch out-of-band transport errors (SSE disconnects, parse errors). Set {@linkcode @modelcontextprotocol/client!client/client.Client#onclose | client.onclose} to detect when the
connection drops — pending requests are rejected with a {@linkcode @modelcontextprotocol/client!index.SdkErrorCode.ConnectionClosed | CONNECTION_CLOSED} error:
Set `client.onerror` to catch out-of-band transport errors (SSE disconnects, parse errors). Set `client.onclose` to detect when the
connection drops — pending requests are rejected with a `CONNECTION_CLOSED` error:
```ts source="../examples/guides/clientGuide.examples.ts#errorHandling_lifecycle"
// Out-of-band errors (SSE disconnects, parse errors)
@ -924,7 +916,7 @@ client.onclose = () => {
### Timeouts
All requests have a 60-second default timeout. Pass a custom `timeout` in the options to override it. On timeout, the SDK sends a cancellation notification to the server (on a 2026-07-28 Streamable HTTP connection the per-request stream is aborted instead, which is the
spec's cancellation signal) and rejects the promise with {@linkcode @modelcontextprotocol/client!index.SdkErrorCode.RequestTimeout | SdkErrorCode.RequestTimeout}:
spec's cancellation signal) and rejects the promise with `SdkErrorCode.RequestTimeout`:
```ts source="../examples/guides/clientGuide.examples.ts#errorHandling_timeout"
try {
@ -942,7 +934,7 @@ try {
### HTTP transport errors
When an HTTP transport request fails with a non-OK status, the SDK throws {@linkcode @modelcontextprotocol/client!index.SdkHttpError | SdkHttpError}, an `SdkError` subclass with typed `data` (`{ status, statusText? }`) and `status`/`statusText` getters, so you can branch on the status without casting. The codes are the `ClientHttp*` members of `SdkErrorCode`: e.g. `CLIENT_HTTP_AUTHENTICATION` (a 401 persisting after re-authentication), `CLIENT_HTTP_FORBIDDEN` (a 403 `insufficient_scope` after the step-up
When an HTTP transport request fails with a non-OK status, the SDK throws `SdkHttpError`, an `SdkError` subclass with typed `data` (`{ status, statusText? }`) and `status`/`statusText` getters, so you can branch on the status without casting. The codes are the `ClientHttp*` members of `SdkErrorCode`: e.g. `CLIENT_HTTP_AUTHENTICATION` (a 401 persisting after re-authentication), `CLIENT_HTTP_FORBIDDEN` (a 403 `insufficient_scope` after the step-up
retry cap), `CLIENT_HTTP_FAILED_TO_OPEN_STREAM`. (Exception: an unexpected response content type throws a plain `SdkError` with code `CLIENT_HTTP_UNEXPECTED_CONTENT`.)
```ts source="../examples/guides/clientGuide.examples.ts#errorHandling_http"
@ -959,7 +951,7 @@ try {
## Client middleware
Use {@linkcode @modelcontextprotocol/client!client/middleware.createMiddleware | createMiddleware()} and {@linkcode @modelcontextprotocol/client!client/middleware.applyMiddlewares | applyMiddlewares()} to compose fetch middleware pipelines. Middleware wraps the underlying `fetch`
Use `createMiddleware()` and `applyMiddlewares()` to compose fetch middleware pipelines. Middleware wraps the underlying `fetch`
call and can add headers, handle retries, or log requests. Pass the enhanced fetch to the transport via the `fetch` option:
```ts source="../examples/guides/clientGuide.examples.ts#middleware_basic"

View file

@ -1,17 +0,0 @@
---
title: Documents
children:
- ./server-quickstart.md
- ./server.md
- ./client-quickstart.md
- ./client.md
- ./faq.md
---
# Documents
- [Server Quickstart](./server-quickstart.md) – build a weather server from scratch and connect it to VS Code
- [Server](./server.md) – building MCP servers: transports, tools, resources, prompts, server-initiated requests, and deployment
- [Client Quickstart](./client-quickstart.md) – build an LLM-powered chatbot that connects to an MCP server and calls its tools
- [Client](./client.md) – building MCP clients: connecting, tools, resources, prompts, server-initiated requests, and error handling
- [FAQ](./faq.md) – frequently asked questions and troubleshooting

View file

@ -2,7 +2,7 @@
title: FAQ
---
## FAQ
# FAQ
<details>
<summary>Table of Contents</summary>
@ -67,7 +67,7 @@ For production use, you can either:
### Where can I find runnable server examples?
The [server quickstart](./server-quickstart.md) walks you through building a weather server from scratch. Its complete source lives in [`examples/server-quickstart/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/server-quickstart/). For more advanced examples (OAuth, streaming, sessions, etc.), see the server examples index in [`examples/README.md`](../examples/README.md).
The [server quickstart](./server-quickstart.md) walks you through building a weather server from scratch. Its complete source lives in [`examples/server-quickstart/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/server-quickstart/). For more advanced examples (OAuth, streaming, sessions, etc.), see the server examples index in [`examples/README.md`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/README.md).
### Where are the server auth helpers?

View file

@ -49,6 +49,6 @@ the codemod first; the guide is the codemod's companion for what's left.
## See also
- [`@modelcontextprotocol/codemod` README](../../packages/codemod/README.md)
- [`@modelcontextprotocol/codemod` README](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/README.md)
- [FAQ](../faq.md)
- [Examples](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples)

View file

@ -60,16 +60,16 @@ Migrating a large codebase gradually instead of in one pass? See
## What the codemod handles
The codemod ([`@modelcontextprotocol/codemod`](../../packages/codemod/README.md))
The codemod ([`@modelcontextprotocol/codemod`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/README.md))
mechanically applies every rename whose mapping is fixed. The mappings are the
**source of truth** — they live in the codemod package and are not reproduced here:
| Mapping | Source file |
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `@modelcontextprotocol/sdk/...` import paths → v2 packages | [`mappings/importMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts) |
| Symbol renames (`McpError` → `ProtocolError`, `JSONRPCError` → `JSONRPCErrorResponse`, …) | [`mappings/symbolMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/symbolMap.ts) |
| `setRequestHandler(Schema, …)` → `setRequestHandler('method/string', …)` | [`mappings/schemaToMethodMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/schemaToMethodMap.ts) |
| `extra.*` → `ctx.mcpReq.*` / `ctx.http?.*` property remap | [`mappings/contextPropertyMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/contextPropertyMap.ts) |
| `@modelcontextprotocol/sdk/...` import paths → v2 packages | [`mappings/importMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts) |
| Symbol renames (`McpError` → `ProtocolError`, `JSONRPCError` → `JSONRPCErrorResponse`, …) | [`mappings/symbolMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/symbolMap.ts) |
| `setRequestHandler(Schema, …)` → `setRequestHandler('method/string', …)` | [`mappings/schemaToMethodMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/schemaToMethodMap.ts) |
| `extra.*` → `ctx.mcpReq.*` / `ctx.http?.*` property remap | [`mappings/contextPropertyMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/contextPropertyMap.ts) |
In addition the codemod:
@ -365,7 +365,7 @@ coexist under different names.
### Imports & transports
The codemod rewrites every `@modelcontextprotocol/sdk/...` import path via
[`importMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts).
[`importMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts).
A few transports need a decision the codemod can't make:
- **`StreamableHTTPServerTransport` → which runtime?** The codemod renames it to
@ -431,7 +431,7 @@ A few transports need a decision the codemod can't make:
(deprecated, frozen v1 copy); migrate AS to a dedicated IdP/OAuth library. `AuthInfo`
is now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`.
The codemod's [`importMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts)
The codemod's [`importMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts)
routes every `…/server/auth/**` deep path (including
`…/server/auth/middleware/{bearerAuth,allowedMethods,clientAuth}.js`,
`…/server/auth/handlers/*.js`, `…/server/auth/providers/proxyProvider.js`) to
@ -452,7 +452,7 @@ object named `extra` — is now a structured **context** object named `ctx`. Thi
`ctx` that appears throughout the rest of this guide.
The codemod renames the parameter and remaps property access via
[`contextPropertyMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/contextPropertyMap.ts).
[`contextPropertyMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/contextPropertyMap.ts).
A few mappings need optional-chaining adjustment (the `http` group is `undefined` on
stdio):
@ -522,7 +522,7 @@ wire behavior, and remains functional for at least the twelve-month deprecation
The low-level handler registration takes a **method string** instead of a Zod schema.
The codemod rewrites every spec-method registration via
[`schemaToMethodMap.ts`](../../packages/codemod/src/migrations/v1-to-v2/mappings/schemaToMethodMap.ts).
[`schemaToMethodMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/schemaToMethodMap.ts).
```typescript
// v1
@ -1807,7 +1807,7 @@ where an entry notes its own signature change:
## Need help?
- The codemod's [`@mcp-codemod-error`](../../packages/codemod/README.md) markers point
- The codemod's [`@mcp-codemod-error`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/README.md) markers point
at every site it could not safely rewrite.
- The [FAQ](../faq.md) covers common v2 questions.
- Runnable [examples](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples)

View file

@ -207,7 +207,7 @@ interface ForecastResponse {
### Registering tools
Each tool is registered with {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#registerTool | server.registerTool()}, which takes the tool name, a configuration object (with description and input schema), and a callback that implements the tool logic. Create the server inside a `createServer()` factory and register both weather tools on it. The serving entry in the next step builds the instance it serves by calling this factory, so keep it cheap and side-effect-free:
Each tool is registered with `server.registerTool()`, which takes the tool name, a configuration object (with description and input schema), and a callback that implements the tool logic. Create the server inside a `createServer()` factory and register both weather tools on it. The serving entry in the next step builds the instance it serves by calling this factory, so keep it cheap and side-effect-free:
```ts source="../examples/server-quickstart/src/index.ts#registerTools"
// Create a server with both weather tools registered. The serving entry calls
@ -352,7 +352,7 @@ function createServer(): McpServer {
### Running the server
Finally, serve the factory on stdio with {@linkcode @modelcontextprotocol/server!server/serveStdio.serveStdio | serveStdio}. The entry owns the transport and negotiates the protocol revision with each client, so the same factory serves current hosts (such as VS Code) and clients that speak the 2026-07-28 draft revision; see [Serving the 2026-07-28 draft revision on stdio](./server.md#serving-the-2026-07-28-draft-revision-on-stdio) in the server guide for the options:
Finally, serve the factory on stdio with `serveStdio`. The entry owns the transport and negotiates the protocol revision with each client, so the same factory serves current hosts (such as VS Code) and clients that speak the 2026-07-28 draft revision; see [Serving the 2026-07-28 draft revision on stdio](./server.md#serving-the-2026-07-28-draft-revision-on-stdio) in the server guide for the options:
```ts source="../examples/server-quickstart/src/index.ts#main"
void serveStdio(createServer);

View file

@ -8,7 +8,7 @@ This guide covers the TypeScript SDK APIs for building MCP servers. For protocol
Building a server takes three steps:
1. Create an {@linkcode @modelcontextprotocol/server!server/mcp.McpServer | McpServer} and register your [tools](#tools), [resources](#resources), and [prompts](#prompts).
1. Create an `McpServer` and register your [tools](#tools), [resources](#resources), and [prompts](#prompts).
2. Create a transport — [Streamable HTTP](#streamable-http) for remote servers or [stdio](#stdio) for local integrations.
3. Connect them with `server.connect(transport)`.
@ -52,7 +52,7 @@ MCP supports two transport mechanisms (see [Transport layer](https://modelcontex
### Streamable HTTP
Create a {@linkcode @modelcontextprotocol/node!streamableHttp.NodeStreamableHTTPServerTransport | NodeStreamableHTTPServerTransport} and connect it to your server:
Create a `NodeStreamableHTTPServerTransport` and connect it to your server:
```ts source="../examples/guides/serverGuide.examples.ts#streamableHttp_stateful"
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
@ -96,7 +96,7 @@ To keep an existing sessionful 2025 deployment serving legacy traffic, route wit
### stdio
For local, process-spawned integrations, use {@linkcode @modelcontextprotocol/server!server/stdio.StdioServerTransport | StdioServerTransport}:
For local, process-spawned integrations, use `StdioServerTransport`:
```ts source="../examples/guides/serverGuide.examples.ts#stdio_basic"
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
@ -142,7 +142,7 @@ const server = new McpServer(
Tools let clients invoke actions on your server — they are usually the main way LLMs call into your application (see [Tools](https://modelcontextprotocol.io/docs/learn/server-concepts#tools) in the MCP overview).
Register a tool with {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#registerTool | registerTool}. Provide an `inputSchema` (any Standard Schema library that supports JSON Schema conversion: Zod v4 shown here; ArkType and Valibot also conform) to validate
Register a tool with `registerTool`. Provide an `inputSchema` (any Standard Schema library that supports JSON Schema conversion: Zod v4 shown here; ArkType and Valibot also conform) to validate
arguments, and optionally an `outputSchema` for structured return values.
> On the 2026-07-28 draft serving path, a tool whose `inputSchema` carries an `x-mcp-header` annotation has that argument mirrored into an `Mcp-Param-{Name}` HTTP request header by conforming clients. `createMcpHandler` validates those headers before dispatch and rejects a
@ -309,7 +309,7 @@ server.registerResource(
);
```
Dynamic resources use {@linkcode @modelcontextprotocol/server!server/mcp.ResourceTemplate | ResourceTemplate} with URI patterns. The `list` callback lets clients discover available instances:
Dynamic resources use `ResourceTemplate` with URI patterns. The `list` callback lets clients discover available instances:
```ts source="../examples/guides/serverGuide.examples.ts#registerResource_template"
server.registerResource(
@ -375,7 +375,7 @@ server.registerPrompt(
## Completions
Both prompts and resources can support argument completions. Wrap a field in the `argsSchema` with {@linkcode @modelcontextprotocol/server!server/completable.completable | completable()} to provide autocompletion suggestions:
Both prompts and resources can support argument completions. Wrap a field in the `argsSchema` with `completable()` to provide autocompletion suggestions:
```ts source="../examples/guides/serverGuide.examples.ts#registerPrompt_completion"
server.registerPrompt(
@ -408,7 +408,7 @@ For resource templates, pass a `complete` callback map to the `ResourceTemplate`
## Extension capabilities
A server advertises support for [MCP extensions](https://modelcontextprotocol.io/specification/latest/basic/lifecycle#capability-negotiation) through `capabilities.extensions` — a map from extension identifier to that extension's settings object. Declare entries with
{@linkcode @modelcontextprotocol/server!server/server.Server#registerCapabilities | server.server.registerCapabilities()} before connecting:
`server.server.registerCapabilities()` before connecting:
```ts source="../examples/guides/serverGuide.examples.ts#extensionCapabilities_register"
server.server.registerCapabilities({
@ -419,14 +419,14 @@ server.server.registerCapabilities({
The map is advertised in the `initialize` result on legacy connections and in the `server/discover` response on 2026-07-28 ones. Identifiers are prefix-qualified per the spec's `_meta` key naming rules (e.g. `com.example/feature-flags`); each value is free-form JSON for
that extension's settings — `{}` means supported with no settings.
For a runnable pair, see the [`extension-capabilities/` example](../examples/extension-capabilities/README.md); reading the map client-side is covered in the [client guide](./client.md#extension-capabilities).
For a runnable pair, see the [`extension-capabilities/` example](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/extension-capabilities/README.md); reading the map client-side is covered in the [client guide](./client.md#extension-capabilities).
## Cache hints (2026-07-28 draft)
The 2026-07-28 revision requires `ttlMs` and `cacheScope` on the cacheable results (`tools/list`, `prompts/list`, `resources/list`, `resources/templates/list`, `resources/read`, and `server/discover`) so clients and intermediaries know how long a response stays fresh and
whether it may be shared (SEP-2549). The SDK fills both fields automatically when serving that revision, defaulting to `ttlMs: 0` and `cacheScope: 'private'` (immediately stale, never shared). Responses to 2025-era requests are never affected.
To advertise a real cache policy, set {@linkcode @modelcontextprotocol/server!server/server.ServerOptions | ServerOptions.cacheHints} per operation, and/or `cacheHint` on an individual resource registration:
To advertise a real cache policy, set `ServerOptions.cacheHints` per operation, and/or `cacheHint` on an individual resource registration:
```ts source="../examples/guides/serverGuide.examples.ts#cacheHints_basic"
const server = new McpServer(
@ -467,7 +467,7 @@ Use `cacheScope: 'public'` only for results that are identical for every caller:
Logging lets your server send structured diagnostics — debug traces, progress updates, warnings — to the connected client as notifications (see [Logging](https://modelcontextprotocol.io/specification/latest/server/utilities/logging) in the MCP specification).
Declare the `logging` capability, then call `ctx.mcpReq.log(level, data)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside any handler:
Declare the `logging` capability, then call `ctx.mcpReq.log(level, data)` (from `ServerContext`) inside any handler:
```ts source="../examples/guides/serverGuide.examples.ts#logging_capability"
const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { logging: {} } });
@ -499,7 +499,7 @@ On a 2026-07-28 request, `ctx.mcpReq.log()` reads its level filter from the requ
Progress notifications let a tool report incremental status updates during long-running operations (see [Progress](https://modelcontextprotocol.io/specification/latest/basic/utilities/progress) in the MCP specification).
If the client includes a `progressToken` in the request `_meta`, send `notifications/progress` via `ctx.mcpReq.notify()` (from {@linkcode @modelcontextprotocol/server!index.BaseContext | BaseContext}):
If the client includes a `progressToken` in the request `_meta`, send `notifications/progress` via `ctx.mcpReq.notify()` (from `BaseContext`):
```ts source="../examples/guides/serverGuide.examples.ts#registerTool_progress"
server.registerTool(
@ -539,10 +539,10 @@ server.registerTool(
Servers can signal that their tool, prompt, or resource lists changed, or that a specific resource's content changed, so clients can refresh.
**List changes** are emitted automatically: registering, enabling, disabling, updating, or removing a tool, prompt, or resource sends the matching `notifications/*/list_changed` (`McpServer` advertises the corresponding `listChanged: true` capability on registration;
declare it up front only when using the low-level `Server`). You can also send them explicitly with {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#sendToolListChanged | sendToolListChanged()}, `sendPromptListChanged()`, and `sendResourceListChanged()`.
declare it up front only when using the low-level `Server`). You can also send them explicitly with `sendToolListChanged()`, `sendPromptListChanged()`, and `sendResourceListChanged()`.
**Per-resource updates** (2025-era connections) require hand-wiring; `registerResource` has no subscribe option. Declare `resources: { subscribe: true }`, register the `resources/subscribe`/`resources/unsubscribe` handlers on the underlying low-level server, and push
{@linkcode @modelcontextprotocol/server!server/server.Server#sendResourceUpdated | sendResourceUpdated()} when the data changes:
`sendResourceUpdated()` when the data changes:
```ts source="../examples/guides/serverGuide.examples.ts#subscriptions_legacy"
const server = new McpServer(
@ -569,7 +569,7 @@ async function onConfigChanged() {
```
**On the 2026-07-28 revision** clients receive change notifications only on a `subscriptions/listen` stream they open, and the serving entries handle that method themselves (nothing to register). Over HTTP, publish through the handler's typed
{@linkcode @modelcontextprotocol/server!server/serverEventBus.ServerNotifier | notify} facade; each call reaches every open subscription that opted in:
`notify` facade; each call reaches every open subscription that opted in:
```ts source="../examples/guides/serverGuide.examples.ts#subscriptions_notify"
const handler = createMcpHandler(() => buildServer());
@ -579,8 +579,8 @@ handler.notify.resourceUpdated('config://app');
handler.notify.toolsChanged();
```
The default in-process {@linkcode @modelcontextprotocol/server!server/serverEventBus.InMemoryServerEventBus | InMemoryServerEventBus} covers single-process deployments; multi-process deployments supply their own
{@linkcode @modelcontextprotocol/server!server/serverEventBus.ServerEventBus | ServerEventBus} via the `bus` option. On stdio, `serveStdio` pins one instance per connection and routes its ordinary `send*ListChanged()` calls onto open subscriptions automatically. Per-resource updates need one change on a 2026 connection: the subscription bookkeeping lives at the entry (the client's listen filter), so the hand-wired `resources/subscribe` handlers above never run. Publish
The default in-process `InMemoryServerEventBus` covers single-process deployments; multi-process deployments supply their own
`ServerEventBus` via the `bus` option. On stdio, `serveStdio` pins one instance per connection and routes its ordinary `send*ListChanged()` calls onto open subscriptions automatically. Per-resource updates need one change on a 2026 connection: the subscription bookkeeping lives at the entry (the client's listen filter), so the hand-wired `resources/subscribe` handlers above never run. Publish
`sendResourceUpdated()` unconditionally when the data changes and let the entry deliver it only to subscriptions that listed the URI.
On the 2026-07-28 revision delivery is capability-gated per type: the entry honors `resourceSubscriptions` only when the server advertises `resources: { subscribe: true }`, and each list-changed type only with the matching `listChanged` capability (on 2025-era connections
@ -624,13 +624,13 @@ To propagate context onward (for example on a server-initiated sampling request,
MCP is bidirectional: servers can request input _from_ the client during tool execution, as long as the client declares matching capabilities (see [Architecture](https://modelcontextprotocol.io/docs/learn/architecture) in the MCP overview). On 2025-era connections the server pushes a JSON-RPC request to the client (the sections below). On the 2026-07-28 revision there is no server→client request channel: the handler **returns** an `input_required` result carrying the embedded requests,
and the client retries the call with the responses.
On a connection pinned to the 2026-07-28 draft revision (served via `serveStdio` or `createMcpHandler`), the push-style channels below throw an {@linkcode @modelcontextprotocol/server!index.SdkError | SdkError} with
code {@linkcode @modelcontextprotocol/server!index.SdkErrorCode.MethodNotSupportedByProtocolVersion | METHOD_NOT_SUPPORTED_BY_PROTOCOL_VERSION} before anything reaches the wire (see the [2026-07-28 support guide](./migration/support-2026-07-28.md)).
On a connection pinned to the 2026-07-28 draft revision (served via `serveStdio` or `createMcpHandler`), the push-style channels below throw an `SdkError` with
code `METHOD_NOT_SUPPORTED_BY_PROTOCOL_VERSION` before anything reaches the wire (see the [2026-07-28 support guide](./migration/support-2026-07-28.md)).
### Requesting input on 2026-07-28: `input_required`
On the 2026-07-28 revision a `tools/call`, `prompts/get`, or `resources/read` handler requests client input by returning {@linkcode @modelcontextprotocol/server!index.inputRequired | inputRequired(...)}. The result names one or more
embedded requests, built with `inputRequired.elicit(...)` (form elicitation), `inputRequired.elicitUrl(...)` (URL elicitation), `inputRequired.createMessage(...)` (sampling), or `inputRequired.listRoots()`. Write the handler **write-once**: on every entry, first read what has already arrived via {@linkcode @modelcontextprotocol/server!index.acceptedContent | acceptedContent(ctx.mcpReq.inputResponses, key)}, and only ask for what is still missing:
On the 2026-07-28 revision a `tools/call`, `prompts/get`, or `resources/read` handler requests client input by returning `inputRequired(...)`. The result names one or more
embedded requests, built with `inputRequired.elicit(...)` (form elicitation), `inputRequired.elicitUrl(...)` (URL elicitation), `inputRequired.createMessage(...)` (sampling), or `inputRequired.listRoots()`. Write the handler **write-once**: on every entry, first read what has already arrived via `acceptedContent(ctx.mcpReq.inputResponses, key)`, and only ask for what is still missing:
```ts source="../examples/guides/serverGuide.examples.ts#registerTool_inputRequired"
server.registerTool(
@ -674,7 +674,7 @@ and the handler reads it back with the typed `ctx.mcpReq.requestState<State>()`
> [!IMPORTANT]
> `requestState` round-trips through the client and comes back as **attacker-controlled input**. State that influences authorization, resource access, or business logic must be integrity-protected; the SDK applies no protection of its own. Use
> {@linkcode @modelcontextprotocol/server!index.createRequestStateCodec | createRequestStateCodec}, an HMAC-SHA256 codec whose `verify` drops directly into the `ServerOptions.requestState` hook, which runs before the handler and answers tampered or expired state with a
> `createRequestStateCodec`, an HMAC-SHA256 codec whose `verify` drops directly into the `ServerOptions.requestState` hook, which runs before the handler and answers tampered or expired state with a
> wire-level `-32602` (frozen message `"Invalid or expired requestState"`). The codec is signed, not encrypted. Do not put secrets in the payload.
```ts source="../examples/guides/serverGuide.examples.ts#requestState_codec"
@ -746,7 +746,7 @@ See [`mrtr/server.ts`](https://github.com/modelcontextprotocol/typescript-sdk/bl
Sampling lets a tool handler request an LLM completion from the connected client — the handler describes a prompt and the client returns the model's response (see [Sampling](https://modelcontextprotocol.io/docs/learn/client-concepts#sampling) in the MCP overview). Use sampling
when a tool needs the model to generate or transform text mid-execution.
Call `ctx.mcpReq.requestSampling(params)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler:
Call `ctx.mcpReq.requestSampling(params)` (from `ServerContext`) inside a tool handler:
```ts source="../examples/guides/serverGuide.examples.ts#registerTool_sampling"
server.registerTool(
@ -796,7 +796,7 @@ Elicitation lets a tool handler request direct input from the user — form fiel
> `ctx.mcpReq.elicitInput` is the 2025-era push channel and **throws a typed error on a 2026-07-28-era request**. Return `inputRequired.elicit(...)` (form) or `inputRequired.elicitUrl(...)` (URL) via `inputRequired({ inputRequests: { … } })` instead; see
> [Requesting input on 2026-07-28](#requesting-input-on-2026-07-28-input_required). The throw-style `UrlElicitationRequiredError` (`-32042`) also fails loudly toward 2026-era requests.
Call `ctx.mcpReq.elicitInput(params)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler:
Call `ctx.mcpReq.elicitInput(params)` (from `ServerContext`) inside a tool handler:
```ts source="../examples/guides/serverGuide.examples.ts#registerTool_elicitation"
server.registerTool(
@ -851,8 +851,7 @@ For runnable examples, see [`elicitation/server.ts`](https://github.com/modelcon
> `server.server.listRoots()` **throws a typed error on a 2026-07-28-era instance**. Return `inputRequired({ inputRequests: { roots: inputRequired.listRoots() } })` and read the response from `ctx.mcpReq.inputResponses` on re-entry; see
> [Requesting input on 2026-07-28](#requesting-input-on-2026-07-28-input_required).
Roots let a tool handler discover the client's workspace directories — for example, to scope a file search or identify project boundaries (see [Roots](https://modelcontextprotocol.io/docs/learn/client-concepts#roots) in the MCP overview). Call {@linkcode
@modelcontextprotocol/server!server/server.Server#listRoots | server.server.listRoots()} (requires the client to declare the `roots` capability):
Roots let a tool handler discover the client's workspace directories — for example, to scope a file search or identify project boundaries (see [Roots](https://modelcontextprotocol.io/docs/learn/client-concepts#roots) in the MCP overview). Call `server.server.listRoots()` (requires the client to declare the `roots` capability):
```ts source="../examples/guides/serverGuide.examples.ts#registerTool_roots"
server.registerTool(
@ -889,9 +888,9 @@ process.on('SIGINT', async () => {
});
```
Calling {@linkcode @modelcontextprotocol/server!index.Transport#close | transport.close()} closes SSE streams and rejects any pending outbound requests. In-flight tool handlers are not automatically drained — they are terminated when the process exits.
Calling `transport.close()` closes SSE streams and rejects any pending outbound requests. In-flight tool handlers are not automatically drained — they are terminated when the process exits.
For stdio servers, {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#close | server.close()} is sufficient:
For stdio servers, `server.close()` is sufficient:
```ts source="../examples/guides/serverGuide.examples.ts#shutdown_stdio"
process.on('SIGINT', async () => {
@ -909,7 +908,7 @@ For a complete multi-session server with shutdown handling, see [`repl/server.ts
Under normal circumstances, cross-origin browser restrictions limit what a malicious website can do to your localhost server. [DNS rebinding attacks](https://en.wikipedia.org/wiki/DNS_rebinding) get around those restrictions entirely by making the requests appear as same-origin,
since the attacking domain resolves to localhost. Validating the host header on the server side protects against this scenario. **All localhost MCP servers should use DNS rebinding protection.**
The recommended approach is to use {@linkcode @modelcontextprotocol/express!express.createMcpExpressApp | createMcpExpressApp()} (from `@modelcontextprotocol/express`) or {@linkcode @modelcontextprotocol/hono!hono.createMcpHonoApp | createMcpHonoApp()} (from
The recommended approach is to use `createMcpExpressApp()` (from `@modelcontextprotocol/express`) or `createMcpHonoApp()` (from
`@modelcontextprotocol/hono`), which enable Host header validation by default:
```ts source="../examples/guides/serverGuide.examples.ts#dnsRebinding_basic"

View file

@ -0,0 +1,70 @@
import { existsSync, readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig, type DefaultTheme } from 'vitepress';
const siteDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
/**
* The v1 site's source content (docs + generated API markdown) is populated into docs/v1/content/
* at build time by scripts/build-docs-site.sh from the v1.x branch. The API Reference sidebar is
* generated there by typedoc + typedoc-vitepress-theme.
*/
function apiSidebarItems(): DefaultTheme.SidebarItem[] {
const sidebarPath = resolve(siteDir, 'content/api/typedoc-sidebar.json');
if (!existsSync(sidebarPath)) {
console.warn(`[docs/v1] ${sidebarPath} not found — run \`bash scripts/build-docs-site.sh\` to populate the v1 content.`);
return [];
}
return JSON.parse(readFileSync(sidebarPath, 'utf8'));
}
export default defineConfig({
title: 'MCP TypeScript SDK (v1)',
description: 'Documentation for v1.x of the MCP TypeScript SDK.',
base: '/',
sitemap: { hostname: 'https://ts.sdk.modelcontextprotocol.io' },
srcDir: 'content',
markdown: {
config(md) {
// Same rewrite as the v2 site: JSDoc carries site-root-relative spec links that are
// meant to resolve on modelcontextprotocol.io.
const orig = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const href = tokens[idx].attrGet('href');
if (href?.startsWith('/specification/')) {
tokens[idx].attrSet('href', `https://modelcontextprotocol.io${href}`);
}
return orig(tokens, idx, options, env, self);
};
}
},
themeConfig: {
nav: [
{ text: 'Guides', link: '/server', activeMatch: '^/(server|client|capabilities|protocol|faq)' },
{ text: 'API Reference', link: '/api/', activeMatch: '^/api/' },
{ text: 'V2 Docs', link: 'https://ts.sdk.modelcontextprotocol.io/v2/' }
],
sidebar: [
{
text: 'Guides',
items: [
{ text: 'Server', link: '/server' },
{ text: 'Client', link: '/client' },
{ text: 'Capabilities', link: '/capabilities' },
{ text: 'Protocol', link: '/protocol' },
{ text: 'FAQ', link: '/faq' }
]
},
{
text: 'API Reference',
collapsed: true,
items: apiSidebarItems()
}
],
outline: { level: [2, 3] },
search: { provider: 'local' },
socialLinks: [{ icon: 'github', link: 'https://github.com/modelcontextprotocol/typescript-sdk/tree/v1.x' }]
}
});

View file

@ -0,0 +1,6 @@
<template>
<div class="version-banner">
You are viewing the v1.x documentation. The in-development v2 docs are at
<a href="https://ts.sdk.modelcontextprotocol.io/v2/">/v2/</a>.
</div>
</template>

View file

@ -0,0 +1,16 @@
import type { Theme } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import { h } from 'vue';
import Banner from './Banner.vue';
// Shared with the v2 site — do not duplicate.
import '../../../.vitepress/theme/custom.css';
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-top': () => h(Banner)
});
}
} satisfies Theme;

View file

@ -1,9 +0,0 @@
document.addEventListener("DOMContentLoaded", function () {
var banner = document.createElement("div");
banner.innerHTML =
"This documents a <strong>pre-release</strong> version of the SDK. Expect breaking changes. For the stable SDK, see the <a href='/'>V1 docs</a>.";
banner.style.cssText =
"background:#fff3cd;color:#856404;border-bottom:1px solid #ffc107;padding:8px 16px;text-align:center;font-size:14px;";
banner.querySelector("a").style.cssText = "color:#856404;text-decoration:underline;";
document.body.insertBefore(banner, document.body.firstChild);
});

View file

@ -27,16 +27,19 @@
"sync:snippets": "tsx scripts/sync-snippets.ts",
"examples:oauth-server:w": "pnpm --filter @mcp-examples/oauth exec tsx --watch server.ts",
"run:examples": "tsx scripts/examples/run-examples.ts",
"docs": "typedoc",
"docs:multi": "bash scripts/generate-multidoc.sh",
"docs:check": "typedoc",
"docs:api": "typedoc",
"docs:index": "tsx scripts/build-docs-index.ts",
"docs:dev": "pnpm docs:index && vitepress dev docs",
"docs:build": "pnpm docs:index && pnpm docs:api && vitepress build docs",
"docs:multi": "bash scripts/build-docs-site.sh",
"docs:check": "pnpm docs:build",
"typecheck:all": "pnpm -r typecheck",
"build:all": "pnpm -r build",
"ci:publish": "pnpm run build:all && pnpm changeset publish",
"prepack:all": "pnpm -r prepack",
"lint:all": "pnpm sync:snippets --check && pnpm -r lint",
"lint:fix:all": "pnpm sync:snippets && pnpm -r lint:fix",
"check:all": "pnpm -r typecheck && pnpm -r lint && pnpm run docs:check",
"check:all": "pnpm sync:snippets --check && pnpm -r typecheck && pnpm -r lint && pnpm run docs:check",
"test:all": "pnpm -r test",
"prepare": "npx --no-install lefthook install",
"test:conformance:client": "pnpm --filter @modelcontextprotocol/test-conformance run test:conformance:client",
@ -77,8 +80,11 @@
"tslib": "^2.8.1",
"tsx": "catalog:devTools",
"typedoc": "catalog:devTools",
"typedoc-plugin-markdown": "^4.12.0",
"typedoc-vitepress-theme": "^1.1.3",
"typescript": "catalog:devTools",
"typescript-eslint": "catalog:devTools",
"vitepress": "^1.6.4",
"vitest": "catalog:devTools",
"zod": "catalog:runtimeShared"
},

View file

@ -68,10 +68,10 @@ bracket access → `.get()`; sending plain-record headers keeps working), OAuth
error-class consolidation (`instanceof InvalidGrantError` → `OAuthError` +
`OAuthErrorCode`), per-scenario `SdkErrorCode` branch selection, `ctx.mcpReq.send()`
schema-arg drop, and behavioral adaptation are manual — see the
[migration guide](../../docs/migration/upgrade-to-v2.md) for what to do after the
[migration guide](https://ts.sdk.modelcontextprotocol.io/v2/migration/upgrade-to-v2) for what to do after the
codemod runs.
The codemod handles the v1→v2 SDK surface upgrade only. Adopting the 2026-07-28
protocol revision (`createMcpHandler`, multi-round-trip requests, `versionNegotiation`)
is architectural and not codemod-automatable — see
[docs/migration/support-2026-07-28.md](../../docs/migration/support-2026-07-28.md).
[docs/migration/support-2026-07-28.md](https://ts.sdk.modelcontextprotocol.io/v2/migration/support-2026-07-28).

Some files were not shown because too many files have changed in this diff Show more