Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/v2-feedback.yaml
Original file line number Diff line number Diff line change
@@ -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"]

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.


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
Expand Down
20 changes: 10 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
Comment thread
claude[bot] marked this conversation as resolved.
- 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
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment thread
claude[bot] marked this conversation as resolved.
>
> 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).
Comment on lines +16 to +21

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.


## 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/).
Comment thread
claude[bot] marked this conversation as resolved.

## What is MCP?

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
17 changes: 11 additions & 6 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

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

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

Expand Down
20 changes: 7 additions & 13 deletions docs/get-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
29 changes: 12 additions & 17 deletions docs/get-started/real-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",

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.

"mcp",
"run",
"/absolute/path/to/server.py"
Expand All @@ -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.

Expand All @@ -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.
Expand All @@ -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"]
}
}
}
Expand All @@ -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"]
}
}
}
Expand All @@ -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.
Expand All @@ -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.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
* **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.

Expand Down
Loading
Loading