Present v2 as the stable release across the README, docs, and policies (#3178)
Conformance Tests / server-conformance (push) Has been cancelled
Conformance Tests / client-conformance (push) Has been cancelled
Deploy Docs / deploy-docs (push) Has been cancelled
GitHub Actions Security Analysis / zizmor (push) Has been cancelled
CI / checks (push) Failing after 1s
CI / all-green (push) Has been cancelled

This commit is contained in:
Max
2026-07-28 14:31:36 +01:00
committed by GitHub
parent 78e6fbb7e4
commit 6f69a3758e
10 changed files with 73 additions and 87 deletions
+3 -3
View File
@@ -1,15 +1,15 @@
name: v2 feedback
description: Bugs, API friction, or docs gaps in v2 of the SDK
title: "[v2] "
labels: ["v2-alpha"]
labels: ["v2"]
body:
- type: markdown
attributes:
value: |
Thanks for trying v2. Anything that broke, surprised you, or slowed you down is useful — API feedback is explicitly welcome while v2 is in pre-release.
Anything in v2 that broke, surprised you, or slowed you down is useful, including API friction and docs gaps.
Docs: https://py.sdk.modelcontextprotocol.io/v2/ · Migration from v1: https://py.sdk.modelcontextprotocol.io/v2/migration/
Docs: https://py.sdk.modelcontextprotocol.io/ · Migration from v1: https://py.sdk.modelcontextprotocol.io/migration/
- type: textarea
id: what
+10 -10
View File
@@ -2,16 +2,16 @@
## Branching Model
<!-- TODO: drop this section once v2 ships and main becomes the stable line -->
- `main` is currently the V2 rework.
- Breaking changes are expected here — removing or replacing an API must be
intentional. Adding a replacement API or `@deprecated` shim must likewise be
a deliberate design choice, not bolted on for free.
- Breaking changes (including those softened by a backwards-compatibility
shim) must be documented in `docs/migration.md`.
- `v1.x` is the release branch for the current stable line. Backport PRs target
this branch and use a `[v1.x]` title prefix.
- `main` is the current stable line (v2); releases are cut from it (see
`RELEASE.md`).
- Removing or replacing an API must be intentional, and what shipped in 2.x
is public surface. Adding a replacement API or `@deprecated` shim is
likewise a deliberate design choice, not bolted on for free.
- Changes that break code written against v1 (including those softened by a
backwards-compatibility shim) must be documented in `docs/migration.md`.
- `v1.x` is the maintenance branch for the previous major. Backport PRs
target it and use a `[v1.x]` title prefix; only critical bug fixes and
security fixes land there.
- `README.md` documents v2. The v1 README lives on the `v1.x` branch.
## Package Management
+3 -3
View File
@@ -76,11 +76,11 @@ uv tool install pre-commit --with pre-commit-uv --force-reinstall
| Change Type | Target Branch | Example |
|-------------|---------------|---------|
| New features, breaking changes | `main` | New APIs, refactors |
| New features and fixes for v2 | `main` | New APIs, refactors |
| Security fixes for v1 | `v1.x` | Critical patches |
| Bug fixes for v1 | `v1.x` | Non-breaking fixes |
| Critical bug fixes for v1 | `v1.x` | Backports of severe bugs |
> **Note:** `main` is the v2 development branch. Breaking changes are welcome on `main`. The `v1.x` branch receives only security and critical bug fixes.
> **Note:** `main` is the current stable line (v2). The `v1.x` branch is the previous major's maintenance line and receives only security and critical bug fixes.
2. Create a new branch from your chosen base branch
+10 -10
View File
@@ -13,18 +13,18 @@
</div>
> [!CAUTION]
> **This README documents v2 of the MCP Python SDK, currently a release candidate (`2.0.0rc1`); the stable v2 release is planned for 2026-07-28.** Do not use v2 in production yet. Pre-releases are published to PyPI as `2.0.0aN` / `2.0.0bN` / `2.0.0rcN`, and **a pre-release may still contain breaking changes from the previous one**. Pin an exact version and expect to update your code when you bump the pin.
> [!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).
## Documentation
**The documentation lives at <https://py.sdk.modelcontextprotocol.io/v2/>.**
**The documentation lives at <https://py.sdk.modelcontextprotocol.io/>.**
It has a [Get started guide](https://py.sdk.modelcontextprotocol.io/v2/get-started/), [What's new in v2](https://py.sdk.modelcontextprotocol.io/v2/whats-new/), the [API reference](https://py.sdk.modelcontextprotocol.io/v2/api/mcp/), and the [migration guide](https://py.sdk.modelcontextprotocol.io/v2/migration/).
It has a [Get started guide](https://py.sdk.modelcontextprotocol.io/get-started/), [What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/), the [API reference](https://py.sdk.modelcontextprotocol.io/api/mcp/), and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/).
## What is MCP?
@@ -41,10 +41,10 @@ Python 3.10+.
## Installation
```bash
uv add "mcp[cli]==2.0.0rc1" # or: pip install "mcp[cli]==2.0.0rc1"
uv add "mcp[cli]" # or: pip install "mcp[cli]"
```
The pin matters while v2 is in pre-release: an unpinned install resolves to the latest stable v1.x, which this README does not describe. Check [PyPI](https://pypi.org/project/mcp/#history) for the newest pre-release, and use `uv run --with "mcp==2.0.0rc1"` for one-off commands.
The `cli` extra adds the `mcp` command-line tool (`mcp dev`, `mcp run`, `mcp install`) on top of the SDK; install plain `mcp` if you don't need it. For one-off commands, `uv run --with "mcp[cli]" mcp ...` works without a project.
## A server in 15 lines
@@ -82,7 +82,7 @@ Call `add` with `a=1`, `b=2` and you get `3` back.
Notice what you did **not** write: no JSON Schema (`a: int, b: int` _is_ the schema), no request parsing, no validation code, no protocol handling. Two type-hinted Python functions and a docstring.
[Get started](https://py.sdk.modelcontextprotocol.io/v2/get-started/) takes it from here.
[Get started](https://py.sdk.modelcontextprotocol.io/get-started/) takes it from here.
## A client in 10 lines
@@ -122,7 +122,7 @@ This project is licensed under the MIT License. See the [LICENSE](https://github
[python-badge]: https://img.shields.io/pypi/pyversions/mcp.svg
[python-url]: https://www.python.org/downloads/
[docs-badge]: https://img.shields.io/badge/docs-python--sdk-blue.svg
[docs-url]: https://py.sdk.modelcontextprotocol.io/v2/
[docs-url]: https://py.sdk.modelcontextprotocol.io/
[protocol-badge]: https://img.shields.io/badge/protocol-modelcontextprotocol.io-blue.svg
[protocol-url]: https://modelcontextprotocol.io
[spec-badge]: https://img.shields.io/badge/spec-spec.modelcontextprotocol.io-blue.svg
+10 -5
View File
@@ -4,12 +4,17 @@ Thank you for helping keep the Model Context Protocol and its ecosystem secure.
## Supported Versions
Security fixes are released for the most recent stable (v1.x) release line.
| Version | Line | Support |
| ------------------------------------ | ----------------------- | ---------------------------------------- |
| 2.x (newest release) | current stable (`main`) | bug fixes, security fixes, new features |
| 1.28.x (`v1.x` branch) | maintenance | critical bug fixes and security fixes |
| < 1.28, and all pre-release versions | unsupported | upgrade to the newest 1.28.x or to 2.x |
v2 pre-releases (`2.0.0aN`, …) are development snapshots: fixes land only in
the newest pre-release, and already-published pre-releases are not patched. If
you are testing the v2 line, track the latest pre-release; for production use,
stay on the latest stable release.
Only the newest release of a supported line receives fixes, so reproduce against
it before reporting. If your project depends on `mcp` and is not yet ready for
2.x, constrain to `mcp>=1.28,<2` and follow the
[migration guide](https://py.sdk.modelcontextprotocol.io/migration/) when you
migrate.
## Reporting Security Issues
+7 -13
View File
@@ -2,30 +2,24 @@
The Python SDK is on PyPI as [`mcp`](https://pypi.org/project/mcp/). It requires **Python 3.10+**.
These docs describe **v2**, currently a release candidate, so the version pin is not optional yet:
These docs describe **v2**, the current stable release line:
=== "uv"
```bash
uv add "mcp[cli]==2.0.0rc1"
uv add "mcp[cli]"
```
=== "pip"
```bash
pip install "mcp[cli]==2.0.0rc1"
pip install "mcp[cli]"
```
!!! warning "Why the pin"
Installers never select a pre-release unless you name one, so an unpinned `uv add "mcp[cli]"`
gives you the latest **v1.x** release, which these docs do not describe. Check the
[release history](https://pypi.org/project/mcp/#history) for the newest pre-release before you copy
the line above.
The same applies to one-off commands: `uv run --with "mcp==2.0.0rc1" ...`, not `uv run --with mcp ...`.
If your *package* depends on `mcp`, add a `<2` upper bound (for example `mcp>=1.27,<2`) before
the stable v2 lands so the major version bump doesn't surprise you.
!!! note "Coming from v1?"
v2 is a major version with breaking changes; the **[Migration Guide](../migration.md)**
covers every one. If your *package* depends on `mcp` and isn't ready to migrate, keep a
`<2` upper bound (for example `mcp>=1.28,<2`) so an unpinned resolve stays on the 1.x line.
## What gets installed
+12 -17
View File
@@ -23,17 +23,12 @@ That is the last line of Python on this page. From here down it is all host conf
Every host below gets the same command:
```bash
uv run --with "mcp[cli]==2.0.0rc1" mcp run /absolute/path/to/server.py
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
One command for all of them because `uv run --with` resolves the pinned SDK into a fresh environment on the spot: it works from any directory, needs no project and no virtual environment to activate, and always gets the exact `mcp` version these docs describe. That matters here more than anywhere else, because a host launches your server from *its* working directory with a near-empty environment, not from your shell.
One command for all of them because `uv run --with` resolves the SDK into a fresh environment on the spot: it works from any directory and needs no project and no virtual environment to activate. That matters here more than anywhere else, because a host launches your server from *its* working directory with a near-empty environment, not from your shell.
It is also the command `mcp install` writes into Claude Desktop's config for you (below), so what you type by hand and what the tool generates agree.
!!! warning "The version pin is not optional"
v2 of this SDK is a release candidate, and installers never select a pre-release unless you name one. An
unpinned `--with "mcp[cli]"` gives you the latest **v1.x**, which these docs do not describe.
Use the exact pin from **[Installation](installation.md)**.
It is also the command `mcp install` writes into Claude Desktop's config for you (below), so what you type by hand and what the tool generates agree, apart from the exact version pin the tool adds.
!!! tip "If a host can't find `uv`"
A host spawns your server with a minimal `PATH`, and `uv` may not be on it. Replace the bare
@@ -74,7 +69,7 @@ There is nothing to be mystified by. This is the entry it writes:
"run",
"--frozen",
"--with",
"mcp[cli]==2.0.0rc1",
"mcp[cli]==2.0.0",
"mcp",
"run",
"/absolute/path/to/server.py"
@@ -84,12 +79,12 @@ There is nothing to be mystified by. This is the entry it writes:
}
```
That's the launch command from the section above with two additions: the absolute path to `uv`, and `--frozen` so `uv` never rewrites a lockfile it happens to be near. It lands in `claude_desktop_config.json`, which lives at:
That's the launch command from the section above with three additions: the absolute path to `uv`, `--frozen` so `uv` never rewrites a lockfile it happens to be near, and an exact pin to the `mcp` version you have installed. It lands in `claude_desktop_config.json`, which lives at:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
You can write that file by hand. `mcp install` exists so you don't make the two classic mistakes (a relative path, a missing version pin) while doing it.
You can write that file by hand. `mcp install` exists so you don't make the classic mistake (a relative path) while doing it.
Fully quit Claude Desktop (not just its window) and reopen it.
@@ -107,7 +102,7 @@ Fully quit Claude Desktop (not just its window) and reopen it.
There is no file to edit. Register the server with the `claude` CLI; everything after `--` is the launch command.
```bash
claude mcp add bookshop -- uv run --with "mcp[cli]==2.0.0rc1" mcp run /absolute/path/to/server.py
claude mcp add bookshop -- uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Run `/mcp` inside a Claude Code session to confirm `bookshop` is connected and its tools are listed.
@@ -121,7 +116,7 @@ Create `.cursor/mcp.json` in your project root.
"mcpServers": {
"bookshop": {
"command": "uv",
"args": ["run", "--with", "mcp[cli]==2.0.0rc1", "mcp", "run", "/absolute/path/to/server.py"]
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
}
}
}
@@ -139,7 +134,7 @@ Create `.vscode/mcp.json` in your project root.
"bookshop": {
"type": "stdio",
"command": "uv",
"args": ["run", "--with", "mcp[cli]==2.0.0rc1", "mcp", "run", "/absolute/path/to/server.py"]
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
}
}
}
@@ -156,7 +151,7 @@ Two differences from Cursor's file, and they are the only two: the wrapper key i
Before you touch any host config, run the launch command yourself:
```bash
uv run --with "mcp[cli]==2.0.0rc1" mcp run /absolute/path/to/server.py
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Nothing prints, and it doesn't return. That silence is correct: a stdio server is waiting for a host to speak first on stdin (`Ctrl-C` to stop it). A traceback or an immediate exit is the real bug, and now you can read it instead of guessing at it through a host.
@@ -174,8 +169,8 @@ For anything past those three, **[Troubleshooting](../troubleshooting.md)** is t
## Recap
* A **host** (Claude Desktop, an IDE) runs an MCP client that launches your server as a child process over stdio. Connecting means giving it one launch command.
* That command is `uv run --with "mcp[cli]==2.0.0rc1" mcp run /absolute/path/to/server.py`: version-pinned, no venv to activate, works from any directory. The pin is mandatory while v2 is pre-release.
* **Claude Desktop** is the one host `mcp install` configures for you. It writes that same command (plus the absolute path to `uv`) into `claude_desktop_config.json`, so you never have to.
* That command is `uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py`: no venv to activate, works from any directory.
* **Claude Desktop** is the one host `mcp install` configures for you. It writes that same command (plus the absolute path to `uv`, `--frozen`, and an exact pin to the version you have installed) into `claude_desktop_config.json`, so you never have to.
* **Claude Code** is `claude mcp add bookshop -- <launch command>`. **Cursor** is `.cursor/mcp.json` under `mcpServers`. **VS Code** is `.vscode/mcp.json` under `servers`, each entry with a `type`.
* Absolute paths everywhere, restart the host after editing its config, and never let anything but the SDK write to stdout.
+9 -14
View File
@@ -1,9 +1,9 @@
# MCP Python SDK
!!! info "You are viewing the v2 documentation"
The v1.x documentation is at [/v1/](https://py.sdk.modelcontextprotocol.io/v1/).
New to v2, or coming from v1? **[What's new in v2](whats-new.md)** is the five-minute tour of what changed.
Trying v2? [Tell us what you find](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml) — it is the most useful thing you can do for the SDK right now.
!!! info "This documents v2, the current stable release line"
New to v2, or coming from v1? **[What's new in v2](whats-new.md)** is the five-minute tour of what changed, and the **[Migration Guide](migration.md)** covers every breaking change.
Still on v1.x? Its documentation lives at the [v1.x docs](https://py.sdk.modelcontextprotocol.io/v1/).
Something rough or confusing? [Tell us](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml).
The **Model Context Protocol (MCP)** lets applications provide context to LLMs in a standardized way, separating the concern of *providing* context from the LLM interaction itself.
@@ -22,22 +22,17 @@ Python 3.10+.
=== "uv"
```bash
uv add "mcp[cli]==2.0.0rc1"
uv add "mcp[cli]"
```
=== "pip"
```bash
pip install "mcp[cli]==2.0.0rc1"
pip install "mcp[cli]"
```
The `[cli]` extra gives you the `mcp` command; you'll want it for development.
!!! warning "Pin the version while v2 is pre-release"
Installers never select a pre-release unless you name one, so an unpinned `uv add "mcp[cli]"`
gives you the latest **v1.x** release, which this documentation does not describe. Check
[PyPI](https://pypi.org/project/mcp/#history) for the newest pre-release before you copy the line
above. See [Installation](get-started/installation.md) for the details.
See [Installation](get-started/installation.md) for what each dependency is for.
## Example
@@ -98,5 +93,5 @@ You wrote two Python functions with type hints and a docstring. The SDK does the
* Migrating from v1? Start with the **[Migration Guide](migration.md)**.
* Hunting for an exact signature? The **[API Reference](api/mcp/index.md)** is generated from the source.
* Reading with an LLM? This documentation is also published in the [llms.txt](https://llmstxt.org/) format:
[llms.txt](https://py.sdk.modelcontextprotocol.io/v2/llms.txt) is an index of the pages, and
[llms-full.txt](https://py.sdk.modelcontextprotocol.io/v2/llms-full.txt) contains every page in a single file.
[llms.txt](https://py.sdk.modelcontextprotocol.io/llms.txt) is an index of the pages, and
[llms-full.txt](https://py.sdk.modelcontextprotocol.io/llms-full.txt) contains every page in a single file.
+2 -2
View File
@@ -186,8 +186,8 @@ in `httpx2`; build an `ssl.SSLContext` and configure it instead.
Both commands run your server through a fresh `uv run --with ...` environment. In v1 the
`mcp` requirement in that command was unpinned, so the spawned environment resolved to the
newest stable release rather than the version you had installed; with a v2 pre-release
installed, `mcp dev server.py` built a v1 environment that could not import a v2 server.
newest stable release rather than the version you had installed; while v2 was in
pre-release, `mcp dev server.py` built a v1 environment that could not import a v2 server.
Both commands now pin the requirement to the version you are running
(`mcp==<installed version>`). Source builds and other unpublished versions, which have
nothing on PyPI to pin to, keep the unpinned form.
+7 -10
View File
@@ -4,13 +4,10 @@ Two things happened at once in v2. The **SDK was rebuilt**: a new engine under b
This page is the tour of both halves, one section per headline, each ending in the page that owns the topic. It is not the porting manual. That is the **[Migration Guide](migration.md)**: every breaking change, with before and after code.
!!! note "v2 is a release candidate"
`pip install mcp` still installs v1.x: you opt into v2 with an exact version pin, and the
API can still move before the stable release, which lands alongside the spec release.
**[Installation](get-started/installation.md)** has the copy-paste install line and the
pinning rules. And if anything in v2 breaks, surprises, or slows you down,
[tell us](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml):
before the stable release, that is the most useful thing you can send us.
!!! 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).
## The SDK: v1 to v2
@@ -191,13 +188,13 @@ That file is the pitch in one place: one server, one `Resolve`-backed tool, and
### Change notifications become one stream
At 2026-07-28 the standalone HTTP GET stream and `resources/subscribe` are replaced by `subscriptions/listen`: the client opens one long-lived stream and names the notification kinds it wants. `MCPServer` serves it out of the box; you publish with `await ctx.notify_resource_updated(uri)` (and `notify_tools_changed()`, and so on), a middleware can refuse a listen request per caller, and multi-replica deployments plug in a shared `SubscriptionBus`. On the client (since `2.0.0b2`), `async with client.listen(...)` opens the stream: the filter goes in as keyword arguments, typed change events come back, and `sub.honored` is the subset the server agreed to deliver.
At 2026-07-28 the standalone HTTP GET stream and `resources/subscribe` are replaced by `subscriptions/listen`: the client opens one long-lived stream and names the notification kinds it wants. `MCPServer` serves it out of the box; you publish with `await ctx.notify_resource_updated(uri)` (and `notify_tools_changed()`, and so on), a middleware can refuse a listen request per caller, and multi-replica deployments plug in a shared `SubscriptionBus`. On the client, `async with client.listen(...)` opens the stream: the filter goes in as keyword arguments, typed change events come back, and `sub.honored` is the subset the server agreed to deliver.
**[Subscriptions](handlers/subscriptions.md)** covers publishing and serving, **[its Clients twin](client/subscriptions.md)** the watching end, and **[Deploy & scale](run/deploy.md)** the bus.
### The rest, quickly
* **Identity is optional, per-message metadata.** The request-side `clientInfo` `_meta` key is optional (the required pair is `protocolVersion` + `clientCapabilities`), and `serverInfo` moved out of the `server/discover` result body: servers stamp it into every 2026-era result's `_meta` instead (since `2.0.0rc1`; [spec #3002](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/3002)). The SDK always stamps; `client.server_info` is `None` when a server does not identify itself (for example, a middleware stripped the key). **[The low-level Server](advanced/low-level-server.md)** shows the stamp on the wire.
* **Identity is optional, per-message metadata.** The request-side `clientInfo` `_meta` key is optional (the required pair is `protocolVersion` + `clientCapabilities`), and `serverInfo` moved out of the `server/discover` result body: servers stamp it into every 2026-era result's `_meta` instead ([spec #3002](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/3002)). The SDK always stamps; `client.server_info` is `None` when a server does not identify itself (for example, a middleware stripped the key). **[The low-level Server](advanced/low-level-server.md)** shows the stamp on the wire.
* **Requests are routable without parsing bodies.** Modern HTTP requests carry `Mcp-Method` (and, for the three tool-ish calls, `Mcp-Name`); a tool input-schema property annotated with `x-mcp-header` is mirrored into an `Mcp-Param-*` header and cross-checked by the server ([SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)). Gateways and rate limiters can route on headers alone; the **[Migration Guide](migration.md#servers-validate-mcp-param-headers-against-the-request-body-sep-2243)** has the rules.
* **Results carry cache hints.** List and read results declare `ttlMs` and `cacheScope` ([SEP-2549](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549)); you set them per method with `cache_hints=`, and `Client` honors them with a built-in response cache. A server that sends no hints (every pre-2026 server) sees identical, uncached traffic. **[Caching hints](client/caching.md)**.
* **Extensions are first class.** Servers and clients declare optional capability bundles under reverse-DNS identifiers ([SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)); the built-in `Apps` extension (MCP Apps) is the reference. **[Extensions](advanced/extensions.md)** and **[MCP Apps](advanced/apps.md)**.
@@ -208,5 +205,5 @@ At 2026-07-28 the standalone HTTP GET stream and `resources/subscribe` are repla
## Upgrading from v1?
* The **[Migration Guide](migration.md)** is the complete, exact list of what to change; this page was the why.
* **v1.x is not going anywhere.** It stays the stable line, with critical fixes and security patches, and nothing about the 2026-07-28 spec release breaks it. If you publish a library that depends on `mcp`, add an upper bound (for example `mcp>=1.27,<2`) so stable v2 does not surprise your users.
* **v1.x is not going anywhere.** It moves to maintenance, keeps getting critical fixes and security patches, and nothing about the 2026-07-28 spec release breaks it; its docs live at [/v1/](https://py.sdk.modelcontextprotocol.io/v1/). If you publish a library that depends on `mcp` and are not ready to migrate, keep an upper bound (for example `mcp>=1.28,<2`) so an unpinned resolve stays on 1.x.
* Something rough, confusing, or broken? **[File v2 feedback](https://github.com/modelcontextprotocol/python-sdk/issues/new?template=v2-feedback.yaml)**; it all gets read.