Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ Issues labeled `needs confirmation` or `needs maintainer action` are **not** rea

Before starting, comment on the issue so we can assign it to you. This prevents duplicate effort.

## Issue Triage

Every new issue gets a first look from a maintainer within two business days. That first look is the *triage*: it means labeling the issue and deciding whether it is valid and actionable, not fixing it.

The labels follow the shared [MCP SDK taxonomy](https://modelcontextprotocol.io/community/sdk-tiers#issue-triage-labels): one **type** (`bug`, `enhancement`, `question`), one **status** (`needs confirmation`, `needs repro`, `ready for work`, `good first issue`, `help wanted`), and — once actionable — one **priority**:

| Label | Meaning | Commitment |
|-------|---------|------------|
| `P0` | Critical: core functionality failures (connections, message exchange, tools/resources/prompts) or a High/Critical-severity security issue | resolved within 7 days |
| `P1` | Significant bug affecting many users | next release |
| `P2` | Moderate issue or valuable feature request | as capacity allows |
| `P3` | Nice-to-have or rare edge case | opportunistic |

Security reports do not belong in the issue tracker; [SECURITY.md](SECURITY.md) has the private channel.

## Development Setup

1. Make sure you have Python 3.10+ installed
Expand Down Expand Up @@ -125,6 +140,7 @@ pre-commit run --all-files
- Follow PEP 8 style guidelines
- Add type hints to all functions
- Include docstrings for public APIs
- Changing a dependency's version bound or adding a runtime dependency follows the [Dependency Policy](DEPENDENCY_POLICY.md)

## Pull Requests

Expand Down
26 changes: 26 additions & 0 deletions DEPENDENCY_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Dependency Policy

`mcp` is a library that lives inside other people's environments, so its dependency requirements are chosen to constrain your resolver as little as possible while still describing what the SDK actually needs.

## How requirements are declared

* **Floors, not pins.** Every runtime dependency is a `>=` lower bound, set to the oldest version that provides what the SDK uses. There are no upper bounds unless a dependency's next major version is known to break the SDK.
* **The one exception is `mcp-types`.** The wire-types package is developed and released with `mcp` in lockstep, so `mcp` requires exactly its own version (`mcp-types==<same version>`). It is not an independent constraint on your environment; it is the other half of the SDK.
* **Environment markers instead of parallel packages** — Python-version and platform differences (`python_version`, `sys_platform`) are expressed as markers on the requirement, so one wheel serves every supported environment.
* **Optional features are extras.** Anything only some users need lives behind an extra (`mcp[cli]`, `mcp[rich]`) rather than in the base requirement set.

## When a floor moves

A minimum version is raised only when the SDK starts relying on functionality, a fix, or an API that first appeared in that version. It is not raised because a dependency published a security advisory. The `>=` bound already lets — and expects — you to run the newest release your other constraints allow, so a higher floor would only shrink the set of environments the SDK installs into without changing what any correctly-updated environment resolves to. The SDK also does not add code to work around a vulnerability in a dependency; the fix belongs upstream and in your lockfile. ([Background](https://github.com/Kludex/uvicorn/discussions/2643) on this stance from another library that adopted it, and [python-sdk#1552](https://github.com/modelcontextprotocol/python-sdk/issues/1552).)

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.

Check warning on line 18 in DEPENDENCY_POLICY.md

View check run for this annotation

Claude / Claude Code Review

Policy docs disagree on cross-major dependency floor raises

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 py

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.


## Automated updates

[Dependabot](https://github.com/modelcontextprotocol/python-sdk/blob/main/.github/dependabot.yml) opens monthly, grouped pull requests for the `uv` lockfile and for GitHub Actions, with a 14-day cooldown on newly published versions. These refresh the versions the SDK is developed and tested against (`uv.lock`); they never change the requirements published to PyPI, which move only under the rules above.

## Security in the SDK itself

Vulnerabilities in the SDK's own code — as opposed to its dependencies — follow the reporting process and response commitments in [SECURITY.md](https://github.com/modelcontextprotocol/python-sdk/blob/main/SECURITY.md).
3 changes: 3 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Bumping Dependencies

[`DEPENDENCY_POLICY.md`](DEPENDENCY_POLICY.md) says *when* a bound should
move; this is the mechanics.

Check warning on line 6 in RELEASE.md

View check run for this annotation

Claude / Claude Code Review

Canonized dependency-bump recipe tells maintainers to rewrite uv.lock to floors

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 re
Comment on lines +5 to +6

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.


1. Change the dependency version in `pyproject.toml`. The root `mcp` project's
runtime dependencies are dynamic and live under
`[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`.
Expand Down
34 changes: 34 additions & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Roadmap

The SDK's job is to implement the MCP specification, so its roadmap is organized by specification revision: one GitHub project board per revision, each item an issue or pull request you can follow. This page names the board that is current, what remains open on it, and the maintenance stance for the previous major.

## The 2026-07-28 revision — shipped, with follow-ups

v2 implements the [2026-07-28 specification](https://modelcontextprotocol.io/specification/2026-07-28) (and negotiates back to every earlier revision — see [Protocol versions](protocol-versions.md)); **[What's new in v2](whats-new.md)** is the tour of what that meant for the SDK.

* Board: **[python-sdk · 2026-07-28 spec](https://github.com/orgs/modelcontextprotocol/projects/42)**, tracking issue [#2891](https://github.com/modelcontextprotocol/python-sdk/issues/2891).
* Cross-SDK view: [2026-07-28 Spec Implementation](https://github.com/orgs/modelcontextprotocol/projects/41) tracks the same revision across all official SDKs.

Open on that board:

* **Capabilities API and the `server/discover` handler** — the last core item still in progress ([#2896](https://github.com/modelcontextprotocol/python-sdk/issues/2896)).

## Extensions and optional client auth not yet implemented

The 2026-07-28 revision moved some functionality out of the core protocol into named extensions, and defines client-side auth mechanisms an SDK may support. The ones this SDK does not implement yet are exactly the entries in the conformance suite's expected-failures baseline, [`.github/actions/conformance/expected-failures.yml`](https://github.com/modelcontextprotocol/python-sdk/blob/main/.github/actions/conformance/expected-failures.yml) — that file is grouped by SEP and each entry is removed as the corresponding work lands, so it is the live burn-down list:

* **Tasks extension** (`io.modelcontextprotocol/tasks`, [SEP-2663](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/seps/2663-tasks-extension.md)) — deferred at 2.0 because the 2026-07-28 design is wire-incompatible with the earlier in-core Tasks; tracked in [#2806](https://github.com/modelcontextprotocol/python-sdk/issues/2806).
* **DPoP-bound access tokens** ([SEP-1932](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1932)) in the OAuth client.
* **The workload-identity `jwt-bearer` grant** in the OAuth client.

None of these gates conformance today — extension scenarios are informational in the tier scoring — but each is a real gap for anyone who needs the feature, and they are the current queue.

## 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>

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

## The previous major

`v1.x` is a maintenance line: critical bug fixes and security fixes only, no new features. Its documentation stays available at [/v1/](https://py.sdk.modelcontextprotocol.io/v1/), the support terms are in [Versioning and support policy](versioning.md#supported-release-lines), and the path off it is the **[Migration Guide](migration.md)**.
77 changes: 77 additions & 0 deletions docs/versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Versioning and support policy

This page states what a version number of the `mcp` package promises you: which changes can arrive in a minor release, which are held for the next major, how deprecations are announced, and how long each release line is supported.

## The version number

Releases follow [Semantic Versioning](https://semver.org/) semantics, written in [PEP 440](https://peps.python.org/pep-0440/) syntax:

* **`2.X.Y`** — the version comes from the git tag; there is no version field to edit.
* **`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`.

Check warning on line 13 in docs/versioning.md

View check run for this annotation

Claude / Claude Code Review

versioning.md understates when installers select pre-releases

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"

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>

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.


`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`).

## What the public API is

The compatibility promise covers the public API:

* every name exported by `mcp` (its `__all__`) and by `mcp_types`,
* the import paths, classes, functions, and parameters documented on this site and in the [API Reference](api/mcp/index.md),
* documented behavior of those APIs.

It does not cover names beginning with an underscore, modules and attributes that appear nowhere in the documentation, or the exact text of log lines, warnings, and exception messages (their *type* and the documented conditions that raise them are covered; their wording is not). Depending on one of those is depending on an implementation detail that may change in any release.

Two labels mark APIs that sit outside the promise while they settle:

* **Provisional** — shipped and supported, but the signature or semantics may still change in a minor release. The middleware chain is the current example, and its documentation says so.
* **Experimental** — behind an explicit opt-in and expected to change; treat it as a preview.

## What counts as a breaking change

These wait for the next major version:

* removing or renaming a public name,
* changing a signature so that a call that worked stops working (a removed or reordered parameter, a newly required argument, a narrowed accepted type),
* changing a return type, a raised exception type, or documented behavior in a way existing callers would notice,
* removing a documented import path, extra, or CLI command.

These do not, and can ship in a minor release:

* new functions, parameters with defaults, classes, fields, and enum members,
* changes to provisional or experimental APIs,
* new deprecation warnings, and the eventual removal of a protocol feature the specification has retired (see [Deprecations](#deprecations)),
* raising a dependency's minimum version when the SDK needs newer functionality, or dropping a Python version that upstream has ended support for — both called out in the release notes (the [dependency policy](https://github.com/modelcontextprotocol/python-sdk/blob/main/DEPENDENCY_POLICY.md) covers the first),
* bug fixes, including fixes that make the SDK match documented or specified behavior it should have had all along.

When a fix is arguably both a bug fix and a behavior change, the deciding question is whether reasonable code written against the *documented* behavior breaks. If it does, the change is breaking.

## Deprecations

There are two kinds, warned differently on purpose.

**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>


## Supported release lines

Two lines are maintained, and only the newest release of a line receives fixes:

| Line | Branch | Receives |
| --- | --- | --- |
| 2.x — current stable | `main` | bug fixes, security fixes, new features |
| 1.x — maintenance | [`v1.x`](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x) | critical bug fixes and security fixes |

Older 1.x releases and all pre-releases are unsupported. The security-specific version of this table, and how to report a vulnerability, is in [SECURITY.md](https://github.com/modelcontextprotocol/python-sdk/blob/main/SECURITY.md). Still on 1.x? Its documentation is at [/v1/](https://py.sdk.modelcontextprotocol.io/v1/), and a `<2` upper bound on your `mcp` requirement keeps an unpinned resolve on that line until you migrate.

Python versions are supported from the version in the package's `requires-python` up to the newest CPython release the test suite runs against; support for a Python version ends only after that version's upstream end-of-life.

## Where changes are announced

* **Release notes** — every release publishes curated notes on [GitHub Releases](https://github.com/modelcontextprotocol/python-sdk/releases): highlights, anything known-incomplete, and a full change list. Pre-releases say what changed since the previous pre-release.
* **The migration guide** — every breaking change between majors is documented in **[Migration Guide](migration.md)** with before-and-after code; a change is not merged for a major release without its entry.
* **The `breaking change` label** — pull requests that make a breaking change carry it, so the set is queryable ahead of a major release.
* **Deprecation warnings** — as above, one release of warning at minimum before an SDK API is removed.
3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ nav:
- MCP Apps: advanced/apps.md
- Troubleshooting: troubleshooting.md
- Migration Guide: migration.md
- About:
- Versioning and support policy: versioning.md
- Roadmap: roadmap.md
- API Reference: api/

theme:
Expand Down
Loading