Skip to content

Commit 14ed0cb

Browse files
committed
merge: update stdio EOF drain fix onto latest main
Rebuilds the PR on current main while preserving newly-added runner cache-hint handling. Keeps the stdio-only EOF drain behavior and regression coverage for accepted handler responses and transport-builder rejection responses.
2 parents f4a8fc9 + 9bdc03d commit 14ed0cb

190 files changed

Lines changed: 9877 additions & 1953 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/bin/bash
2+
# Run a client conformance suite, re-verifying unexpected failures solo.
3+
# Concurrent suite runs on a 2-vCPU runner can push scenarios with real-time
4+
# waits past tolerance; solo, a real failure fails again while a contention
5+
# artifact passes. Failures that only reproduce under concurrency are excused.
6+
set -uo pipefail
7+
8+
: "${CONFORMANCE_PKG:?set CONFORMANCE_PKG (pinned in .github/workflows/conformance.yml)}"
9+
# One attempt: a solo failure on the quiet runner disproves the contention
10+
# hypothesis; a second try would be the blind retry this script avoids.
11+
SOLO_ATTEMPTS="${CONFORMANCE_SOLO_ATTEMPTS:-1}"
12+
13+
# Relative args resolve from the repo root; same contract as run-server.sh.
14+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15+
cd "$SCRIPT_DIR/../../.." || exit 1
16+
17+
log="$(mktemp)"
18+
trap 'rm -f "$log"' EXIT
19+
20+
npx --yes "$CONFORMANCE_PKG" client "$@" 2>&1 | tee "$log"
21+
rc=${PIPESTATUS[0]}
22+
if [ "$rc" -eq 0 ]; then
23+
exit 0
24+
fi
25+
26+
plain="$(sed 's/\x1b\[[0-9;]*m//g' "$log")"
27+
28+
# If the harness's summary wording changes, the list comes up empty and the
29+
# original exit code passes through - never a false green.
30+
mapfile -t scenarios < <(
31+
printf '%s\n' "$plain" |
32+
sed -n '/^Unexpected failures (not in baseline):$/,/^$/p' |
33+
sed -n 's/^ ✗ //p'
34+
)
35+
if [ "${#scenarios[@]}" -eq 0 ]; then
36+
exit "$rc"
37+
fi
38+
for scenario in "${scenarios[@]}"; do
39+
if ! [[ "$scenario" =~ ^[A-Za-z0-9/_-]+$ ]]; then
40+
echo "Extracted unexpected-failure name '${scenario}' does not look like a scenario name; passing the suite failure through." >&2
41+
exit "$rc"
42+
fi
43+
done
44+
45+
# A stale baseline entry is a configuration error a solo rerun cannot excuse.
46+
# Here-string, not a pipe: grep -q quitting early would SIGPIPE printf and,
47+
# under pipefail, skip this guard exactly when the pattern is present.
48+
if grep -q '^Stale baseline entries' <<<"$plain"; then
49+
echo "Suite also reported stale baseline entries; not retrying." >&2
50+
exit "$rc"
51+
fi
52+
53+
# Drop the suite-only flags: --scenario replaces --suite, and solo runs are
54+
# judged directly rather than against the baseline.
55+
rerun_args=()
56+
output_dir=""
57+
skip_next=0
58+
expect_output_dir=0
59+
for arg in "$@"; do
60+
if [ "$skip_next" -eq 1 ]; then
61+
if [ "$expect_output_dir" -eq 1 ]; then
62+
output_dir="$arg"
63+
fi
64+
skip_next=0
65+
expect_output_dir=0
66+
continue
67+
fi
68+
case "$arg" in
69+
--output-dir)
70+
skip_next=1
71+
expect_output_dir=1
72+
;;
73+
--suite | --expected-failures) skip_next=1 ;;
74+
--output-dir=*) output_dir="${arg#--output-dir=}" ;;
75+
--suite=* | --expected-failures=*) ;;
76+
*) rerun_args+=("$arg") ;;
77+
esac
78+
done
79+
if [ -n "$output_dir" ]; then
80+
rerun_args+=(--output-dir "${output_dir}-solo")
81+
fi
82+
83+
for scenario in "${scenarios[@]}"; do
84+
passed=0
85+
for attempt in $(seq 1 "$SOLO_ATTEMPTS"); do
86+
echo ""
87+
echo "Re-running '${scenario}' solo (attempt ${attempt}/${SOLO_ATTEMPTS})..."
88+
if npx --yes "$CONFORMANCE_PKG" client --scenario "$scenario" "${rerun_args[@]}"; then
89+
passed=1
90+
break
91+
fi
92+
done
93+
if [ "$passed" -ne 1 ]; then
94+
echo "'${scenario}' still fails when run alone: real failure, not suite contention." >&2
95+
exit 1
96+
fi
97+
done
98+
99+
if [ -n "$output_dir" ]; then
100+
mkdir -p "$output_dir"
101+
printf '%s\n' "${scenarios[@]}" > "$output_dir/FLAKE_RESCUED"
102+
fi
103+
echo "All ${#scenarios[@]} unexpected failure(s) passed when re-run solo; the suite failures were parallel-run contention."
104+
exit 0

