Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/

Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
89 changes: 89 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions docs/.vitepress/theme/Banner.vue
Original file line number Diff line number Diff line change
@@ -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 changes: 148 additions & 0 deletions docs/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Loading
Loading