Skip to content

Present v2 as the stable release across the README, docs, and policies - #3178

Merged
maxisbey merged 2 commits into
mainfrom
stable-flip
Jul 28, 2026
Merged

Present v2 as the stable release across the README, docs, and policies#3178
maxisbey merged 2 commits into
mainfrom
stable-flip

Conversation

@maxisbey

@maxisbey maxisbey commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

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 the v2.0.0 tag, because README.md at the tagged commit is the PyPI long description.

Motivation and Context

At the stable release, pip install mcp installs 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:

  • README: the pre-release caution block becomes a short stable-line note (with the "coming from v1" pointers and the <2 bound for projects not ready to migrate); the install line becomes plain mcp[cli]; doc links use the site root.
  • docs: index.md, installation.md, and real-host.md lose their beta admonitions and version pins (the launch-command page keeps only the exact pin that mcp install itself writes); whats-new.md drops its beta note and two stale "since 2.0.0bN" provenance markers (one of which named 2.0.0b3, a version that will never exist); a one-line tense fix in migration.md.
  • SECURITY.md gains a supported-versions table (2.x current, 1.28.x maintenance, older and pre-releases unsupported).
  • AGENTS.md and CONTRIBUTING.md describe main as the stable line and v1.x as the maintenance branch; the v2 feedback issue template drops its pre-release wording and the v2-alpha label.

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,<2 is 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 to RELEASE.md is 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

  • 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

AI disclosure

AI assistance was used to draft and validate this change; I reviewed the result and take responsibility for it.

AI Disclaimer

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation preview

Preview https://pr-3178.mcp-python-docs.pages.dev
Deployment https://e551f1fe.mcp-python-docs.pages.dev
Commit d24bde4
Triggered by @maxisbey
Updated 2026-07-28 13:25:43 UTC

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 10 files

Re-trigger cubic

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docs/whats-new.md
Comment on lines +7 to +10
!!! 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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.middleware is 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.

  1. This PR merges; per its description, v2.0.0 is tagged immediately after — v2 is now final.
  2. The docs site rebuilds from main. docs/whats-new.md says "v2 is the stable line"; docs/index.md says "This documents v2, the current stable release line."
  3. A reader evaluating middleware opens Advanced > Middleware and reads: "The signature and semantics are expected to change before v2 is final."
  4. 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.)