.github/workflows/conformance.yml

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,20 @@ jobs:
6464
./.github/actions/conformance/run-server.sh
6565
--suite active
6666
--expected-failures ./.github/actions/conformance/expected-failures.yml
67+
--output-dir conformance-results/server-active
6768
- name: Run server conformance (draft suite)
6869
run: >-
6970
./.github/actions/conformance/run-server.sh
7071
--suite draft
7172
--expected-failures ./.github/actions/conformance/expected-failures.yml
73+
--output-dir conformance-results/server-draft
7274
- name: Run server conformance (2026-07-28 wire, all suite)
7375
run: >-
7476
./.github/actions/conformance/run-server.sh
7577
--suite all
7678
--spec-version 2026-07-28
7779
--expected-failures ./.github/actions/conformance/expected-failures.2026-07-28.yml
80+
--output-dir conformance-results/server-2026-07-28
7881
- name: Run server conformance (all suite, extension scenarios)
7982
# A bare `--suite all` (no --spec-version) selects every scenario
8083
# shipped with the pinned harness — including the extension-tagged
@@ -91,6 +94,15 @@ jobs:
9194
./.github/actions/conformance/run-server.sh
9295
--suite all
9396
--expected-failures ./.github/actions/conformance/expected-failures.yml
97+
--output-dir conformance-results/server-all
98+
- name: Upload conformance results
99+
# The log has only summary counts; per-check data is in checks.json.
100+
if: failure()
101+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
102+
with:
103+
name: server-conformance-results
104+
path: conformance-results/
105+
if-no-files-found: ignore
94106

95107
client-conformance:
96108
runs-on: ubuntu-latest
@@ -118,22 +130,39 @@ jobs:
118130
echo "CONFORMANCE_PKG=file:/tmp/conformance.tgz" >> "$GITHUB_ENV"
119131
;;
120132
esac
121-
- run: uv sync --frozen --all-extras --package mcp
133+
# --compile-bytecode: without it, ~40 concurrently spawned interpreters
134+
# race to byte-compile site-packages during the timing-sensitive window.
135+
- run: uv sync --frozen --all-extras --package mcp --compile-bytecode
136+
- name: Pre-compile bytecode (editable sources)
137+
run: uv run --frozen python -m compileall -q src .github/actions/conformance
122138
- name: Run client conformance (all suite)
123139
# The harness runs all scenarios via unbounded Promise.all; with 40
124140
# scenarios on a 2-core runner the slowest one (sse-retry, which has a
125141
# real-time SSE reconnect wait) needs more than the 30s default budget.
142+
# `.venv/bin/python` (not `uv run`) avoids lockfile re-checks in ~40
143+
# concurrent spawns; run-client.sh re-runs unexpected failures solo.
126144
run: >-
127-
npx --yes "$CONFORMANCE_PKG" client
128-
--command 'uv run --frozen python .github/actions/conformance/client.py'
145+
./.github/actions/conformance/run-client.sh
146+
--command '.venv/bin/python .github/actions/conformance/client.py'
129147
--suite all
130148
--timeout 60000
131149
--expected-failures ./.github/actions/conformance/expected-failures.yml
150+
--output-dir conformance-results/client-all
132151
- name: Run client conformance (2026-07-28 wire, all suite)
133152
run: >-
134-
npx --yes "$CONFORMANCE_PKG" client
135-
--command 'uv run --frozen python .github/actions/conformance/client.py'
153+
./.github/actions/conformance/run-client.sh
154+
--command '.venv/bin/python .github/actions/conformance/client.py'
136155
--suite all
137156
--timeout 60000
138157
--spec-version 2026-07-28
139158
--expected-failures ./.github/actions/conformance/expected-failures.2026-07-28.yml
159+
--output-dir conformance-results/client-2026-07-28
160+
- name: Upload conformance results
161+
# The log has only summary counts; per-check data is in checks.json.
162+
# Also on FLAKE_RESCUED: rescued-flake evidence is otherwise discarded.
163+
if: failure() || hashFiles('conformance-results/**/FLAKE_RESCUED') != ''
164+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
165+
with:
166+
name: client-conformance-results
167+
path: conformance-results/
168+
if-no-files-found: ignore

