Skip to content

Commit 732abe0

Browse files
docs: replace the typedoc site with VitePress (v2 + v1 reface) (#2395)
1 parent 801111e commit 732abe0

26 files changed

Lines changed: 1996 additions & 270 deletions

.github/workflows/deploy-docs.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ jobs:
3737
cache: pnpm
3838
cache-dependency-path: pnpm-lock.yaml
3939

40-
- name: Generate multi-version docs
41-
run: bash scripts/generate-multidoc.sh tmp/docs-combined
40+
- name: Install dependencies
41+
run: pnpm install
42+
43+
- name: Build multi-version docs site
44+
run: bash scripts/build-docs-site.sh tmp/docs-combined
4245

4346
- name: Configure Pages
4447
uses: actions/configure-pages@v6

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ dist/
5050
# Git worktrees for local doc generation
5151
.worktrees/
5252

53+
# Generated docs-site artifacts
54+
docs/index.md
55+
docs/api/
56+
docs/.vitepress/cache/
57+
docs/.vitepress/dist/
58+
docs/v1/content/
59+
docs/v1/.vitepress/cache/
60+
docs/v1/.vitepress/dist/
61+
5362
# Conformance test results
5463
results/
5564

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,16 @@ The complete code for each tutorial is in [`examples/server-quickstart/`](https:
150150

151151
### Building docs locally
152152

153-
To generate the API reference documentation locally:
153+
To work on the documentation site locally:
154154

155155
```bash
156-
pnpm docs # Generate V2 docs only (output: tmp/docs/)
157-
pnpm docs:multi # Generate combined V1 + V2 docs (output: tmp/docs-combined/)
156+
pnpm docs:api # Generate the API reference markdown (output: docs/api/)
157+
pnpm docs:dev # Start the VitePress dev server for the V2 site
158+
pnpm docs:build # Build the V2 site (output: docs/.vitepress/dist/)
159+
pnpm docs:multi # Build the combined V1 + V2 site (output: tmp/docs-combined/)
158160
```
159161

160-
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/`.
162+
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/`.
161163

162164
## v1 (legacy) documentation and fixes
163165

docs/.vitepress/config.mts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { existsSync, readFileSync } from 'node:fs';
2+
import { dirname, resolve } from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
5+
import { defineConfig, type DefaultTheme } from 'vitepress';
6+
7+
const docsDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
8+
9+
/**
10+
* The API Reference sidebar is generated by `pnpm docs:api` (typedoc + typedoc-vitepress-theme)
11+
* into docs/api/typedoc-sidebar.json. When it is missing (e.g. `docs:dev` before `docs:api`),
12+
* fall back to an empty group with a hint instead of crashing.
13+
*/
14+
function apiSidebarItems(): DefaultTheme.SidebarItem[] {
15+
const sidebarPath = resolve(docsDir, 'api/typedoc-sidebar.json');
16+
if (!existsSync(sidebarPath)) {
17+
console.warn(`[docs] ${sidebarPath} not found — run \`pnpm docs:api\` first to populate the API Reference section.`);
18+
return [];
19+
}
20+
return JSON.parse(readFileSync(sidebarPath, 'utf8'));
21+
}
22+
23+
export default defineConfig({
24+
title: 'MCP TypeScript SDK',
25+
description: 'The TypeScript SDK implementation of the Model Context Protocol specification.',
26+
base: '/v2/',
27+
srcExclude: ['v1/**', 'behavior-surface-pins.md'],
28+
sitemap: { hostname: 'https://ts.sdk.modelcontextprotocol.io/v2/' },
29+
markdown: {
30+
config(md) {
31+
// Spec-generated JSDoc (packages/core-internal/src/types/spec.types.*.ts) carries
32+
// site-root-relative links like /specification/draft/basic/index#meta that are meant
33+
// to resolve on modelcontextprotocol.io. Rewrite them at render time so they work on
34+
// this site (and so the dead-link check sees them as external).
35+
const orig = md.renderer.rules.link_open ?? ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));
36+
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
37+
const href = tokens[idx].attrGet('href');
38+
if (href?.startsWith('/specification/')) {
39+
tokens[idx].attrSet('href', `https://modelcontextprotocol.io${href}`);
40+
}
41+
return orig(tokens, idx, options, env, self);
42+
};
43+
}
44+
},
45+
themeConfig: {
46+
nav: [
47+
{ text: 'Guide', link: '/server-quickstart', activeMatch: '^/(server|client|faq)' },
48+
{ text: 'Migration', link: '/migration/', activeMatch: '^/migration/' },
49+
{ text: 'API Reference', link: '/api/', activeMatch: '^/api/' },
50+
{ text: 'V1 Docs', link: 'https://ts.sdk.modelcontextprotocol.io/' }
51+
],
52+
sidebar: [
53+
{
54+
text: 'Getting started',
55+
items: [
56+
{ text: 'Server Quickstart', link: '/server-quickstart' },
57+
{ text: 'Client Quickstart', link: '/client-quickstart' }
58+
]
59+
},
60+
{
61+
text: 'Guides',
62+
items: [
63+
{ text: 'Server', link: '/server' },
64+
{ text: 'Client', link: '/client' }
65+
]
66+
},
67+
{
68+
text: 'Migration',
69+
items: [
70+
{ text: 'Overview', link: '/migration/' },
71+
{ text: 'Upgrade to v2', link: '/migration/upgrade-to-v2' },
72+
{ text: '2026-07-28 protocol support', link: '/migration/support-2026-07-28' }
73+
]
74+
},
75+
{
76+
text: 'FAQ',
77+
items: [{ text: 'FAQ', link: '/faq' }]
78+
},
79+
{
80+
text: 'API Reference',
81+
collapsed: true,
82+
items: apiSidebarItems()
83+
}
84+
],
85+
outline: { level: [2, 3] },
86+
search: { provider: 'local' },
87+
socialLinks: [{ icon: 'github', link: 'https://github.com/modelcontextprotocol/typescript-sdk' }]
88+
}
89+
});

docs/.vitepress/theme/Banner.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<template>
2+
<div class="version-banner">
3+
You are viewing the in-development v2 documentation. The stable v1 docs are at
4+
<a href="https://ts.sdk.modelcontextprotocol.io/">ts.sdk.modelcontextprotocol.io</a>.
5+
</div>
6+
</template>

docs/.vitepress/theme/custom.css

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**
2+
* Shared theme for the MCP TypeScript SDK docs (v2 and v1 sites).
3+
*
4+
* Visual pass modeled on the Python SDK docs (py.sdk.modelcontextprotocol.io),
5+
* which use mkdocs-material with a monochrome palette: primary/accent black on
6+
* light, white on slate dark. We map that onto VitePress brand variables.
7+
*/
8+
9+
/* ---------------------------------------------------------------- branding */
10+
11+
:root {
12+
/* Monochrome brand: near-black on light backgrounds. */
13+
--vp-c-brand-1: #1a1a1a;
14+
--vp-c-brand-2: #333333;
15+
--vp-c-brand-3: #1f1f1f;
16+
--vp-c-brand-soft: rgba(0, 0, 0, 0.08);
17+
18+
/* Restrained accent for links — dark with a faint blue cast, like
19+
material's black-primary link color. */
20+
--vp-c-text-link: #21409a;
21+
22+
/* Neutral grays. */
23+
--vp-c-gray-1: #dddde3;
24+
--vp-c-gray-2: #e4e4e9;
25+
--vp-c-gray-3: #ebebef;
26+
27+
/* Buttons: solid black, like material's primary buttons. */
28+
--vp-button-brand-bg: #1a1a1a;
29+
--vp-button-brand-border: #1a1a1a;
30+
--vp-button-brand-text: #ffffff;
31+
--vp-button-brand-hover-bg: #333333;
32+
--vp-button-brand-hover-border: #333333;
33+
--vp-button-brand-hover-text: #ffffff;
34+
--vp-button-brand-active-bg: #000000;
35+
--vp-button-brand-active-border: #000000;
36+
--vp-button-brand-active-text: #ffffff;
37+
38+
/* Home hero: plain text, no gradient. */
39+
--vp-home-hero-name-color: var(--vp-c-text-1);
40+
41+
/* Height reserved for the fixed version banner (layout-top slot). */
42+
--vp-layout-top-height: 36px;
43+
}
44+
45+
.dark {
46+
/* Monochrome brand: near-white on dark backgrounds (material "slate"). */
47+
--vp-c-brand-1: #e8e8e8;
48+
--vp-c-brand-2: #cccccc;
49+
--vp-c-brand-3: #dddddd;
50+
--vp-c-brand-soft: rgba(255, 255, 255, 0.12);
51+
52+
--vp-c-text-link: #9db4e8;
53+
54+
--vp-button-brand-bg: #e8e8e8;
55+
--vp-button-brand-border: #e8e8e8;
56+
--vp-button-brand-text: #1a1a1a;
57+
--vp-button-brand-hover-bg: #ffffff;
58+
--vp-button-brand-hover-border: #ffffff;
59+
--vp-button-brand-hover-text: #1a1a1a;
60+
--vp-button-brand-active-bg: #cccccc;
61+
--vp-button-brand-active-border: #cccccc;
62+
--vp-button-brand-active-text: #1a1a1a;
63+
}
64+
65+
/* Links: restrained accent, underline on hover only (material-ish). */
66+
.vp-doc a {
67+
color: var(--vp-c-text-link);
68+
text-decoration: none;
69+
font-weight: 500;
70+
}
71+
72+
.vp-doc a:hover {
73+
text-decoration: underline;
74+
}
75+
76+
/* ------------------------------------------------------------- admonitions */
77+
78+
/*
79+
* Material-style admonitions: left accent border + tinted background.
80+
* Covers VitePress custom containers and GitHub-style alerts (> [!NOTE] …),
81+
* which VitePress renders with the same .custom-block classes.
82+
*/
83+
.vp-doc .custom-block {
84+
border: none;
85+
border-left: 4px solid var(--vp-c-divider);
86+
border-radius: 2px;
87+
padding: 12px 16px;
88+
}
89+
90+
.vp-doc .custom-block .custom-block-title {
91+
font-weight: 700;
92+
}
93+
94+
.vp-doc .custom-block.info,
95+
.vp-doc .custom-block.note {
96+
border-left-color: #448aff;
97+
background-color: rgba(68, 138, 255, 0.08);
98+
}
99+
100+
.vp-doc .custom-block.tip,
101+
.vp-doc .custom-block.important {
102+
border-left-color: #00bfa5;
103+
background-color: rgba(0, 191, 165, 0.08);
104+
}
105+
106+
.vp-doc .custom-block.warning,
107+
.vp-doc .custom-block.caution {
108+
border-left-color: #ff9100;
109+
background-color: rgba(255, 145, 0, 0.08);
110+
}
111+
112+
.vp-doc .custom-block.danger {
113+
border-left-color: #ff5252;
114+
background-color: rgba(255, 82, 82, 0.08);
115+
}
116+
117+
.vp-doc .custom-block.details {
118+
border-left-color: var(--vp-c-divider);
119+
background-color: var(--vp-c-bg-soft);
120+
}
121+
122+
/* -------------------------------------------------------------- the banner */
123+
124+
.version-banner {
125+
position: fixed;
126+
top: 0;
127+
left: 0;
128+
right: 0;
129+
z-index: 35;
130+
height: var(--vp-layout-top-height);
131+
display: flex;
132+
align-items: center;
133+
justify-content: center;
134+
padding: 0 16px;
135+
overflow: hidden;
136+
background-color: var(--vp-c-bg-soft);
137+
border-bottom: 1px solid var(--vp-c-divider);
138+
color: var(--vp-c-text-2);
139+
font-size: 13px;
140+
line-height: 1.3;
141+
text-align: center;
142+
}
143+
144+
.version-banner a {
145+
color: var(--vp-c-text-1);
146+
text-decoration: underline;
147+
margin-left: 4px;
148+
}

docs/.vitepress/theme/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Theme } from 'vitepress';
2+
import DefaultTheme from 'vitepress/theme';
3+
import { h } from 'vue';
4+
5+
import Banner from './Banner.vue';
6+
import './custom.css';
7+
8+
export default {
9+
extends: DefaultTheme,
10+
Layout() {
11+
return h(DefaultTheme.Layout, null, {
12+
'layout-top': () => h(Banner)
13+
});
14+
}
15+
} satisfies Theme;

0 commit comments

Comments
 (0)