diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
deleted file mode 100644
index d9362afd57..0000000000
--- a/.github/workflows/deploy-docs.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-name: Deploy Docs
-
-on:
- push:
- branches:
- - main
- - v1.x
- paths:
- - docs/**
- - mkdocs.yml
- - src/mcp/**
- - scripts/build-docs.sh
- - pyproject.toml
- - uv.lock
- - .github/workflows/deploy-docs.yml
- workflow_dispatch:
-
-concurrency:
- group: deploy-docs
- cancel-in-progress: false
-
-jobs:
- deploy-docs:
- runs-on: ubuntu-latest
-
- permissions:
- contents: read
- pages: write
- id-token: write
-
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
-
- steps:
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
-
- - name: Install uv
- uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1
- with:
- enable-cache: true
- version: 0.9.5
-
- - name: Build combined docs (v1.x at /, main at /v2/)
- run: bash scripts/build-docs.sh site
-
- - name: Configure Pages
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
-
- - name: Upload Pages artifact
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
- with:
- path: site
-
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
diff --git a/.github/workflows/shared.yml b/.github/workflows/shared.yml
index 16ba250cde..468359fef4 100644
--- a/.github/workflows/shared.yml
+++ b/.github/workflows/shared.yml
@@ -76,3 +76,20 @@ jobs:
- name: Check doc snippets are up to date
run: uv run --frozen scripts/update_doc_snippets.py --check
+
+ # The published site is built and deployed from main, which builds this
+ # branch's docs (via scripts/docs/build.sh) under /v1/; this branch has no
+ # deploy workflow of its own. This job runs that same script so a change
+ # that breaks the v1 docs fails here rather than at main's next deploy.
+ docs:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+
+ - uses: astral-sh/setup-uv@v7
+ with:
+ enable-cache: true
+ version: 0.9.5
+
+ - name: Build docs
+ run: bash scripts/docs/build.sh
diff --git a/README.md b/README.md
index 9eaef01af8..325060dfb1 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,9 @@
-> **This documents v1.x, the stable release line of the MCP Python SDK. v2 is in alpha.**
+> **This documents v1.x, the maintenance line of the MCP Python SDK.** v2 is the current stable release: `pip install mcp` now installs 2.x. See the [v2 documentation](https://py.sdk.modelcontextprotocol.io/) and the [migration guide](https://py.sdk.modelcontextprotocol.io/migration/) for what changed and how to upgrade.
>
-> v2 pre-releases are published to PyPI as `2.0.0aN`. Installers never select a pre-release unless you opt in (for example `pip install mcp==2.0.0a1`), so v1.x users 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 v2 release lands.** See the [v2 documentation](https://github.com/modelcontextprotocol/python-sdk/blob/main/README.v2.md) and the [migration guide](https://github.com/modelcontextprotocol/python-sdk/blob/main/docs/migration.md) for what's changing.
->
-> v1.x remains recommended for production use. It is in maintenance mode and continues to receive critical bug fixes and security patches.
+> **Staying on v1.x?** Keep a `<2` upper bound on your requirement (for example `mcp>=1.28,<2`) so an unpinned resolve stays on the 1.x line. v1.x remains supported for existing deployments and continues to receive critical bug fixes and security patches; its documentation is at .
## Table of Contents
@@ -38,7 +36,7 @@
[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://modelcontextprotocol.github.io/python-sdk/
+[docs-url]: https://py.sdk.modelcontextprotocol.io/v1/
[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
@@ -69,13 +67,13 @@ If you haven't created a uv-managed project yet, create one:
Then add MCP to your project dependencies:
```bash
- uv add "mcp[cli]"
+ uv add "mcp[cli]<2"
```
Alternatively, for projects using pip for dependencies:
```bash
-pip install "mcp[cli]"
+pip install "mcp[cli]<2"
```
### Running the standalone MCP development tools
@@ -143,7 +141,7 @@ _Full example: [examples/snippets/servers/fastmcp_quickstart.py](https://github.
You can install this server in [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp) and interact with it right away. First, run the server:
```bash
-uv run --with mcp examples/snippets/servers/fastmcp_quickstart.py
+uv run --with "mcp<2" examples/snippets/servers/fastmcp_quickstart.py
```
Then add it to Claude Code:
@@ -177,8 +175,8 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you bui
- [Low-Level Server](docs/low-level-server.md) -- direct handler registration for advanced use cases
- [Protocol Features](docs/protocol.md) -- MCP primitives, server capabilities
- [Testing](docs/testing.md) -- in-memory transport testing with pytest
-- [API Reference](https://modelcontextprotocol.github.io/python-sdk/api/)
-- [Experimental Features (Tasks)](https://modelcontextprotocol.github.io/python-sdk/experimental/tasks/)
+- [API Reference](https://py.sdk.modelcontextprotocol.io/v1/api/)
+- [Experimental Features (Tasks)](https://py.sdk.modelcontextprotocol.io/v1/experimental/tasks/)
- [Model Context Protocol documentation](https://modelcontextprotocol.io)
- [Model Context Protocol specification](https://modelcontextprotocol.io/specification/latest)
- [Officially supported servers](https://github.com/modelcontextprotocol/servers)
diff --git a/docs/index.md b/docs/index.md
index 8462445a60..c44891c137 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,7 +1,9 @@
# MCP Python SDK
-!!! tip "Looking for the upcoming v2?"
- See the [v2 development documentation](https://py.sdk.modelcontextprotocol.io/v2/).
+!!! tip "You are viewing the v1.x maintenance-line documentation"
+ v2 is the current stable release: its documentation is at
+ . Staying on v1.x for now? Pin `mcp<2`
+ (for example `mcp>=1.28,<2`) so an unpinned install doesn't move you to 2.x.
The **Model Context Protocol (MCP)** allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction.
@@ -48,7 +50,7 @@ if __name__ == "__main__":
Run the server:
```bash
-uv run --with mcp server.py
+uv run --with "mcp<2" server.py
```
Then open the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) and connect to `http://localhost:8000/mcp`:
@@ -74,5 +76,5 @@ Full API documentation is available in the [API Reference](api.md).
## llms.txt
Reading with an LLM? This documentation is also published in the [llms.txt](https://llmstxt.org/) format:
-[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.
+[llms.txt](https://py.sdk.modelcontextprotocol.io/v1/llms.txt) is an index of the pages, and
+[llms-full.txt](https://py.sdk.modelcontextprotocol.io/v1/llms-full.txt) contains every page in a single file.
diff --git a/docs/installation.md b/docs/installation.md
index 6e20706a84..2352a1132f 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -1,16 +1,18 @@
# Installation
-The Python SDK is available on PyPI as [`mcp`](https://pypi.org/project/mcp/) so installation is as simple as:
+The Python SDK is available on PyPI as [`mcp`](https://pypi.org/project/mcp/). These docs describe the **v1.x maintenance line**;
+the `<2` bound keeps you on it now that `pip install mcp` selects the 2.x stable release by default (the
+[v2 documentation](https://py.sdk.modelcontextprotocol.io/) covers that line):
=== "pip"
```bash
- pip install mcp
+ pip install "mcp<2"
```
=== "uv"
```bash
- uv add mcp
+ uv add "mcp<2"
```
The following dependencies are automatically installed:
diff --git a/mkdocs.yml b/mkdocs.yml
index f19a0ee3ee..684c4a1232 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -5,7 +5,7 @@ strict: true
repo_name: modelcontextprotocol/python-sdk
repo_url: https://github.com/modelcontextprotocol/python-sdk
edit_uri: edit/v1.x/docs/
-site_url: https://py.sdk.modelcontextprotocol.io/
+site_url: https://py.sdk.modelcontextprotocol.io/v1/
# TODO(Marcelo): Add Anthropic copyright?
# copyright: © Model Context Protocol 2025 to present
diff --git a/pyproject.toml b/pyproject.toml
index 9a4cf051ef..6bee7f4923 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -91,7 +91,7 @@ bump = true
[project.urls]
Homepage = "https://modelcontextprotocol.io"
-Documentation = "https://py.sdk.modelcontextprotocol.io/"
+Documentation = "https://py.sdk.modelcontextprotocol.io/v1/"
Repository = "https://github.com/modelcontextprotocol/python-sdk"
Issues = "https://github.com/modelcontextprotocol/python-sdk/issues"
diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh
deleted file mode 100755
index 8286786091..0000000000
--- a/scripts/build-docs.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env bash
-#
-# Build combined v1 + v2 documentation for GitHub Pages.
-#
-# v1 docs (from the v1.x branch) are placed at the site root.
-# v2 docs (from main) are placed under /v2/.
-#
-# The two lines use different toolchains: v1.x still builds with MkDocs, while
-# main builds with Zensical (which needs a pre-build step to materialise the API
-# reference and a post-build step for llms.txt — see scripts/docs/). Each branch
-# is fetched fresh from origin and built with its own synced `docs` group, so
-# the output is identical regardless of which branch triggered the workflow.
-# This script is intended to run in CI; for a local v2 preview use
-# `scripts/serve-docs.sh`.
-#
-# Usage:
-# scripts/build-docs.sh [output-dir]
-#
-# Default output directory: site
-#
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
-OUTPUT_DIR="$(cd "$REPO_ROOT" && mkdir -p "${1:-site}" && cd "${1:-site}" && pwd)"
-V1_WORKTREE="$REPO_ROOT/.worktrees/v1-docs"
-V2_WORKTREE="$REPO_ROOT/.worktrees/v2-docs"
-
-cleanup() {
- cd "$REPO_ROOT"
- git worktree remove --force "$V1_WORKTREE" 2>/dev/null || true
- git worktree remove --force "$V2_WORKTREE" 2>/dev/null || true
- rmdir "$REPO_ROOT/.worktrees" 2>/dev/null || true
-}
-trap cleanup EXIT
-
-# Build the checked-out worktree into its local `site/`, picking the toolchain
-# from the branch's own files rather than hard-coding it here: a branch that
-# ships the Zensical build recipe (scripts/docs/build.sh) builds with it,
-# otherwise it falls back to MkDocs. This keeps the combined build correct
-# regardless of which branch triggered it. Zensical requires site_dir to live
-# within the project root, so both paths build to the local `site/` and let
-# the caller copy it to its destination.
-build_site() {
- if [[ -f scripts/docs/build.sh ]]; then
- bash scripts/docs/build.sh
- else
- uv sync --frozen --group docs
- NO_MKDOCS_2_WARNING=1 uv run --frozen --no-sync mkdocs build --site-dir site
- fi
-}
-
-build_branch() {
- local branch="$1" worktree="$2" dest="$3"
-
- echo "=== Building docs for ${branch} ==="
- git fetch origin "$branch"
- git worktree remove --force "$worktree" 2>/dev/null || true
- rm -rf "$worktree"
- git worktree add --detach "$worktree" "origin/${branch}"
-
- (
- cd "$worktree"
- rm -rf site
- build_site
- mkdir -p "$dest"
- cp -a site/. "$dest/"
- )
-}
-
-rm -rf "${OUTPUT_DIR:?}"/*
-
-build_branch v1.x "$V1_WORKTREE" "$OUTPUT_DIR"
-build_branch main "$V2_WORKTREE" "$OUTPUT_DIR/v2"
-
-echo "=== Combined docs built at $OUTPUT_DIR ==="
diff --git a/scripts/docs/build.sh b/scripts/docs/build.sh
new file mode 100755
index 0000000000..6c5ab88f62
--- /dev/null
+++ b/scripts/docs/build.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+#
+# Build the v1.x documentation into ./site.
+#
+# This is the single build recipe for this branch's docs. The combined site
+# is assembled and deployed from main, which fetches this branch and runs this
+# script to build it under /v1/ (main's scripts/build-docs.sh picks
+# scripts/docs/build.sh from each branch it builds). The `docs` CI job on
+# this branch runs the same script, so what CI checks is what gets published.
+#
+# Usage:
+# scripts/docs/build.sh
+#
+set -euo pipefail
+
+cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+
+uv sync --frozen --group docs
+NO_MKDOCS_2_WARNING=1 uv run --frozen --no-sync mkdocs build --site-dir site