.github/workflows/deploy-docs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ jobs:
4949

5050
- name: Build combined docs (v1.x at /, main at /v2/)
5151
run: bash scripts/build-docs.sh site
52+
env:
53+
# Silence mkdocs-material's MkDocs 2.0 warning banner in CI logs.
54+
NO_MKDOCS_2_WARNING: "1"
5255

5356
- name: Configure Pages
5457
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

.github/workflows/docs-preview.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ jobs:
130130

131131
- run: uv sync --frozen --group docs
132132
- run: uv run --frozen --no-sync mkdocs build
133+
env:
134+
# Silence mkdocs-material's MkDocs 2.0 warning banner in CI logs.
135+
NO_MKDOCS_2_WARNING: "1"
133136

134137
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
135138
with:

.github/workflows/shared.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,6 @@ jobs:
125125

126126
- name: Build the docs in strict mode
127127
run: uv run --frozen --no-sync mkdocs build --strict
128+
env:
129+
# Silence mkdocs-material's MkDocs 2.0 warning banner in CI logs.
130+
NO_MKDOCS_2_WARNING: "1"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,6 @@ cython_debug/
173173

174174
# claude code
175175
results/
176+
177+
# conformance CI local runs
178+
conformance-results/

AGENTS.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,10 @@ rather than adding new standalone sections.
138138
## Documentation
139139

140140
When a change affects public API or user-visible behaviour, update the relevant
141-
page(s) under `docs/` in the same PR. Docs are organised by topic
142-
(`tutorial/`, `client/`, `run/`, `advanced/`) — find the page covering the
143-
feature you touched rather than adding a new one.
141+
page(s) under `docs/` in the same PR. Docs are organised by the `nav:` sections
142+
in `mkdocs.yml` (Get started, Servers, Inside your handler, Running your server,
143+
Clients, Advanced), not by the on-disk directory names. Find the page covering
144+
the feature you touched in `mkdocs.yml` rather than adding a new one.
144145

145146
## Formatting & Type Checking
146147

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
>
1919
> **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.**
2020
>
21-
> 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 the [migration guide](https://py.sdk.modelcontextprotocol.io/v2/migration/) for what's changed. Stable v2 is targeted for 2026-07-27, 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).
21+
> 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-27, 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).
2222
2323
## Documentation
2424

2525
**The documentation lives at <https://py.sdk.modelcontextprotocol.io/v2/>.**
2626

27-
It has the full [tutorial](https://py.sdk.modelcontextprotocol.io/v2/tutorial/), the [API reference](https://py.sdk.modelcontextprotocol.io/v2/api/mcp/), and the [migration guide](https://py.sdk.modelcontextprotocol.io/v2/migration/).
27+
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/).
2828

2929
## What is MCP?
3030

@@ -82,7 +82,7 @@ Call `add` with `a=1`, `b=2` and you get `3` back.
8282

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

85-
[The tutorial](https://py.sdk.modelcontextprotocol.io/v2/tutorial/) takes it from here.
85+
[Get started](https://py.sdk.modelcontextprotocol.io/v2/get-started/) takes it from here.
8686

8787
## A client in 10 lines
8888

RELEASE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ the publish job — `skip-existing` makes it skip whatever already landed. The
3535

3636
1. Update the pre-release version examples in `README.md` and the docs
3737
(grep the outgoing version — the pins live in the README Installation
38-
section, `docs/index.md`, and `docs/installation.md`) so the tagged
38+
section, `docs/index.md`, `docs/get-started/installation.md`, and `docs/get-started/real-host.md`) so the tagged
3939
commit — and therefore the README PyPI publishes — names the version
4040
being released. When entering a new phase (alpha → beta → rc), update
4141
the banner wording too.

docs/.overrides/.icons/mcp.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)