docs: replace the typedoc site with VitePress (v2 + v1 reface) (#2395)
This commit is contained in:
parent
801111ea15
commit
732abe0ac4
26 changed files with 1996 additions and 270 deletions
7
.github/workflows/deploy-docs.yml
vendored
7
.github/workflows/deploy-docs.yml
vendored
|
|
@ -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
9
.gitignore
vendored
|
|
@ -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/
|
||||
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
89
docs/.vitepress/config.mts
Normal file
89
docs/.vitepress/config.mts
Normal 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' }]
|
||||
}
|
||||
});
|
||||
6
docs/.vitepress/theme/Banner.vue
Normal file
6
docs/.vitepress/theme/Banner.vue
Normal 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>
|
||||
148
docs/.vitepress/theme/custom.css
Normal file
148
docs/.vitepress/theme/custom.css
Normal 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;
|
||||
}
|
||||
15
docs/.vitepress/theme/index.ts
Normal file
15
docs/.vitepress/theme/index.ts
Normal 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;
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
70
docs/v1/.vitepress/config.mts
Normal file
70
docs/v1/.vitepress/config.mts
Normal 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' }]
|
||||
}
|
||||
});
|
||||
6
docs/v1/.vitepress/theme/Banner.vue
Normal file
6
docs/v1/.vitepress/theme/Banner.vue
Normal 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>
|
||||
16
docs/v1/.vitepress/theme/index.ts
Normal file
16
docs/v1/.vitepress/theme/index.ts
Normal 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;
|
||||
|
|
@ -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);
|
||||
});
|
||||
14
package.json
14
package.json
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue