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

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

View file

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

View file

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

View file

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

View file

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