From 2b0da5631a7cb460c17d60b8cc735626d5e1b3f8 Mon Sep 17 00:00:00 2001 From: Max <224885523+maxisbey@users.noreply.github.com> Date: Thu, 7 May 2026 17:32:30 +0100 Subject: [PATCH 1/5] build: pin PEP 517 build dependencies (#2547) --- pyproject.toml | 18 ++++++++++++++++++ uv.lock | 13 +++++++++++++ 2 files changed, 31 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index a5d2c3d80a..364b9add0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,24 @@ mcp = "mcp.cli:app [cli]" [tool.uv] default-groups = ["dev", "docs"] required-version = ">=0.9.5" +# PEP 517 build isolation fetches [build-system].requires (and transitives) at +# floating-latest with no hash check on every fresh sync; uv does not lock them +# (astral-sh/uv#5190). Pinning here narrows that to known-good versions. Covers +# the workspace builds (hatchling + uv-dynamic-versioning) and the legacy +# setuptools fallback used by the strict-no-cover git dep. +build-constraint-dependencies = [ + "hatchling==1.29.0", + "uv-dynamic-versioning==0.14.0", + "dunamai==1.26.1", + "jinja2==3.1.6", + "markupsafe==3.0.3", + "packaging==26.1", + "pathspec==1.0.4", + "pluggy==1.6.0", + "tomlkit==0.14.0", + "trove-classifiers==2026.1.14.14", + "setuptools==82.0.1", +] [dependency-groups] dev = [ diff --git a/uv.lock b/uv.lock index 705d014aa5..b396898b66 100644 --- a/uv.lock +++ b/uv.lock @@ -28,6 +28,19 @@ members = [ "mcp-sse-polling-demo", "mcp-structured-output-lowlevel", ] +build-constraints = [ + { name = "dunamai", specifier = "==1.26.1" }, + { name = "hatchling", specifier = "==1.29.0" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "markupsafe", specifier = "==3.0.3" }, + { name = "packaging", specifier = "==26.1" }, + { name = "pathspec", specifier = "==1.0.4" }, + { name = "pluggy", specifier = "==1.6.0" }, + { name = "setuptools", specifier = "==82.0.1" }, + { name = "tomlkit", specifier = "==0.14.0" }, + { name = "trove-classifiers", specifier = "==2026.1.14.14" }, + { name = "uv-dynamic-versioning", specifier = "==0.14.0" }, +] [[package]] name = "annotated-types" From bf3e0010b87a6a2535711500eb9590741b1d1940 Mon Sep 17 00:00:00 2001 From: Dayna Blackwell Date: Fri, 8 May 2026 06:28:36 -0700 Subject: [PATCH 2/5] fix: chain exceptions in get_prompt and read_resource handlers (#2542) --- src/mcp/server/mcpserver/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index be77705da6..b3471163b7 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -447,8 +447,8 @@ async def read_resource( context = Context(mcp_server=self) try: resource = await self._resource_manager.get_resource(uri, context) - except ValueError: - raise ResourceError(f"Unknown resource: {uri}") + except ValueError as exc: + raise ResourceError(f"Unknown resource: {uri}") from exc try: content = await resource.read() @@ -1109,4 +1109,4 @@ async def get_prompt( ) except Exception as e: logger.exception(f"Error getting prompt {name}") - raise ValueError(str(e)) + raise ValueError(str(e)) from e From 161834d4aee2633c42d3976c8f8751b6c4d947d5 Mon Sep 17 00:00:00 2001 From: Max <224885523+maxisbey@users.noreply.github.com> Date: Fri, 8 May 2026 17:42:44 +0100 Subject: [PATCH 3/5] refactor: import SSEError from httpx_sse public API (#2560) --- src/mcp/client/sse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index 193204a153..74e5ba8062 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -7,8 +7,7 @@ import anyio import httpx from anyio.abc import TaskStatus -from httpx_sse import aconnect_sse -from httpx_sse._exceptions import SSEError +from httpx_sse import SSEError, aconnect_sse from mcp import types from mcp.shared._context_streams import create_context_streams From f4753440dac8b2b6fa6407808e06c51258b78322 Mon Sep 17 00:00:00 2001 From: Max <224885523+maxisbey@users.noreply.github.com> Date: Mon, 18 May 2026 15:28:13 +0100 Subject: [PATCH 4/5] ci: deploy docs to py.sdk.modelcontextprotocol.io via Pages artifact (v1 at /, v2 at /v2/) (#2634) --- .github/workflows/deploy-docs.yml | 57 +++++++++++++++++++++ .github/workflows/publish-docs-manually.yml | 35 ------------- .github/workflows/publish-pypi.yml | 29 ----------- .gitignore | 1 + docs/index.md | 3 ++ mkdocs.yml | 2 +- pyproject.toml | 1 + scripts/build-docs.sh | 54 +++++++++++++++++++ 8 files changed, 117 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/deploy-docs.yml delete mode 100644 .github/workflows/publish-docs-manually.yml create mode 100755 scripts/build-docs.sh diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000000..d9362afd57 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,57 @@ +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/publish-docs-manually.yml b/.github/workflows/publish-docs-manually.yml deleted file mode 100644 index ee45ab5c8a..0000000000 --- a/.github/workflows/publish-docs-manually.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Publish Docs manually - -on: - workflow_dispatch: - -jobs: - docs-publish: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - - name: Install uv - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 - with: - enable-cache: true - version: 0.9.5 - - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - - run: uv sync --frozen --group docs - - run: uv run --frozen --no-sync mkdocs gh-deploy --force - env: - ENABLE_SOCIAL_CARDS: "true" diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index bfc3cc64e1..7ba11e86fa 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -51,32 +51,3 @@ jobs: - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 - - docs-publish: - runs-on: ubuntu-latest - needs: ["pypi-publish"] - permissions: - contents: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - - name: Install uv - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 - with: - enable-cache: true - version: 0.9.5 - - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - - run: uv sync --frozen --group docs - - run: uv run --frozen --no-sync mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 5ff4ce9771..3443adf7c8 100644 --- a/.gitignore +++ b/.gitignore @@ -143,6 +143,7 @@ venv.bak/ # mkdocs documentation /site +/.worktrees/ # mypy .mypy_cache/ diff --git a/docs/index.md b/docs/index.md index 436d1c8fcd..6a937da67f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,8 @@ # MCP Python SDK +!!! info "You are viewing the in-development v2 documentation" + For the current stable release, see the [v1.x documentation](https://py.sdk.modelcontextprotocol.io/). + 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. This Python SDK implements the full MCP specification, making it easy to: diff --git a/mkdocs.yml b/mkdocs.yml index 3a555785a7..e48c64242d 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/main/docs/ -site_url: https://modelcontextprotocol.github.io/python-sdk +site_url: https://py.sdk.modelcontextprotocol.io/v2/ # TODO(Marcelo): Add Anthropic copyright? # copyright: © Model Context Protocol 2025 to present diff --git a/pyproject.toml b/pyproject.toml index 364b9add0b..d88869da1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,6 +115,7 @@ bump = true [project.urls] Homepage = "https://modelcontextprotocol.io" +Documentation = "https://py.sdk.modelcontextprotocol.io/v2/" 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 new file mode 100755 index 0000000000..5a61309acf --- /dev/null +++ b/scripts/build-docs.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# Build combined v1 + v2 MkDocs 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/. +# +# Both branches are fetched fresh from origin, so the output is identical +# regardless of which branch triggered the workflow. This script is intended +# to run in CI; for local single-branch preview use `uv run mkdocs serve`. +# +# 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 + +rm -rf "${OUTPUT_DIR:?}"/* + +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" + uv sync --frozen --group docs + uv run --frozen --no-sync mkdocs build --site-dir "$dest" + ) +} + +build_branch v1.x "$V1_WORKTREE" "$OUTPUT_DIR" +build_branch main "$V2_WORKTREE" "$OUTPUT_DIR/v2" + +echo "=== Combined docs built at $OUTPUT_DIR ===" From e8e64842781c66b613872cf394de6e7d6f6925bf Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Wed, 20 May 2026 17:06:57 +0200 Subject: [PATCH 5/5] ci: add zizmor for GitHub Actions security analysis (#2648) --- .github/workflows/claude.yml | 3 ++- .github/workflows/comment-on-release.yml | 22 ++++++++++++----- .github/workflows/conformance.yml | 4 ++++ .github/workflows/deploy-docs.yml | 2 ++ .github/workflows/publish-pypi.yml | 7 +++++- .github/workflows/shared.yml | 6 +++++ .github/workflows/weekly-lockfile-update.yml | 2 ++ .github/workflows/zizmor.yml | 25 ++++++++++++++++++++ 8 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 59dac99dcb..18f5377cb6 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -30,12 +30,13 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 + persist-credentials: false - name: Run Claude Code id: claude uses: anthropics/claude-code-action@2f8ba26a219c06cfb0f468eef8d97055fa814f97 # v1.0.53 with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} # zizmor: ignore[secrets-outside-env] use_commit_signing: true additional_permissions: | actions: read diff --git a/.github/workflows/comment-on-release.yml b/.github/workflows/comment-on-release.yml index 15d6a1d26a..66f1fcc32a 100644 --- a/.github/workflows/comment-on-release.yml +++ b/.github/workflows/comment-on-release.yml @@ -16,13 +16,16 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 + persist-credentials: false - name: Get previous release id: previous_release uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + CURRENT_TAG: ${{ github.event.release.tag_name }} with: script: | - const currentTag = '${{ github.event.release.tag_name }}'; + const currentTag = process.env.CURRENT_TAG; // Get all releases const { data: releases } = await github.rest.repos.listReleases({ @@ -54,10 +57,13 @@ jobs: - name: Get merged PRs between releases id: get_prs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + CURRENT_TAG: ${{ github.event.release.tag_name }} + PREVIOUS_TAG_JSON: ${{ steps.previous_release.outputs.result }} with: script: | - const currentTag = '${{ github.event.release.tag_name }}'; - const previousTag = ${{ steps.previous_release.outputs.result }}; + const currentTag = process.env.CURRENT_TAG; + const previousTag = JSON.parse(process.env.PREVIOUS_TAG_JSON); if (!previousTag) { console.log('No previous release found, skipping'); @@ -104,11 +110,15 @@ jobs: - name: Comment on PRs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + PR_NUMBERS_JSON: ${{ steps.get_prs.outputs.result }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + RELEASE_URL: ${{ github.event.release.html_url }} with: script: | - const prNumbers = ${{ steps.get_prs.outputs.result }}; - const releaseTag = '${{ github.event.release.tag_name }}'; - const releaseUrl = '${{ github.event.release.html_url }}'; + const prNumbers = JSON.parse(process.env.PR_NUMBERS_JSON); + const releaseTag = process.env.RELEASE_TAG; + const releaseUrl = process.env.RELEASE_URL; const comment = `This pull request is included in [${releaseTag}](${releaseUrl})`; diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index d876da00b0..9c33d2936b 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -19,6 +19,8 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: enable-cache: true @@ -34,6 +36,8 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: enable-cache: true diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index d9362afd57..fcf7a6c1a7 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -34,6 +34,8 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 7ba11e86fa..c72061d12b 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -4,6 +4,9 @@ on: release: types: [published] +permissions: + contents: read + jobs: release-build: name: Build distribution @@ -11,11 +14,13 @@ jobs: needs: [checks] steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: - enable-cache: true + enable-cache: false version: 0.9.5 - name: Set up Python 3.12 diff --git a/.github/workflows/shared.yml b/.github/workflows/shared.yml index efb45c8898..5f115aef89 100644 --- a/.github/workflows/shared.yml +++ b/.github/workflows/shared.yml @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: @@ -57,6 +59,8 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 @@ -83,6 +87,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: diff --git a/.github/workflows/weekly-lockfile-update.yml b/.github/workflows/weekly-lockfile-update.yml index 5d79d06d52..c30c72991a 100644 --- a/.github/workflows/weekly-lockfile-update.yml +++ b/.github/workflows/weekly-lockfile-update.yml @@ -15,6 +15,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000000..83d3eb3817 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,25 @@ +name: GitHub Actions Security Analysis + +on: + push: + branches: ["main"] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + + permissions: + security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files. + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor 🌈 + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6