Present v2 as the stable release across the README, docs, and policies - #3178
Present v2 as the stable release across the README, docs, and policies#3178maxisbey wants to merge 2 commits into
Conversation
📚 Documentation preview
|
There was a problem hiding this comment.
Beyond the inline nits, I also checked the new AGENTS.md pointer to RELEASE.md: at this commit RELEASE.md still says stable releases are cut from v1.x, but the PR description declares the RELEASE.md-update PR as a merge-order dependency, so that is a release-day sequencing item rather than a defect in this diff.
Extended reasoning...
Both inline findings were verified against the repository (the stale pre-release wording in docs/advanced/middleware.md and the remaining /v2/ URLs in the two pyproject.toml files and mkdocs.yml). The one additional concern examined was the AGENTS.md reference to RELEASE.md, whose current content contradicts this PR's branching-model text; it was ruled out because the PR explicitly depends on a separate RELEASE.md PR merging first. Not approving: the findings are nits, but this is the release-day framing flip with strict merge-order dependencies and author-flagged wording decisions (SECURITY.md support horizon, spec blog link swap) that warrant a maintainer's eye on timing and policy.
| !!! note "v2 is the stable line" | ||
| `pip install mcp` installs 2.x, and **[Installation](get-started/installation.md)** has the | ||
| copy-paste install line. If anything in v2 breaks, surprises, or slows you down, | ||
| [tell us](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml). |
There was a problem hiding this comment.
🟡 The pre-release-tense sweep missed docs/advanced/middleware.md (line 9), a published docs page (Advanced > Middleware in the mkdocs nav) that still says "The signature and semantics are expected to change before v2 is final." Once this PR merges and v2.0.0 is tagged, the stable docs site promises API changes before a release that has already happened. Reword to post-stable framing (e.g. "may change in a future release"), keeping the provisional label the way this PR's whats-new.md edit does.
Extended reasoning...
What the bug is. This PR's stated purpose is to present v2 as the stable release "across the README, docs, and policies," and it systematically removes every pre-release-tense framing from the pages it touches — including the whats-new.md admonition anchored here, which now declares "v2 is the stable line." But docs/advanced/middleware.md line 9 was missed. Its warning admonition still reads:
Server.middlewareis marked provisional in the source. The signature and semantics are expected to change before v2 is final.
Why this page matters. docs/advanced/middleware.md is in the published docs nav (mkdocs.yml, Advanced > Middleware), so it ships on the stable docs site. This PR is explicitly described as the last merge before the v2.0.0 tag — so the sentence becomes factually wrong the moment the PR achieves its purpose: it promises API changes "before" a release that, at that point, has already happened.
Why the PR creates the inconsistency. Before this PR, "before v2 is final" was accurate — v2 was in beta and the docs said so everywhere. After this PR, every other page says v2 is stable (docs/index.md, whats-new.md, README, installation docs), and the middleware page alone contradicts that framing. The PR even performed exactly this class of fix elsewhere: it reworded a tense in docs/migration.md ("with a v2 pre-release installed" → "while v2 was in pre-release") and its whats-new.md edit shows the intended pattern — keep the "deliberately marked provisional" label for middleware while dropping the beta-timeline language.
Why existing checks don't catch it. The strict mkdocs build and pre-commit checks validate links, snippets, and formatting — not temporal accuracy of prose. And the PR's sweep evidently targeted version pins (2.0.0bN) and /v2/ URLs, which this sentence contains neither of; only a grep for phrases like before v2 is final finds it.
Step-by-step proof.
- This PR merges; per its description,
v2.0.0is tagged immediately after — v2 is now final. - The docs site rebuilds from main.
docs/whats-new.mdsays "v2 is the stable line";docs/index.mdsays "This documents v2, the current stable release line." - A reader evaluating middleware opens Advanced > Middleware and reads: "The signature and semantics are expected to change before v2 is final."
- v2 is final, so the sentence either reads as a promise about a deadline that already passed, or misleads the reader into thinking v2 hasn't shipped yet — directly contradicting steps 2's framing.
How to fix. One-line reword keeping the provisional status but dropping the pre-release timeline, e.g.: "The signature and semantics may change in a future release." (For completeness: examples/stories/middleware/README.md carries similar "planned before beta" / "before v2 final" wording, but the examples/stories/ READMEs are internal dev notes full of TODO(maxisbey) markers, not published docs, and were clearly never in this sweep's scope — the published middleware.md page is the actionable miss.)
Severity. Nit: prose-only, nothing breaks, and unlike the PyPI long description the docs site rebuilds from main, so it's trivially fixable post-tag — but fixing it in this PR keeps the sweep complete.
(Note: the bug tracker anchors this comment to docs/whats-new.md lines 7–10 because the tracker requires a file in the diff; the actual stale text is at docs/advanced/middleware.md:8-9.)
| **The documentation lives at <https://py.sdk.modelcontextprotocol.io/>.** | ||
|
|
||
| It has a [Get started guide](https://py.sdk.modelcontextprotocol.io/v2/get-started/), [What's new in v2](https://py.sdk.modelcontextprotocol.io/v2/whats-new/), the [API reference](https://py.sdk.modelcontextprotocol.io/v2/api/mcp/), and the [migration guide](https://py.sdk.modelcontextprotocol.io/v2/migration/). | ||
| It has a [Get started guide](https://py.sdk.modelcontextprotocol.io/get-started/), [What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/), the [API reference](https://py.sdk.modelcontextprotocol.io/api/mcp/), and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/). |
There was a problem hiding this comment.
🟡 The /v2/ → root URL sweep missed three non-Markdown files: pyproject.toml:145 and src/mcp-types/pyproject.toml:34 still set Documentation = "https://py.sdk.modelcontextprotocol.io/v2/", and mkdocs.yml:7 still has site_url: .../v2/. The two pyproject URLs ship in the immutable 2.0.0 wheel metadata (the PyPI sidebar Documentation link) via the exact same tagged-artifact mechanism this PR cites for updating README.md, so the 2.0.0 PyPI page would show a /v2/ sidebar link next to a README that uses root URLs — worth fixing before the tag.
Extended reasoning...
What was missed. This PR's stated purpose is that root doc links become the canonical v2 docs at the stable release, and it flips every https://py.sdk.modelcontextprotocol.io/v2/... URL to the site root across README.md, the docs pages, and the v2-feedback issue template. Three occurrences in non-Markdown files were left behind (verified at this PR's HEAD):
pyproject.toml:145—Documentation = "https://py.sdk.modelcontextprotocol.io/v2/"under[project.urls]src/mcp-types/pyproject.toml:34— the same URL for themcp-typesdistributionmkdocs.yml:7—site_url: https://py.sdk.modelcontextprotocol.io/v2/
Why the pyproject entries are in scope for this PR. The PR description explains that it must merge last, right before the v2.0.0 tag, because README.md at the tagged commit is the PyPI long description — repo content at the tag becomes permanent PyPI artifacts. The [project.urls] Documentation entry ships in the 2.0.0 wheel metadata through exactly the same mechanism: it becomes the Documentation link in the PyPI project sidebar. The declared dependency on the docs-site-layout PR plausibly covers mkdocs.yml's site_url (that's docs-site configuration), but a site-layout PR would not touch package metadata in the two pyproject.toml files, and nothing in this PR's description assigns them elsewhere.
Concrete walk-through. (1) This PR merges as-is and v2.0.0 is tagged at that commit. (2) The release builds the mcp and mcp-types 2.0.0 wheels; both carry Project-URL: Documentation, https://py.sdk.modelcontextprotocol.io/v2/ in their METADATA. (3) PyPI renders the 2.0.0 page: the long description (this PR's README) links to https://py.sdk.modelcontextprotocol.io/ root URLs throughout, while the sidebar Documentation link on the same page points at /v2/. (4) The docs-site-layout PR (which per the stated merge order lands before this one) flips the site so v2 serves at the root and v1 moves to /v1/; whether /v2/ still resolves afterwards depends entirely on that PR keeping a redirect. (5) PyPI metadata for a released version is immutable, so if /v2/ ever stops resolving, the 2.0.0 sidebar link is permanently broken.
Why nothing else catches it. The pre-commit README-snippets check and the strict docs build that the PR cites as testing only cover Markdown and docs sources — neither inspects [project.urls] in the pyproject files or site_url in mkdocs.yml. The root cause is simply that the URL sweep was run over Markdown/YAML doc content and missed the TOML/config files.
Impact and fix. Nothing breaks functionally — the most likely outcome is a redirect or a merely inconsistent link — which is why this is a nit rather than blocking. But because the metadata is frozen at the tag, the fix is only cheap before tagging: update the two Documentation URLs in pyproject.toml and src/mcp-types/pyproject.toml to https://py.sdk.modelcontextprotocol.io/ in this PR (they follow the same at-the-tag logic as the README changes here). mkdocs.yml's site_url controls the built site's canonical URLs and sitemap; if the docs-site-layout PR doesn't already flip it, it should be updated too, but that one can reasonably be assigned to the layout PR.
| > [!NOTE] | ||
| > **This is v2 of the MCP Python SDK, the current stable release line.** It is a major rework of the SDK, both to support the [2026-07-28 MCP specification](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/) (and every earlier revision) and to fix long-standing architectural issues. Coming from v1? See [What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/) for the tour of what changed and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/) for every breaking change. | ||
| > | ||
| > **v1.x is the only stable release line and remains recommended for production.** It lives on the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x) and continues to receive critical bug fixes and security patches; see [the v1.x README](https://github.com/modelcontextprotocol/python-sdk/blob/v1.x/README.md) for its documentation. `pip` and `uv` don't select a pre-release unless you explicitly request one, so existing installs are unaffected. **If your package depends on `mcp`, add a `<2` upper bound to your version constraint (for example `mcp>=1.27,<2`) before the stable release lands.** | ||
| > **Not ready to migrate?** v1.x lives on the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x), continues to receive critical bug fixes and security patches, and is documented at <https://py.sdk.modelcontextprotocol.io/v1/>. Since `pip install mcp` now installs 2.x, keep a `<2` upper bound on your requirement (for example `mcp>=1.28,<2`) until you've migrated. | ||
| > | ||
| > v2 is a major rework of the SDK, both to support the [2026-07-28 MCP specification release](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/) and to fix long-standing architectural issues. See [What's new in v2](https://py.sdk.modelcontextprotocol.io/v2/whats-new/) for the tour of what changed, and the [migration guide](https://py.sdk.modelcontextprotocol.io/v2/migration/) for every breaking change. Stable v2 is targeted for 2026-07-28, alongside the spec release. Try the pre-releases and [tell us what breaks](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml), or discuss in [#python-sdk-dev on the MCP Contributors Discord](https://discord.gg/6CSzBmMkjX). | ||
| > Something rough, confusing, or broken? [Open an issue](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml) or find us in [#python-sdk-dev on the MCP Contributors Discord](https://discord.gg/6CSzBmMkjX). |
There was a problem hiding this comment.
🟡 The rewritten admonition uses GitHub alert syntax (> [!NOTE]), which PyPI's renderer (readme_renderer, which does not enable the alerts extension) doesn't support — the 2.0.0 PyPI long description will open with the literal text "[!NOTE]" as the first visible line of the blockquote. Since this PR merges last precisely because README.md at the tag becomes the immutable PyPI long description, this is the last cheap moment to fix it: drop the [!NOTE] marker line (a plain blockquote renders fine on both GitHub and PyPI) or switch to a construct PyPI renders.
Extended reasoning...
What the bug is. The rewritten top admonition (README.md lines 16–21) is a GitHub alert: a blockquote whose first line is > [!NOTE]. Alert syntax ([!NOTE], [!CAUTION], …) is a GitHub-app-layer extension, not part of the GFM spec, and PyPI's renderer does not implement it. Because pyproject.toml sets readme = "README.md" (content type text/markdown), PyPI renders the long description via readme_renderer — and that pipeline passes the marker through as literal text.
Verified empirically, not from documentation. Rendering this PR's README with the workspace's readme_renderer 45.0 wheel via readme_renderer.markdown.render(text, variant='GFM') — the exact code path warehouse uses — produces output that begins:
<blockquote><p>[!NOTE]
<strong>This is v2 of the MCP Python SDK, the current stable release line.</strong> ...The root cause is a renderer-config fact: readme_renderer 45.0 configures its comrak backend with autolink/footnotes/header_ids/strikethrough/table/tagfilter/tasklist, but never enables comrak's alerts extension (which exists — so this is definitively a disabled feature, not an unsupported one that might quietly work). The marker line therefore survives as visible junk text at the top of the rendered page.
Why this is in scope for this PR, not merely pre-existing. The old README used > [!CAUTION] through the same mechanism, so pre-release PyPI pages (2.0.0rc1 etc.) already show a literal marker — but those pages are throwaway. This PR rewrites these exact lines, and its entire stated reason for merging last, right before the v2.0.0 tag, is that "README.md at the tagged commit is the PyPI long description." That makes this PR the artifact whose output becomes the permanent front page of the stable 2.0.0 release — and the last cheap moment to fix it, by the PR's own tagged-artifact reasoning (the same reasoning already applied to the /v2/ sidebar-URL finding).
Why existing checks don't catch it. The pre-commit README check only syncs docs_src/ snippets; the strict mkdocs build doesn't render README.md at all (it isn't a docs page); and twine check-style validation only requires the markdown to render without error — literal [!NOTE] text renders fine, it just looks wrong.
Step-by-step proof of the trigger.
- This PR merges;
v2.0.0is tagged at that commit. - The release builds the wheel/sdist with this README as the
text/markdownlong description. - PyPI renders it with readme_renderer's GFM variant; the alerts extension is not enabled, so the
[!NOTE]line survives as text inside a plain<blockquote>. - The stable release's PyPI page opens — directly under the badges — with the stray token "[!NOTE]" above the bold "This is v2 of the MCP Python SDK…" opener. PyPI metadata for a released version is immutable, so 2.0.0's page can never be fixed after the tag.
How to fix. One line, pre-tag: delete the > [!NOTE] marker line so the block is a plain blockquote — it renders acceptably on both GitHub and PyPI, losing only GitHub's colored NOTE styling. (Alternatively, keep the alert on GitHub and strip it at build time, but that machinery isn't worth it for one admonition.)
Severity. Nit: purely cosmetic, nothing breaks, twine check and the docs build both pass — but the blemish is permanent on the release's highest-visibility artifact, and only this PR can still fix it cheaply.
| !!! note "v2 is the stable line" | ||
| `pip install mcp` installs 2.x, and **[Installation](get-started/installation.md)** has the | ||
| copy-paste install line. If anything in v2 breaks, surprises, or slows you down, | ||
| [tell us](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml). |
There was a problem hiding this comment.
🟡 The stable-release tense sweep missed a published docstring: src/mcp/shared/dispatcher.py:253 still says "The lifecycle surface is provisional; run() may change before v2 stable." — scripts/docs/gen_ref_pages.py generates an API reference page for mcp.shared.dispatcher, so mkdocstrings renders this sentence verbatim on the stable docs site (and it ships frozen in the 2.0.0 wheel's help()/IDE tooltips). Reword to post-stable framing, e.g. "run() may change in a future release."
Extended reasoning...
What the bug is. This PR's purpose is to remove pre-release-era framing from everything published at the v2.0.0 tag, but one user-facing artifact outside the Markdown sweep still carries it: the class docstring of mcp.shared.dispatcher.Dispatcher (src/mcp/shared/dispatcher.py:253) reads:
The lifecycle surface is provisional;
run()may change before v2 stable.
Once this PR merges as the last commit before the v2.0.0 tag (its stated merge position), that sentence promises API changes before a moment that has already passed — contradicting the site-wide "v2 is the stable line" framing this PR establishes in docs/whats-new.md, docs/index.md, and the README.
Why this is user-facing, not an internal comment. scripts/docs/gen_ref_pages.py generates an API reference page for every public module: its only filters are underscore-prefixed path components (line 190) and EXCLUDED = frozenset({"mcp.types"}) (line 37). mcp.shared.dispatcher passes both, so the build emits docs/api/mcp/shared/dispatcher.md containing ::: mcp.shared.dispatcher, and mkdocstrings renders the Dispatcher class docstring verbatim on the published docs site — the same site whose other pages this PR just rewrote to say v2 is stable. The docstring also ships frozen in the 2.0.0 wheel, surfacing in help() and IDE tooltips for anyone on the released package.
Why the existing checks miss it. The strict docs build renders the docstring happily — it is valid prose — and the PR's sweep targeted version pins (2.0.0bN) and /v2/ URLs in Markdown/YAML, which this sentence contains neither of. Only a grep for phrases like before v2 stable over the source tree finds it.
Not a duplicate of the earlier middleware.md comment. That finding is docs/advanced/middleware.md line 9, a hand-written docs page reached through the mkdocs nav; fixing it does not touch this docstring, and its reasoning scoped itself to middleware.md as "the actionable miss" without examining source docstrings or the generated API reference. This one reaches readers through a different mechanism (gen_ref_pages → mkdocstrings) and needs its own one-line reword.
Step-by-step proof.
- This PR merges; per its description,
v2.0.0is tagged immediately after — v2 is now stable. - The docs site rebuilds from main.
docs/whats-new.mdsays "v2 is the stable line";docs/index.mdsays "This documents v2, the current stable release line." - A reader browsing the API reference opens the
mcp.shared.dispatcherpage (generated bygen_ref_pages.py, rendered by mkdocstrings) and reads: "The lifecycle surface is provisional;run()may change before v2 stable." - v2 is stable, so the sentence either reads as a promise about a deadline that already passed, or misleads the reader into thinking v2 hasn't shipped — directly contradicting step 2's framing. The same text appears in
help(mcp.shared.dispatcher.Dispatcher)for anyone on the 2.0.0 wheel, where it can never be fixed post-release.
How to fix. One-line reword keeping the provisional label but dropping the pre-release timeline: "The lifecycle surface is provisional; run() may change in a future release." For a complete cleanup of the same stale phrasing, there is also an internal-only occurrence (not rendered anywhere) in the TODO comment at src/mcp/server/lowlevel/server.py:436-438 ("...before v2 final").
Severity. Nit: prose-only, nothing breaks. Unlike the immutable PyPI long description, the docs site rebuilds from main, so the rendered page is fixable post-tag — but the wheel's docstrings (help()/IDE surface) are frozen per release, so pre-tag is the cheap moment to fix it.
| description: Bugs, API friction, or docs gaps in v2 of the SDK | ||
| title: "[v2] " | ||
| labels: ["v2-alpha"] | ||
| labels: ["v2"] |
There was a problem hiding this comment.
🟣 Pre-existing (files not touched by this PR, but this PR performs exactly this class of label edit on the sibling template): the labels declared by the two neighboring issue templates don't exist in the repo, so GitHub silently drops them and issues filed through them arrive unlabeled — bug.yaml sets need confirmation (repo label is needs confirmation, plural) and feature-request.yaml sets feature request (no such label). The v2 label this line writes DOES exist, so this edit is correct; the fix is to align the two siblings (need confirmation → needs confirmation, and point feature-request.yaml at an existing label such as enhancement or create feature request).
Extended reasoning...
What the bug is. Two of the three issue templates in this directory that declare labels reference labels that do not exist in the repository. .github/ISSUE_TEMPLATE/bug.yaml line 3 sets labels: ["need confirmation"], but the repository's actual label is needs confirmation (plural). .github/ISSUE_TEMPLATE/feature-request.yaml line 3 sets labels: ["feature request"], which does not exist at all (the closest existing labels are enhancement and question). Both were verified against the repo's live label set via the GitHub REST API — 33 labels total (a single page at per_page=100), containing needs confirmation, needs maintainer, v2, and v2-alpha, but neither need confirmation nor feature request.
How it manifests. GitHub issue forms silently skip labels that don't already exist in the repository — they are never auto-created. So every issue filed through the bug template or the feature-request template arrives with no label at all. This is empirically visible: the most recent issues (#3193, #3194) carry zero labels, consistent with the template label never applying.
Why it matters. The repo's own triage flow keys off this label. CONTRIBUTING.md explicitly states that issues labeled needs confirmation are not ready for work (CONTRIBUTING.md:51 in this PR's checkout), and the bug template's clear intent is to land new reports in that triage state. Because the label string doesn't match, that state never applies automatically, and every bug report needs a maintainer to hand-apply the label before the documented workflow functions.
Why it's surfaced on this PR. This PR edits the labels: line of the sibling template in the same directory — v2-feedback.yaml's v2-alpha → v2 — which is exactly this class of label-hygiene edit. Notably, this PR's own edit is correct: v2 exists in the repo's label set, so the v2-feedback template will label correctly after merge. The two broken siblings simply weren't part of the sweep, which is what makes this the natural PR to mention them on.
Step-by-step proof. (1) A user files a bug via the bug template. (2) GitHub reads labels: ["need confirmation"] from the form definition and looks the string up in the repo's label set. (3) No label with that exact name exists (needs confirmation ≠ need confirmation), so GitHub drops it silently — issue forms never create labels. (4) The issue is created with zero labels, as seen on #3193 and #3194. (5) Triage queries and contributor guidance built around label:"needs confirmation" never see the issue in its intended state.
How to fix. One-line changes in each sibling: bug.yaml "need confirmation" → "needs confirmation", and either create a feature request label or point feature-request.yaml at an existing one (e.g. enhancement). Optionally, the same mismatch pattern appears in CONTRIBUTING.md, which refers to a needs maintainer action label while the repo label is needs maintainer. Since neither defective file is modified by this PR, this is pre-existing and should not block merge — but it's cheap to fold into this PR (or a tiny follow-up) since it's the same directory and the same kind of edit.
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
The README, the get-started docs, the What's new page, SECURITY.md, and the contributor docs all still described v2 as a pre-release: pin an exact 2.0.0b1, do not use in production, v1.x is the only stable line. This flips that framing for the stable release. Install lines become plain `mcp[cli]` (the launch-command page keeps only the exact pin that `mcp install` itself writes), the pre-release banners and pin warnings go away, the What's new page drops its beta note and the two stale "since 2.0.0bN" provenance markers, and SECURITY.md gains a supported-versions table (2.x current, 1.28.x maintenance). v1.x is presented as the maintenance line throughout, with its docs at /v1/ and a `<2` bound for projects that are not ready to migrate. The branching-model text in AGENTS.md and CONTRIBUTING.md now describes main as the stable line, and the v2 feedback issue template loses its pre-release wording and the v2-alpha label. No-Verification-Needed: doc-only
No-Verification-Needed: doc-only
| - `main` is the current stable line (v2); releases are cut from it (see | ||
| `RELEASE.md`). |
There was a problem hiding this comment.
🟡 The new Branching Model bullet says releases are cut from main "(see RELEASE.md)", but RELEASE.md still says the opposite — its "Major or Minor Release" section instructs cutting stable releases from the v1.x branch and calls main "the v2 rework", and its v2 pre-release steps reference the exact pins this PR just removed. The declared dependency #3179 (the RELEASE.md rewrite) did not land before this merged; land it (or fold it in) before the v2.0.0 tag so a releaser following RELEASE.md doesn't target the stable tag at v1.x.
Extended reasoning...
What the bug is. This PR's rewritten AGENTS.md Branching Model opens with: "main is the current stable line (v2); releases are cut from it (see RELEASE.md)." But RELEASE.md at the merged state says the opposite. Its "Major or Minor Release" section (RELEASE.md:12-17) reads: "Stable releases are cut from the v1.x branch ... set the tag's target to the v1.x branch — the UI defaults to main, which is the v2 rework, and a v1 tag created there would publish the v2 codebase as a stable release." So the file the new bullet cites frames a main-targeted stable tag as a dangerous mistake — the exact action the v2.0.0 release requires.\n\nThe staleness goes beyond the one sentence. RELEASE.md's "## v2 Pre-releases" section (lines 21-57) still treats v2 as pre-release-only: step 1 tells the releaser to grep for the outgoing pre-release version pins in README.md, docs/index.md, docs/get-started/installation.md, and docs/get-started/real-host.md — the exact pins this PR just deleted from those exact files — and step 3 says the --prerelease flag keeps GitHub's "Latest" badge "pointing at the stable v1.x line."\n\nWhy this is attributable to this PR's merge, not merely pre-existing. The PR description explicitly declared "Depends on: ... #3179 (so AGENTS.md's pointer to RELEASE.md is not stale)" — but that dependency did not land first. git log at HEAD (which includes this PR's merge commit 32f7e37 and the later fixup e0dad46) shows RELEASE.md last touched at 148278e, before this PR merged, and no commit referencing #3179 exists. The PR's own stated merge-order plan was violated, so the new pointer is stale in-tree right now, in the one file the PR's own new text cites.\n\nStep-by-step proof. (1) A contributor or agent reads the merged AGENTS.md line 5-6: main is stable, releases are cut from it, see RELEASE.md. (2) They open RELEASE.md. Under "Major or Minor Release" — the section that applies to v2.0.0 — it says stable releases are cut from v1.x and warns that tagging main "would publish the v2 codebase as a stable release" as if that were the failure mode rather than the goal. (3) A releaser literally following it targets the v2.0.0 tag at v1.x (publishing the v1 codebase as 2.0.0) or treats tagging main as forbidden; one literally following the v2 section greps for pins that no longer exist. Either way the two docs contradict each other at the most consequential moment — release day.\n\nWhy nothing catches it. Both files are prose; the docs build, pre-commit, and markdownlint validate formatting and links, not cross-file consistency of process instructions. Only a human reading both files side by side notices.\n\nWhy nit rather than blocking. RELEASE.md and AGENTS.md are contributor-facing repo docs, not shipped artifacts — unlike the README (frozen into PyPI metadata at the tag), they can be fixed at any time, including after v2.0.0 ships. The designated fix already exists: #3179 is the RELEASE.md rewrite, declared in this PR's own description. And the concrete-failure path requires the release engineer — the author of both PRs and of the merge-order plan — to follow the stale doc blindly while ignoring that plan. Merging this PR breaks nothing by itself; the actionable item is sequencing.\n\nHow to fix. Land #3179 before the v2.0.0 tag (ideally it should have preceded this merge), or fold its RELEASE.md rewrite into a quick follow-up: "Major or Minor Release" should describe cutting stable releases from main, and the v2-pre-release-specific steps (grep-the-pins, --prerelease-keeps-Latest-on-v1.x) should be retired or reframed for the post-stable era.
| > **This is v2 of the MCP Python SDK, the current stable release line.** It is a major rework of the SDK, both to support the [2026-07-28 MCP specification](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/) (and every earlier revision) and to fix long-standing architectural issues. Coming from v1? See [What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/) for the tour of what changed and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/) for every breaking change. | ||
| > | ||
| > **v1.x is the only stable release line and remains recommended for production.** It lives on the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x) and continues to receive critical bug fixes and security patches; see [the v1.x README](https://github.com/modelcontextprotocol/python-sdk/blob/v1.x/README.md) for its documentation. `pip` and `uv` don't select a pre-release unless you explicitly request one, so existing installs are unaffected. **If your package depends on `mcp`, add a `<2` upper bound to your version constraint (for example `mcp>=1.27,<2`) before the stable release lands.** | ||
| > **Not ready to migrate?** v1.x lives on the [`v1.x` branch](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x), continues to receive critical bug fixes and security patches, and is documented at <https://py.sdk.modelcontextprotocol.io/v1/>. Since `pip install mcp` now installs 2.x, keep a `<2` upper bound on your requirement (for example `mcp>=1.28,<2`) until you've migrated. |
There was a problem hiding this comment.
🟡 The declared dependency #3176 (the docs layout flip) has not landed: .github/workflows/deploy-docs.yml:51 and scripts/build-docs.sh still deploy v1.x at the site root and main under /v2/, with nothing published at /v1/ — so at the merged state every root URL this PR introduces (README, v2-feedback.yaml, SECURITY.md, docs/index.md llms.txt links) resolves to v1 content or 404s, and every /v1/ URL it introduces (this line, docs/index.md:5, docs/whats-new.md:208) 404s outright. The links self-heal once #3176 lands, but it must land before the v2.0.0 tag, since this README is frozen into the PyPI long description.
Extended reasoning...
What the bug is. This PR rewrites every doc URL on the assumption that the docs site layout has flipped — v2 served at the site root, v1 moved under /v1/ — and its own description declares "Depends on: #3176 (root doc links resolve to v2 only after the layout flips)" with a release-day merge order of "layout PRs, then this, then the tag." That dependency did not land. At HEAD (which includes this PR's merge), .github/workflows/deploy-docs.yml:51 still reads "Build combined docs (v1.x at /, main at /v2/)", and scripts/build-docs.sh still ends with build_branch v1.x ... $OUTPUT_DIR followed by build_branch main ... $OUTPUT_DIR/v2 — v1 at the root, v2 under /v2/, and nothing deployed at /v1/ at all. git log --all --grep=3176 finds no commit, and the deploy pipeline was last touched before this PR merged.\n\nHow it manifests. Two classes of broken link, both live on the deployed site right now:\n\n1. Every root URL this PR introduces resolves to the v1.x docs or 404s: the README's whats-new / migration / get-started / API links (lines 17–27), the v2-feedback template's "Docs: https://py.sdk.modelcontextprotocol.io/ · Migration: .../migration/", SECURITY.md's migration-guide link, and docs/index.md's llms.txt / llms-full.txt links. For example, https://py.sdk.modelcontextprotocol.io/whats-new/ has no counterpart on the v1 site, so it 404s; /migration/ either 404s or serves v1 content that does not describe v2's breaking changes.\n2. Every /v1/ URL this PR introduces 404s outright, because the build script never publishes anything under /v1/: README.md line 19 ("documented at https://py.sdk.modelcontextprotocol.io/v1/"), docs/index.md line 5 (the "v1.x docs" link), and docs/whats-new.md line 208.\n\nStep-by-step proof. (1) A v1 user reads the merged README's note block: "Not ready to migrate? v1.x ... is documented at https://py.sdk.modelcontextprotocol.io/v1/". (2) They open that URL. (3) The deploy pipeline built the site from build-docs.sh, which wrote v1.x's site to the output root and main's site to /v2/ — no /v1/ directory exists in the artifact. (4) The URL 404s. Symmetrically, a reader clicking the README's "What's new in v2" root link lands on the v1 site, where /whats-new/ does not exist.\n\nWhy nothing catches it. The PR's stated testing (pre-commit README-snippets check, strict docs build) validates content and intra-docs links, not the deployed site's URL layout — the layout lives in the deploy workflow and build-docs.sh, neither of which this PR touches. Only comparing the PR's URL scheme against the actual deploy script reveals the mismatch.\n\nWhy this is distinct from the existing comments. The earlier inline comment on README.md:27 is about three /v2/ URLs the sweep missed in pyproject.toml / mkdocs.yml — fixing those URLs does nothing about the deploy layout. The AGENTS.md comment covers the other unlanded dependency, #3179's RELEASE.md rewrite. Neither covers #3176 being unlanded and the resulting broken link set.\n\nHow to fix. Land the #3176 layout flip — update deploy-docs.yml and scripts/build-docs.sh so main builds at the root and v1.x under /v1/ — before the v2.0.0 tag. This is nit-level rather than blocking because the fix already exists as this PR's own declared dependency and every link self-heals the moment it lands; but the deadline is real: the README at the tagged commit becomes the immutable PyPI long description, so if the tag happens before #3176, the 2.0.0 PyPI page permanently carries a /v1/ link that was broken at publication time.
| !!! note "v2 is the stable line" | ||
| `pip install mcp` installs 2.x, and **[Installation](get-started/installation.md)** has the | ||
| copy-paste install line. If anything in v2 breaks, surprises, or slows you down, | ||
| [tell us](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml). |
There was a problem hiding this comment.
🟡 One more stable-release tense-sweep miss beyond the two already flagged (docs/advanced/middleware.md and src/mcp/shared/dispatcher.py): the public MCPServer.middleware property docstring at src/mcp/server/mcpserver/server.py:270-271 still says the signature is "expected to change before v2 is final" — it renders on the stable docs API reference via gen_ref_pages/mkdocstrings and ships frozen in the 2.0.0 wheel (help()/IDE tooltips), contradicting the "v2 is the stable line" framing this note adds. Reword to e.g. "may change in a future release"; tests/interaction/_requirements.py:663 ("planned before v2 stable") is the same phrasing, internal-only.
Extended reasoning...
What the bug is. This PR's purpose is to remove pre-release-era framing from everything published at the v2.0.0 tag, and two source-tree occurrences remain that are not covered by the comments already filed on this PR. The main one is the public MCPServer.middleware property docstring at src/mcp/server/mcpserver/server.py:270-271 (added by #3201, an ancestor of this PR's HEAD 83289cc), which ends:
Provisional - the signature is expected to change before v2 is final; see the middleware guide.
Once this PR merges as the last commit before the v2.0.0 tag (its stated merge position), that sentence promises signature changes before a moment that has already passed — directly contradicting the "v2 is the stable line" note this hunk adds to docs/whats-new.md.
Why it is user-facing, not an internal comment. scripts/docs/gen_ref_pages.py generates an API reference page for every public module: its only filters are underscore-prefixed path components and EXCLUDED = frozenset({"mcp.types"}). mcp.server.mcpserver passes both, so mkdocstrings renders this docstring verbatim on the published docs site — the same site whose other pages this PR just rewrote to say v2 is stable. MCPServer is the flagship high-level server class, so this is one of the most-visited API reference pages. The docstring also ships frozen in the 2.0.0 wheel, surfacing in help(MCPServer.middleware) and IDE tooltips for everyone on the released package, where it can never be fixed post-tag.
Why the PR's sweep and existing checks miss it. The sweep targeted version pins (2.0.0bN, 2.0.0rc1) and /v2/ URLs in Markdown/YAML; this sentence contains neither, and the strict docs build renders it happily as valid prose. Only a grep for phrases like before v2 is final / before v2 stable over the whole tree finds it.
Not a duplicate of the comments already on this PR. The earlier tense-sweep comments cover docs/advanced/middleware.md line 9 (the hand-written docs page) and src/mcp/shared/dispatcher.py:253 (the Dispatcher class docstring). Fixing either does not touch server.py:271 — this docstring reaches readers through its own gen_ref_pages → mkdocstrings page for mcp.server.mcpserver and needs its own one-line reword. The dispatcher comment's own residual note referenced only an internal TODO comment, not this docstring.
Step-by-step proof.
- This PR merges; per its description,
v2.0.0is tagged immediately after — v2 is now stable. - The docs site rebuilds from main:
docs/whats-new.mdsays "v2 is the stable line",docs/index.mdsays "This documents v2, the current stable release line." - A reader opens the API reference page for
mcp.server.mcpserver(generated bygen_ref_pages.py, rendered by mkdocstrings) and reads underMCPServer.middleware: "the signature is expected to change before v2 is final". - v2 is final, so the sentence either promises changes by a deadline that already passed or misleads the reader into thinking v2 hasn't shipped — contradicting step 2's framing on the same site. The same text appears in
help()for anyone on the 2.0.0 wheel, permanently.
How to fix. Keep the deliberate "provisional" label (the middleware chain genuinely is marked provisional) but drop the pre-release deadline: "Provisional - the signature may change in a future release; see the middleware guide." For completeness of the same sweep, tests/interaction/_requirements.py:663 says "A typed fault channel restoring visibility is planned before v2 stable." — internal-only (never rendered or shipped as docs), but the same stale phrasing; reword to "planned for a future release".
Severity. Nit: prose-only, nothing breaks, same class as the two tense-sweep misses already filed on this PR. The reason to fold it in here rather than a follow-up is the same tagged-artifact logic this PR itself invokes: the docstring freezes into the immutable 2.0.0 wheel at the tag, so this PR — intended to merge last, right before the tag — is the last cheap moment to fix it.
The README, the get-started docs, the What's new page, SECURITY.md, and the contributor docs all still describe v2 as a pre-release: pin an exact
2.0.0b1, "do not use in production," "v1.x is the only stable line." This flips that framing for the stable release. It is intended to merge last, right before thev2.0.0tag, becauseREADME.mdat the tagged commit is the PyPI long description.Motivation and Context
At the stable release,
pip install mcpinstalls 2.x, so the pre-release guidance inverts: the pin warnings that told readers "unpinned gives you v1.x" become false, and the caution banner would ship inside 2.0.0's PyPI page.Changes:
<2bound for projects not ready to migrate); the install line becomes plainmcp[cli]; doc links use the site root.index.md,installation.md, andreal-host.mdlose their beta admonitions and version pins (the launch-command page keeps only the exact pin thatmcp installitself writes);whats-new.mddrops its beta note and two stale "since 2.0.0bN" provenance markers (one of which named2.0.0b3, a version that will never exist); a one-line tense fix inmigration.md.SECURITY.mdgains a supported-versions table (2.x current, 1.28.x maintenance, older and pre-releases unsupported).AGENTS.mdandCONTRIBUTING.mddescribe main as the stable line and v1.x as the maintenance branch; the v2 feedback issue template drops its pre-release wording and thev2-alphalabel.Reviewer attention on wording: SECURITY.md leaves the v1.x maintenance horizon open-ended (no end date); the README still links the spec release-candidate blog post, to be swapped for the final release post once that URL exists;
mcp>=1.28,<2is the example bound used throughout.Depends on: #3176 (root doc links resolve to v2 only after the layout flips) and #3179 (so
AGENTS.md's pointer toRELEASE.mdis not stale). Merge order for release day: layout PRs, then this, then the tag.How Has This Been Tested?
Docs-only. Pre-commit's README-snippets check passes; the strict docs build runs in CI.
Breaking Changes
None.
Types of changes
Checklist
AI disclosure
AI assistance was used to draft and validate this change; I reviewed the result and take responsibility for it.
AI Disclaimer