Skip to content

Publish versioning, roadmap, and dependency policies for v2 - #3215

Open
maxisbey wants to merge 1 commit into
mainfrom
docs/tier1-governance-files
Open

Publish versioning, roadmap, and dependency policies for v2#3215
maxisbey wants to merge 1 commit into
mainfrom
docs/tier1-governance-files

Conversation

@maxisbey

Copy link
Copy Markdown
Contributor

The versioning policy, roadmap, and dependency policy that the SEP-1730 tier assessment relies on were written to v1.x (#2084) after the branches diverged and never came forward, so the default branch has been without them since v2 became the default. Rather than port them — v1.x's roadmap links the TypeScript project board and dates v2 as "alpha ~mid-March", and its versioning doc describes a release model that no longer exists — this writes them for v2 as it actually stands.

Motivation and Context

  • docs/versioning.md ("Versioning and support policy") — SemVer semantics in PEP 440 syntax, mcp / mcp-types lockstep, the public-API boundary (__all__ plus documented paths; message text is not API), what is and is not a breaking change, provisional/experimental as the two carve-outs, and the two deprecation channels (@deprecated for SDK APIs, MCPDeprecationWarning for spec-retired features). Links SECURITY.md for the supported-lines table.
  • docs/roadmap.md — organized by spec revision: the 2026-07-28 project board (Types Rework #42) and its one open item (Capabilities API + server/discover handler #2896), then the not-yet-implemented set, which is exactly the conformance expected-failures baseline — the tasks extension (SEP-2663, Implement SEP-2663: Tasks Extension #2806), DPoP (SEP-1932), and the jwt-bearer grant — plus the v1.x maintenance stance.
  • DEPENDENCY_POLICY.md (root, since docs/dependency-policy.md is the only docs path the tier check reads and it isn't a natural nav page) — floors-only requirements, the mcp-types exact pin explained, why floors don't move for CVEs (the AGENTS.md stance, with the uvicorn and mitigate CVE-2025-62727 for starlette #1552 reasoning linked), the lowest-direct CI leg that keeps floors honest, and the Dependabot uv/actions setup.
  • CONTRIBUTING.md — an "Issue Triage" section (two-business-day first label, the shared taxonomy, P0 = resolved within 7 days) — the SLA text SEP-1730 measures, which the rewrite had dropped — and a pointer to the dependency policy. RELEASE.md's bump recipe links it too.

Two policy statements in the versioning page are worth an explicit look, since they are commitments rather than descriptions:

  1. Dropping a Python version after its upstream EOL ships in a minor release with a release-note callout, not a major. (v1.x's VERSIONING.md said the opposite; the ecosystem norm is this way, and tying 2.x to Python 3.10 until 3.0 seemed the wrong trade.)
  2. The exact text of log lines, warnings, and exception messages is declared not part of the API — types and documented raise conditions are.

How Has This Been Tested?

scripts/docs/build.sh (strict Zensical build + cross-reference check) green with the two new pages in the nav; markdownlint clean.

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

Additional context

Deliberately not in this PR: the feature-documentation gaps against the tier audit's 48-feature list (a docs PR of their own), the v1.x roadmap's stale content, and a CHANGELOG.md (GitHub Releases remain the changelog, as on every other SDK).

AI Disclaimer

The versioning policy, roadmap, and dependency policy that the SEP-1730
tier assessment relies on were written to the v1.x branch and never
came forward to main. Add them for v2 as they actually stand: a
docs page stating the version scheme, the public-API boundary, what
counts as breaking, and the two deprecation channels; a roadmap page
pointing at the 2026-07-28 project board and the extension work
still open; and a root DEPENDENCY_POLICY.md describing the floors-only
requirement style and the automated-update setup. CONTRIBUTING.md gets
the issue-triage section (label taxonomy, priority commitments) that
its rewrite dropped, and RELEASE.md links the new dependency policy
from the bump recipe.

No-Verification-Needed: doc-only change
@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation preview

Preview https://pr-3215.mcp-python-docs.pages.dev
Deployment https://9e52b348.mcp-python-docs.pages.dev
Commit e90d907
Triggered by @maxisbey
Updated 2026-07-29 16:24:38 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.

3 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/roadmap.md">

<violation number="1" location="docs/roadmap.md:28">
P3: Conformance does not run on every push: the workflow only has a push trigger for `main` (PR updates run via `pull_request`). Scope this statement to pushes to `main` and pull-request updates so the roadmap does not overstate CI coverage.</violation>
</file>

<file name="docs/versioning.md">

<violation number="1" location="docs/versioning.md:13">
P3: Requirements that explicitly name a prerelease (for example `mcp>=2.1.0b1`) do not need to be exact pins, and resolvers may also use a prerelease when no final release satisfies the requirement. This “never” statement can cause users to misdiagnose why a prerelease was installed; describe the normal exclusion rule and its full exceptions instead.</violation>

<violation number="2" location="docs/versioning.md:57">
P2: Modern-protocol users can read this as a guarantee that retired features still work, but roots/sampling fail after warning and `ping` is already absent on 2026-07-28 connections. Qualify this by negotiated protocol revision so it matches the deprecated-features behavior.</violation>
</file>

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

Re-trigger cubic

Comment thread docs/versioning.md

**SDK API deprecations** — a name or parameter this SDK is retiring. The API keeps working, marked with [`typing_extensions.deprecated`](https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.deprecated), so static type checkers flag every call site and Python emits a `DeprecationWarning` at runtime. A deprecated API survives at least one minor release with its warning in place, and is removed only in a major version: something deprecated during 2.x is not removed before 3.0.

**Protocol deprecations** — a feature the MCP specification has retired (for example the SEP-2577 set in the 2026-07-28 revision). These keep working through the specification's deprecation window and warn with `MCPDeprecationWarning`, a `UserWarning` subclass, so the warning is visible by default rather than hidden the way `DeprecationWarning` is outside `__main__`. **[Deprecated features](deprecated.md)** lists every one, its replacement, and how to silence the warning when you genuinely serve older clients.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Modern-protocol users can read this as a guarantee that retired features still work, but roots/sampling fail after warning and ping is already absent on 2026-07-28 connections. Qualify this by negotiated protocol revision so it matches the deprecated-features behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/versioning.md, line 57:

<comment>Modern-protocol users can read this as a guarantee that retired features still work, but roots/sampling fail after warning and `ping` is already absent on 2026-07-28 connections. Qualify this by negotiated protocol revision so it matches the deprecated-features behavior.</comment>

<file context>
@@ -0,0 +1,77 @@
+
+**SDK API deprecations** — a name or parameter this SDK is retiring. The API keeps working, marked with [`typing_extensions.deprecated`](https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.deprecated), so static type checkers flag every call site and Python emits a `DeprecationWarning` at runtime. A deprecated API survives at least one minor release with its warning in place, and is removed only in a major version: something deprecated during 2.x is not removed before 3.0.
+
+**Protocol deprecations** — a feature the MCP specification has retired (for example the SEP-2577 set in the 2026-07-28 revision). These keep working through the specification's deprecation window and warn with `MCPDeprecationWarning`, a `UserWarning` subclass, so the warning is visible by default rather than hidden the way `DeprecationWarning` is outside `__main__`. **[Deprecated features](deprecated.md)** lists every one, its replacement, and how to silence the warning when you genuinely serve older clients.
+
+## Supported release lines
</file context>

Comment thread docs/roadmap.md

## Continuous work

* **Conformance** — every push runs the [conformance suite](https://github.com/modelcontextprotocol/conformance) as both server and client, against the released revisions and against the 2026-07-28 wire specifically; adopting each new harness release and reconciling its baseline is routine.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Conformance does not run on every push: the workflow only has a push trigger for main (PR updates run via pull_request). Scope this statement to pushes to main and pull-request updates so the roadmap does not overstate CI coverage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/roadmap.md, line 28:

<comment>Conformance does not run on every push: the workflow only has a push trigger for `main` (PR updates run via `pull_request`). Scope this statement to pushes to `main` and pull-request updates so the roadmap does not overstate CI coverage.</comment>

<file context>
@@ -0,0 +1,34 @@
+
+## Continuous work
+
+* **Conformance** — every push runs the [conformance suite](https://github.com/modelcontextprotocol/conformance) as both server and client, against the released revisions and against the 2026-07-28 wire specifically; adopting each new harness release and reconciling its baseline is routine.
+* **The next specification revision** — draft-only wire changes are tried behind the draft protocol version before they are final, and land in a release once the revision ships; the SDK targets releasing support alongside each new specification version.
+* **Everything else** — the [issue tracker](https://github.com/modelcontextprotocol/python-sdk/issues) is the source of truth for bugs and smaller features; `P0`–`P3` labels carry priority.
</file context>

Comment thread docs/versioning.md
* **`X` (minor)** — new functionality and every non-breaking change.
* **`Y` (patch)** — bug fixes only.
* **The leading `2` (major)** — the only place a breaking change to the public API can land.
* **Pre-releases** are cut from `main` as `2.X.YaN` (alpha), `2.X.YbN` (beta), and `2.X.YrcN` (release candidate). Installers never select a pre-release unless you ask for one, by exact pin or `--pre`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Requirements that explicitly name a prerelease (for example mcp>=2.1.0b1) do not need to be exact pins, and resolvers may also use a prerelease when no final release satisfies the requirement. This “never” statement can cause users to misdiagnose why a prerelease was installed; describe the normal exclusion rule and its full exceptions instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/versioning.md, line 13:

<comment>Requirements that explicitly name a prerelease (for example `mcp>=2.1.0b1`) do not need to be exact pins, and resolvers may also use a prerelease when no final release satisfies the requirement. This “never” statement can cause users to misdiagnose why a prerelease was installed; describe the normal exclusion rule and its full exceptions instead.</comment>

<file context>
@@ -0,0 +1,77 @@
+* **`X` (minor)** — new functionality and every non-breaking change.
+* **`Y` (patch)** — bug fixes only.
+* **The leading `2` (major)** — the only place a breaking change to the public API can land.
+* **Pre-releases** are cut from `main` as `2.X.YaN` (alpha), `2.X.YbN` (beta), and `2.X.YrcN` (release candidate). Installers never select a pre-release unless you ask for one, by exact pin or `--pre`.
+
+`mcp` and its wire-types package [`mcp-types`](https://pypi.org/project/mcp-types/) release in lockstep at the same version: each `mcp` release requires exactly the matching `mcp-types` (`mcp-types==2.X.Y`).
</file context>

@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 three inline findings, two candidate inconsistencies in the new CONTRIBUTING.md triage section were examined and ruled out: the status-label list omitting needs maintainer action (that label is discussed earlier in the file as a not-ready marker, not part of the shared status taxonomy the section documents), and the taxonomy differing from the labels the issue templates auto-apply (templates apply intake labels; the section describes the maintainer-applied taxonomy at triage time).

Extended reasoning...

This run found three nit-level wording/precision issues in the new policy docs, all posted as inline comments. Beyond those, finder agents raised two candidate contradictions in the CONTRIBUTING.md Issue Triage section, and I cross-checked them against the file and .github/ISSUE_TEMPLATE/: the templates auto-apply intake labels (question, v2, feature request, need confirmation) while the new section documents the shared maintainer-applied triage taxonomy, and the needs-maintainer-action mention is pre-existing text about work-readiness rather than the triage status set — so neither is a real inconsistency introduced by this PR. Recording this so a later pass does not re-explore the same candidates; it is informational only, not a correctness guarantee.

Comment thread DEPENDENCY_POLICY.md

Every declared floor is exercised: CI runs the full test suite both against the locked dependency set and against a `lowest-direct` resolution, on every supported Python version, so a floor that has quietly become false fails the build rather than a user's install.

Raising a floor within the same major version of a dependency is a minor-release change and is called out in the release notes; see the [versioning policy](https://py.sdk.modelcontextprotocol.io/versioning/). Adding a new required runtime dependency is a maintainer decision made in an issue before the pull request, not a side effect of a feature.

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 two new policy docs answer the same question differently: DEPENDENCY_POLICY.md scopes the minor-release permission to floor raises "within the same major version of a dependency", while docs/versioning.md's non-breaking list allows any minimum-version raise (no cross-major carve-out) — and each page defers to the other as the authority. Either drop the qualifier here or add the cross-major case explicitly to versioning.md's breaking-change list, so a reader can tell whether e.g. moving to pydantic>=3 may land in a 2.X.0 release.

Extended reasoning...

The inconsistency. This PR adds two policy documents that both make a normative statement about when a dependency floor raise may ship in a minor release, and they don't agree on scope. docs/versioning.md (line 46, under "These do not [count as breaking], and can ship in a minor release") says unconditionally: "raising a dependency's minimum version when the SDK needs newer functionality … called out in the release notes (the dependency policy covers the first)". DEPENDENCY_POLICY.md line 18 instead says: "Raising a floor within the same major version of a dependency is a minor-release change and is called out in the release notes; see the versioning policy."

Why the qualifier matters. In a document whose stated purpose (per the PR description) is to publish commitments rather than descriptions — read by the SEP-1730 tier assessment — a scoped qualifier reads as deliberate. "Within the same major version" plainly implies that raising a floor across a dependency's major version is a different, larger kind of change. That reading is reinforced by the same file already treating dependency majors specially ("no upper bounds unless a dependency's next major version is known to break the SDK"). versioning.md, by contrast, admits any needed-functionality floor raise into a minor with no such carve-out.

Why a reader can't resolve it. The deferral is circular: versioning.md says "the dependency policy covers the first", and DEPENDENCY_POLICY.md says "see the versioning policy". Neither page claims precedence, so neither text settles which rule governs the cross-major case.

Concrete walk-through. Suppose the SDK decides it needs a feature that first ships in pydantic 3.0, so the floor moves from pydantic>=2.12.0 to pydantic>=3. (1) A maintainer following versioning.md's non-breaking list ships this in mcp 2.(X+1).0 — the text permits any minimum-version raise motivated by needed functionality. (2) A downstream user who read DEPENDENCY_POLICY.md's "within the same major version" qualifier concluded such a raise would wait for mcp 3.0 and kept pydantic<3 pinned in their environment. (3) An unpinned mcp upgrade to the new minor makes their environment unresolvable, and each party can point at one of the two pages to justify their expectation. The whole point of these pages is to prevent exactly that dispute.

Strictly speaking the two statements are logically compatible — asserting the same-major case is minor doesn't assert the cross-major case is major; it's implicature, not contradiction. But either the qualifier is intentional (and versioning.md's unconditional wording is then wrong) or it isn't (and the qualifier is misleading scope-narrowing). One of the two sentences needs an edit either way.

Fix. Pick one: (a) if cross-major floor raises are meant to be minor-eligible, drop "within the same major version" from DEPENDENCY_POLICY.md line 18; or (b) if they're meant to wait for the next mcp major, add that case explicitly to versioning.md's breaking-change list (e.g. "raising a dependency floor across that dependency's major version"). Docs-only, one-sentence change in files this PR introduces — nothing breaks on merge, hence not blocking.

Comment thread RELEASE.md
Comment on lines +5 to +6
[`DEPENDENCY_POLICY.md`](DEPENDENCY_POLICY.md) says *when* a bound should
move; this is the mechanics.

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 new sentence promotes this recipe into the published dependency-policy chain, but step 2 (uv lock --resolution lowest-direct) would rewrite the committed uv.lock down to floor versions — the committed lock is a normal/highest resolution refreshed by Dependabot, exactly as this PR's own DEPENDENCY_POLICY.md "Automated updates" section describes, and lowest-direct belongs only to the CI leg that never touches the lock. The wrong command pre-dates this PR, but since this PR canonizes the recipe as "the mechanics" of the policy, step 2 should be fixed here to plain uv lock (or uv lock --upgrade-package <package> per AGENTS.md).

Extended reasoning...

The bug. The added sentence in RELEASE.md (lines 5–6) — "DEPENDENCY_POLICY.md says when a bound should move; this is the mechanics." — declares the "Bumping Dependencies" recipe below it to be the official mechanics of the new dependency policy. But step 2 of that recipe (line 11) says: Upgrade lock with uv lock --resolution lowest-direct. That command re-resolves every direct dependency of the workspace at its lowest allowed version and writes the result to the committed uv.lock — and since pyproject.toml sets default-groups = ["dev", "docs"], it floors the dev tooling too.\n\nWhy this is wrong. The committed uv.lock is demonstrably a normal/highest resolution, not lowest-direct: pydantic is locked at 2.12.5 against a floor of >=2.12.0, jsonschema at 4.25.1 against >=4.20.0, ruff at 0.12.12 against >=0.8.5. The uv.lock file records no resolution-mode, so running step 2 as written invalidates and re-resolves the entire lock down to floors. The lowest-direct strategy lives only in the CI matrix leg (.github/workflows/shared.yml:66, install-flags: "--upgrade --resolution lowest-direct"), which runs uv sync at test time and never writes the committed lock.\n\nThe contradiction is internal to this PR. The new DEPENDENCY_POLICY.md "Automated updates" section says Dependabot PRs "refresh the versions the SDK is developed and tested against (uv.lock)" — i.e. the lock tracks fresh versions. And AGENTS.md's existing recipe for the same operation is plain uv lock (or uv lock --upgrade-package <package>). Step 2 contradicts both, and the added pointer sentence is what elevates it from a stale line in RELEASE.md to the canonical companion of the published policy.\n\nConcrete walk-through. A maintainer raises the httpx floor per DEPENDENCY_POLICY.md, then follows RELEASE.md's mechanics literally: (1) edit the floor under [tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies; (2) run uv lock --resolution lowest-direct. uv re-resolves the whole workspace at lowest-direct: pydantic drops 2.12.5 → 2.12.0, jsonschema 4.25.1 → 4.20.0, ruff 0.12.12 → 0.8.5, pytest 8.4.2 → 8.4.0, and so on across the lock. The committed diff downgrades the entire lockfile; every dev environment and the CI "locked" matrix leg silently runs the oldest allowed versions, collapsing the locked leg into a near-duplicate of the lowest-direct leg and defeating the two-leg design the new policy document advertises. The next Dependabot refresh would churn it all back.\n\nWhy nothing catches it. No tooling checks that uv.lock was produced with the default resolution — the lock is internally consistent either way, uv run --frozen accepts it, and CI goes green (floors are supposed to pass the suite; that is the point of the lowest-direct leg). Only a human noticing a mass-downgrade diff would stop it.\n\nFix. In this same PR, change step 2 to uv lock (or uv lock --upgrade-package <package>, matching AGENTS.md). The wrong command itself pre-dates the PR — nothing breaks at merge time and this is docs-only — but since the PR edits this exact section to give the recipe official-policy status, the one-line correction belongs here.

Comment thread docs/versioning.md
* **`X` (minor)** — new functionality and every non-breaking change.
* **`Y` (patch)** — bug fixes only.
* **The leading `2` (major)** — the only place a breaking change to the public API can land.
* **Pre-releases** are cut from `main` as `2.X.YaN` (alpha), `2.X.YbN` (beta), and `2.X.YrcN` (release candidate). Installers never select a pre-release unless you ask for one, by exact pin or `--pre`.

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 bullet says installers select a pre-release only "by exact pin or --pre", but PEP 440 (as implemented by pip and uv) has a third opt-in route: any specifier that names a pre-release version (e.g. mcp>=2.1.0b1) enables pre-release selection for that requirement, so a later resolve can pick a newer alpha over the newest stable. Suggest mirroring RELEASE.md's complete wording — "an exact pin, a specifier that names a pre-release version, or --pre" — so the categorical "never" holds.

Extended reasoning...

What the bug is. The new pre-release bullet in docs/versioning.md (line 13) states: "Installers never select a pre-release unless you ask for one, by exact pin or --pre." The enumeration of opt-in routes is incomplete, which makes the categorical "never" false. Under PEP 440's pre-release handling — implemented by both pip and uv — there are three ways a requirement opts into pre-releases: an exact pin on a pre-release version, the --pre flag, and any version specifier that itself names a pre-release version. The page lists only the first two.\n\nHow it manifests — concrete walkthrough. Suppose during a beta phase a user follows the pinned install instructions loosely and writes mcp>=2.1.0b1 in their requirements (a plausible thing to do when the docs/README pin 2.1.0b1, per RELEASE.md's own pre-release-phase process). Later, when 2.4.0 is the newest stable and 2.5.0a1 has been published from main:\n\n1. The resolver sees the specifier >=2.1.0b1, which names a pre-release version.\n2. Per PEP 440, that specifier enables pre-release candidates for this requirement — no exact pin, no --pre flag.\n3. Candidate versions include 2.5.0a1, which satisfies >=2.1.0b1 and is the highest version.\n4. The user's fresh pip install / uv sync resolves to the alpha 2.5.0a1 instead of stable 2.4.0 — exactly the surprise this sentence promises cannot happen.\n\nWhy the surrounding text doesn't prevent it. Nothing else on the page qualifies the claim, and this is a policy page whose entire purpose is stating exact guarantees — readers are meant to rely on the sentence as written. Worse, the repo is internally inconsistent: the pre-existing RELEASE.md ("Pre-releases from main" section, visible in this PR's diff context) states the rule completely and correctly: "installers only select a pre-release when it is requested explicitly (an exact pin, a specifier that names a pre-release version, or --pre)." The new user-facing page understates the maintainer doc it parallels.\n\nImpact. Docs-only imprecision — nothing breaks at merge, no code is affected. But a user who trusts the sentence and carries a >=<beta> specifier out of a beta phase will unexpectedly land on future alphas, and the page they'd consult to understand why tells them it can't happen.\n\nFix. One clause: change "by exact pin or --pre" to mirror RELEASE.md, e.g. "by exact pin, a specifier that names a pre-release version, or --pre." That makes the two documents consistent and the "never" accurate.\n\nAll three verifiers independently confirmed the PEP 440 behavior and the RELEASE.md inconsistency; none refuted. Severity is nit: this PR is a docs PR where factual precision is the substance under review, but the fix is a single clause and merging as-is causes no operational failure.

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