Comment thread README.md
Comment thread README.md
Comment on lines +16 to +21
> [!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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

  1. This PR merges; v2.0.0 is tagged at that commit.
  2. The release builds the wheel/sdist with this README as the text/markdown long description.
  3. 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>.
  4. 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.

Comment thread docs/get-started/real-host.md Outdated
Comment thread docs/whats-new.md
Comment on lines +7 to +10
!!! 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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

  1. This PR merges; per its description, v2.0.0 is tagged immediately after — v2 is now stable.
  2. The docs site rebuilds from main. docs/whats-new.md says "v2 is the stable line"; docs/index.md says "This documents v2, the current stable release line."
  3. A reader browsing the API reference opens the mcp.shared.dispatcher page (generated by gen_ref_pages.py, rendered by mkdocstrings) and reads: "The lifecycle surface is provisional; run() may change before v2 stable."
  4. 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"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 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 confirmationneeds 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-alphav2 — 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 confirmationneed 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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread docs/get-started/real-host.md
Comment thread AGENTS.md
Comment thread README.md
Comment thread docs/whats-new.md
Comment on lines +7 to +10
!!! 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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

  1. This PR merges; per its description, v2.0.0 is tagged immediately after — v2 is now stable.
  2. The docs site rebuilds from main: docs/whats-new.md says "v2 is the stable line", docs/index.md says "This documents v2, the current stable release line."
  3. A reader opens the API reference page for mcp.server.mcpserver (generated by gen_ref_pages.py, rendered by mkdocstrings) and reads under MCPServer.middleware: "the signature is expected to change before v2 is final".
  4. 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.

@maxisbey
maxisbey force-pushed the stable-flip branch 2 times, most recently from 2b9b1d5 to 534f3e0 Compare July 28, 2026 12:55
Comment thread docs/whats-new.md
Comment on lines +7 to +10
!!! 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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 Pre-existing (files untouched by this PR): the stable-release framing sweep didn't cover examples/examples/stories/middleware/README.md:34-36 still says MCPServer "has no public accessor" for middleware "(a MCPServer.middleware accessor is planned before beta)", which is now factually wrong since #3201 (c9c431b, an ancestor of this PR) added exactly that public property, and line 39 promises an outbound seam "before v2 final". The same "TODO(maxisbey): revisit before beta" phrasing remains in examples/stories/{streaming,legacy_routing,sse_polling,roots,sampling}/README.md — one grep-and-reword pass, fine as a follow-up after the tag.

Extended reasoning...

What this is. This PR is the repo-wide sweep that removes pre-release-era framing ("before beta", "before v2 final", version pins, caution banners) ahead of the v2.0.0 tag, but the sweep did not reach examples/. Six story READMEs still carry the old framing, and one of them makes a claim that is now factually false rather than merely stale in tense.

The factually wrong one. examples/stories/middleware/README.md:34-36 reads: "Server.middleware on mcp.server.lowlevel.Server is the one public hook; MCPServer has no public accessor for it yet (a MCPServer.middleware accessor is planned before beta)." But #3201 (commit c9c431b, Expose the middleware chain on MCPServer, already an ancestor of this PR's HEAD) added exactly that accessor — src/mcp/server/mcpserver/server.py:265 defines the public middleware property on MCPServer. So the README denies the existence of an API the stable release ships, and promises it for a phase ("before beta") that ended long ago. Line 39 of the same file says the middleware signature "gains an outbound seam before v2 final" — the same pre-stable-deadline phrasing this PR's sweep rewrote in the README, docs, and policies, which becomes a promise about a moment already passed the instant v2.0.0 is tagged after this PR merges.

The rest of the pattern. The identical TODO(maxisbey): revisit before beta marker remains in five sibling READMEs: examples/stories/streaming/README.md:58, examples/stories/legacy_routing/README.md:98, examples/stories/sse_polling/README.md:7, examples/stories/roots/README.md:6, and examples/stories/sampling/README.md:6. One root cause — the sweep's scope stopped at the README, docs/, and the policy files — so one grep pass (before beta, before v2 final) over examples/ fixes all of it.

Step-by-step proof for the middleware claim. (1) A reader lands on examples/stories/middleware/README.mddocs/client/identity-assertion.md and docs/advanced/apps.md link readers into examples/stories/, so this is a plausible path. (2) They read that MCPServer has no public middleware accessor and that one is "planned before beta". (3) They check the version: 2.0.0, stable — so they conclude either that the plan was dropped or that the API doesn't exist, and reach for the lowlevel Server instead. (4) In fact MCPServer(...).middleware exists and is public (server.py:265, shipped in the 2.0.0 wheel) — the README actively misdirected them away from a shipped API.

Why nothing catches it. These READMEs are not in the mkdocs nav, so the strict docs build never renders them; the sweep targeted version pins and /v2/ URLs, which these sentences contain neither of. Only a phrase-level grep over the whole tree finds them.

Not a duplicate. The three sweep-miss comments already on this PR cover docs/advanced/middleware.md:9, src/mcp/shared/dispatcher.py:253, and the MCPServer.middleware docstring at src/mcp/server/mcpserver/server.py:270-271. Fixing all three leaves examples/ untouched; none of their reasoning examined the stories READMEs.

Why pre-existing / non-blocking. All six files are outside this PR's diff, are GitHub-repo-visible only (not on the docs site, not in the wheel), and can be fixed any time after the tag — nothing breaks by merging. Filed here because this PR is the framing sweep and these are misses of the same class as the already-flagged ones; fold them in if convenient, or sweep them in a follow-up.

How to fix. In middleware/README.md, replace lines 34-36 with the current truth (e.g. "MCPServer exposes the chain via the public MCPServer.middleware property; the lowlevel Server.middleware list is the underlying hook") and reword line 39 to "may gain an outbound seam in a future release". In the other five, drop or reword the TODO(maxisbey): revisit before beta markers (e.g. "revisit in a future release", or just delete them).

"--frozen",
"--with",
"mcp[cli]==2.0.0rc1",
"mcp[cli]==2.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The docs' one remaining exact pin — the mcp[cli]==2.0.0 in this page's example of what mcp install writes — has no process to keep it current: the new RELEASE.md (#3179) states the stable line's docs "carry no version pin" and that routine stable releases need no pin-flip commit, and its grep step naming this file applies only during pre-release phases. From v2.0.1 on the published example will show the tool writing ==2.0.0 while it actually writes the installed version; a one-line fix either side closes the gap (genericize the pin, e.g. mcp[cli]==<installed version>, or add this line to RELEASE.md's stable-release checklist).

Extended reasoning...

What the bug is. This PR deliberately keeps exactly one literal version pin in the docs: the "mcp[cli]==2.0.0" at docs/get-started/real-host.md:72, inside the JSON block showing the entry mcp install writes into claude_desktop_config.json (the PR description: "the launch-command page keeps only the exact pin that mcp install itself writes"). The problem is not the value 2.0.0 — it is that no process now exists to keep it current. This PR's declared dependency #3179 (landed as 68ca87e) rewrote RELEASE.md, and its "Stable release from main" section (RELEASE.md:36-38) states: "The stable line's README and docs carry no version pin (pip install \"mcp[cli]\" installs the newest stable release), so a routine stable release needs no pin-flip commit." That premise is contradicted by this page's one pin, and the step that does grep for pins in docs/get-started/real-host.md (RELEASE.md:110-116) applies only "During a pre-release phase" — it even says "the stable phase drops the pins."

The code path / concrete trigger. src/mcp/cli/claude.py (_mcp_requirement) pins the generated config entry to the installed version: on 2.0.1 it writes mcp[cli]==2.0.1. The docs site rebuilds from main on every push (deploy-docs.yml), and per the new RELEASE.md a routine stable release is tag-only with no doc commit.

Step-by-step proof. (1) v2.0.1 ships; per RELEASE.md's stable-release section, no pin-flip commit is made — the releaser is explicitly told none is needed. (2) The docs rebuild from main; real-host.md still shows "mcp[cli]==2.0.0" under the heading "This is the entry it writes." (3) A user on 2.0.1 runs uv run mcp install server.py; their claude_desktop_config.json contains mcp[cli]==2.0.1, diverging from the page's example, and the gap widens by one version with every release. (4) Nothing catches it: the fenced JSON block carries no snippet-source marker, and the strict docs build validates links, not version strings.

Why this is a nit, not blocking. One verifier refutation argued this is intentional behavior with no concrete failure, and it is largely right about impact: the JSON block is an illustrative sample full of placeholders (Bookshop, /absolute/path/to/uv), the prose at lines 82 and 173 explicitly describes the pin as "an exact pin to the mcp version you have installed," and line 31 already carves out the exception ("...agree, apart from the exact version pin the tool adds") — so a reader whose config shows 2.0.3 is not misled into any broken state, and even copying ==2.0.0 verbatim installs a valid stable release. Where the refutation undersells it is the process gap: the intentional design decision was "keep the one pin the tool itself writes," but the companion RELEASE.md rewrite then asserted the docs carry no pin and built the stable-release checklist on that false premise. The two landed PRs are inconsistent with each other, and the result is guaranteed, unbounded, uncaught drift in the one doc example whose whole point is showing exactly what the tool generates. That's worth a one-line fix, not a merge block.

How to fix (one line either side). Either genericize the example so it cannot rot — e.g. "mcp[cli]==<installed version>" (the surrounding prose already explains the pin in exactly those terms) — or amend RELEASE.md's "Stable release from main" section to name docs/get-started/real-host.md:72 as the one pin to bump per release.

Not a duplicate. The cubic-dev-ai comment on this page (addressed in 43e7743) was about qualifying the Recap's pin promise for source builds; the earlier RELEASE.md finding targeted the old, pre-#3179 text that no longer exists. This finding is about the new RELEASE.md's pin-free claim versus the pin this PR keeps.

maxisbey added 2 commits July 28, 2026 13:23
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
@maxisbey
maxisbey merged commit 6f69a37 into main Jul 28, 2026
37 checks passed
@maxisbey
maxisbey deleted the stable-flip branch July 28, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant