docs: restructure into topical sections and add the four most-asked-for pages
Regroup the 40 docs pages from one flat 15-chapter tutorial plus a 15-item "Advanced" grab-bag into topical sections a reader would scan for, move each page's file into the directory of its new section, reword every page to stand on its own, and add the four pages users were filing issues instead of finding: Troubleshooting, Deploy & scale, Connect to a real host, and Serving legacy clients.
Motivation and Context
The current docs are excellent page-by-page but render as one continuous book, and the four questions the issue tracker sees most have no page. Three kinds of change, in commits that each stand alone:
1. Structure (the nav, then the files)
- "Running your server", the section a deploying user needs most, had exactly one child, and it was titled "ASGI". That page actually held the DNS-rebinding 421 answer, mounting into an existing FastAPI/Starlette app, and CORS; #1798 is literally a user filing an issue asking for a "Guide" to content that already existed on it. It becomes "Add to an existing app", and the section grows Authorization, OpenTelemetry, and two of the new pages.
- "Advanced" held 15 pages, only 2 of which were honestly advanced. Every misfiled page moves to a topical home; the 5 that remain are genuine escape hatches behind a real, clickable index.
- The one page that explains the two protocol eras was the 4th child of "The Client", where no server author looks. It and "Deprecated features" are top-level now.
The regroup and the file moves are separate commits on purpose: the regroup moves zero files (nav sections, nav titles, and file paths are three independent things), and a purely mechanical commit then moves each of the 28 affected pages so its directory matches its section, under one rule (the directory follows the section; the filename stem never changes). docs/tutorial/ is gone, and the old URLs are deliberately not redirected: the docs site is days old and v2 is a beta, so there is nothing meaningful to keep alive. None of docs_src/ moves: the --8<-- snippet includes are repo-root-relative.
The new left panel
MCP Python SDK
Get started install -> first server -> connect it to a real host -> test it
Servers one page per thing a server exposes; Tools first
Inside your handler the Context, dependencies, and everything a
running handler can do
Running your server Add to an existing app / Deploy & scale
/ Authorization / OpenTelemetry / Serving legacy clients
Clients Callbacks / Transports / OAuth / Identity assertion
/ Multiple servers / Caching
Protocol versions
Deprecated features
Advanced The low-level Server / Pagination / Middleware
/ Extensions / MCP Apps
Troubleshooting
Migration Guide
API Reference
2. Wording: every page stands on its own
The pages were written for a linear read-through, so many referred to the reader's history on another page ("In Tools you returned a str and the result came back twice...", "the input schema you met in Tools", "you already know"). Most people arrive at reference docs from a search engine and read one page; for them those sentences are false. An audit of all 42 pages found 24 such sentences on 16 pages; each is rewritten to carry the same facts and (almost always) the same cross-link with only the false claim about the reader's history removed. Cross-references are deliberately untouched: routing the reader elsewhere for more is what good reference docs do. Only a sentence that depends on another page to make sense was the bug. Also: "chapter" becomes "page" everywhere, the page-bottom "Next: ..." course framing is dropped outside the (deliberately sequential) Get started section, five sentences that stated a legacy-only mechanism as a universal truth are made era-accurate, and the elicitation page now leads with the era-portable resolver instead of the legacy ctx.elicit() verb.
3. Four new pages
Every code block on every new page is an executable file under docs_src/, included by the page and exercised by the test suite, which grows from 859 to 973 tests.
- Troubleshooting (top level): every heading is the exact text of an error the SDK produces (the client-side text where that is what a user actually sees), followed by what it means and the one-move fix, and every quoted error is reproduced by a test. It opens with the one that wraps all the others: anyio's
ExceptionGroup, which every exception escapingasync with Client(...)arrives inside. - Deploy & scale: the DNS-rebinding Host allowlist, the most-reported deploy failure, moves here from "Add to an existing app"; its config example was the doc set's one untested inline snippet and is now a tested file. Then the two things "more than one worker" changes:
requestStateacross workers (shared keys and the same server name, because the name is the seal's default audience claim), and change notifications across replicas through a sharedSubscriptionBus. Both are proved by tests that run two server instances in memory, the wrong way and the right way. - Connect to a real host: one tested server file, one short section per host.
mcp installsupports exactly one host (Claude Desktop), so the page shows the exact JSON it writes and where, and gives Claude Code, Cursor, and VS Code their one config block each. - Serving legacy clients: one
streamable_http_app()serves both protocol eras with nothing to configure; what a legacy client costs you is a session (so multiple workers need sticky routing);stateless_http=Trueis legacy-leg-only and trades away the server-to-client channels. Its central example is the dual-era pitch as a passing test: one server, oneResolvetool, and a legacy and a modern client served concurrently in memory.
How Has This Been Tested?
mkdocs build --strictis clean, and under strict mode a missing nav path, a page absent from the nav, a broken inter-page link or anchor, and a bad--8<--include are each a build failure.- The full docs test suite passes: 973 tests (from 859), including 44 new tests behind the four new pages and the CI lint of every new page's fenced code.
./scripts/test(coverage, branch mode,fail_under = 100) passes. All pre-commit hooks pass on every commit. - Beyond CI: every factual claim on the new pages was checked against
src/mcp/(not against other docs), every verbatim error and log string was grep-verified against the SDK's literals, and the rendered site was spot-checked (no literal--8<--survives into the HTML; the new pages render).
Breaking Changes
No code or API changes. The moved pages' old URLs are not redirected, deliberately: the docs site is days old and v2 is pre-release, and the themed 404 page carries the full navigation.
Types of changes
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
Checklist
- I have read the MCP Documentation
- My code follows the repository's style guidelines
- New and existing tests pass locally
- I have added appropriate error handling
- I have added or updated documentation as needed
Additional context
- The
mkdocs-materialfloor is raised to>=9.7.0becausenavigation.path(the breadcrumb feature, already enabled inmkdocs.yml) shipped in the 9.7.0 community edition; the previous>=9.5.45floor let a fresh resolve silently drop a configured feature. - A nav title and a page H1 may differ, deliberately: the sidebar names the reader's goal ("Multiple servers", "Images, audio & icons"), the page keeps its honest technical name ("Session groups", "Media").
- Docs are written v2-first: the modern (2026-07-28) way is presented unqualified, and legacy behaviour appears as short asides on the pages where it genuinely matters, plus one page each for the era model (Protocol versions), the operational posture (Serving legacy clients), and the removed features (Deprecated features). Interleaving v1-to-v2 migration notes throughout the pages is deliberately out of scope for this PR;
docs/migration.mdremains the one migration page.