+
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
new file mode 100644
index 0000000000..2f03d12673
--- /dev/null
+++ b/docs/.vitepress/theme/custom.css
@@ -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;
+}
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
new file mode 100644
index 0000000000..be94142d80
--- /dev/null
+++ b/docs/.vitepress/theme/index.ts
@@ -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;
diff --git a/docs/client.md b/docs/client.md
index 659b39abd0..01822e5929 100644
--- a/docs/client.md
+++ b/docs/client.md
@@ -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"
diff --git a/docs/documents.md b/docs/documents.md
deleted file mode 100644
index 65cff9749c..0000000000
--- a/docs/documents.md
+++ /dev/null
@@ -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
diff --git a/docs/faq.md b/docs/faq.md
index 45603481ff..67ca8c171a 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -2,7 +2,7 @@
title: FAQ
---
-## FAQ
+# FAQ
Table of Contents
@@ -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?
diff --git a/docs/migration/index.md b/docs/migration/index.md
index c9c4161b60..89eb5d0a07 100644
--- a/docs/migration/index.md
+++ b/docs/migration/index.md
@@ -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)
diff --git a/docs/migration/upgrade-to-v2.md b/docs/migration/upgrade-to-v2.md
index f59ecb7dbe..0924aef07d 100644
--- a/docs/migration/upgrade-to-v2.md
+++ b/docs/migration/upgrade-to-v2.md
@@ -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)
diff --git a/docs/server-quickstart.md b/docs/server-quickstart.md
index e9f057c015..49511da872 100644
--- a/docs/server-quickstart.md
+++ b/docs/server-quickstart.md
@@ -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);
diff --git a/docs/server.md b/docs/server.md
index 918b3c297e..dee32b4c6f 100644
--- a/docs/server.md
+++ b/docs/server.md
@@ -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()`
> [!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"
diff --git a/docs/v1/.vitepress/config.mts b/docs/v1/.vitepress/config.mts
new file mode 100644
index 0000000000..9870c131b2
--- /dev/null
+++ b/docs/v1/.vitepress/config.mts
@@ -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' }]
+ }
+});
diff --git a/docs/v1/.vitepress/theme/Banner.vue b/docs/v1/.vitepress/theme/Banner.vue
new file mode 100644
index 0000000000..98c5748b4a
--- /dev/null
+++ b/docs/v1/.vitepress/theme/Banner.vue
@@ -0,0 +1,6 @@
+
+
+ You are viewing the v1.x documentation. The in-development v2 docs are at
+ /v2/.
+