Skip to content

fix: cap mcp<2 — 2.x SDK removed Server.list_tools, CI red on every PR (DSE-1261) - #92

Merged
ernestprovo23 merged 2 commits into
mainfrom
fix/dse-1261-cap-mcp-sdk-v2
Aug 24, 2026
Merged

fix: cap mcp<2 — 2.x SDK removed Server.list_tools, CI red on every PR (DSE-1261)#92
ernestprovo23 merged 2 commits into
mainfrom
fix/dse-1261-cap-mcp-sdk-v2

Conversation

@ernestprovo23

Copy link
Copy Markdown
Member

Closes DSE-1261. Unblocks every open PR — CI has been red on all of them, including #91.

What broke

pyproject.toml pinned mcp>=1.27.2 with no upper bound. The MCP Python SDK shipped 2.0.0, which removed the @server.list_tools() decorator used by the three fixture servers in tests/fixtures/:

AttributeError: 'Server' object has no attribute 'list_tools'

A fresh CI install resolves 2.0.0 and takes down the MCP integrity gate, Sigstore sign+verify (live), and the test suite. It hid for a month because nothing had run on main since 2026-07-26, and the build server's uv.lock still pinned 1.27.2 — so it reproduced only on a clean install.

Why a cap and not a migration

Production code is already 2.x-compatible. capture.py calls ClientSession.list_tools(), which 2.0.0 keeps — verified directly against 2.0.0:

ClientSession.list_tools: True
Server.list_tools:        False

Only the test fixtures use the removed server-side decorator. Porting them to the 2.x add_request_handler API is a real API migration and deserves its own reviewed change; it should not ride along in an unblock. The cap is one line and trivially reversible.

The lock file needed no edit

requirements-dev.lock already pins mcp==1.28.1, and regenerating from the capped pyproject reproduces it byte-for-byte. The Hash-locked dev/CI install job was failing only because it regenerated from the uncapped pyproject and resolved 2.0.0 — so this same one-line change fixes that job too.

Verification

Clean venv built exactly as CI builds it, with COVERAGE_PROCESS_START set:

  • resolves mcp 1.29.0 (cap honored)
  • 843 passed, 2 skipped
  • coverage 86.44% (floor 80)
  • the previously-crashing gate command now returns OK no drift, exit 0

Still separate

Re-check committed example locks fails for a genuinely different reason — real upstream drift in server-everything. That is warden correctly detecting a surface change against a month-stale pin, not a bug. Re-pinning it needs a human to review the surface diff and pin --approve, tracked on DSE-1261.

DSE-1261. CI has been red for every PR since the MCP Python SDK shipped 2.0.0.
pyproject pinned `mcp>=1.27.2` with no upper bound, so a fresh CI install
resolved 2.0.0, which removed the `@server.list_tools()` decorator that the
three fixture servers in tests/fixtures/ use:

    AttributeError: 'Server' object has no attribute 'list_tools'

That took down the MCP integrity gate, the Sigstore live sign+verify job, and
the test suite. It went unnoticed for a month because nothing had run on main
since 2026-07-26 and the build server's uv.lock still pinned 1.27.2.

The cap is deliberately conservative rather than a migration: production code
is ALREADY 2.x-compatible — capture.py calls ClientSession.list_tools(), which
2.0.0 keeps. Only the test fixtures use the removed server-side decorator, so
lifting this cap needs just those three files ported to the 2.x
add_request_handler API. That is a real API migration and belongs in its own
reviewed change, not smuggled into an unblock.

Note requirements-dev.lock needed no edit: it already pins mcp==1.28.1, and
regenerating from the capped pyproject reproduces it byte-for-byte. The
"Hash-locked dev/CI install" job was failing only because it regenerated from
the UNCAPPED pyproject and resolved 2.0.0, so this one-line cap fixes that job
too.

Verified in a clean venv built exactly as CI builds it, with
COVERAGE_PROCESS_START set: resolves mcp 1.29.0, 843 passed, coverage 86.44%,
and the previously-crashing gate command returns "OK no drift" exit 0.
The pip-audit CVE gate was failing closed on a real advisory in the locked
dependency closure:

    cryptography  49.0.0  PYSEC-2026-3552  Fix Versions: 50.0.0

This is the gate doing its job, not a false positive, so the fix is to take
the upgrade rather than widen the gate. Regenerated with
`--upgrade-package cryptography` so only that package moves; mcp stays pinned
at 1.28.1 under the new `<2` cap and nothing else in the closure shifts
version.

Verified in a clean venv:
- `pip_audit --skip-editable` reports zero findings in the locked closure
  (the 7 remaining hits are the venv's own bundled pip 24.0, which is not part
  of the closure and which the CI runner does not ship)
- 843 passed, 2 skipped, coverage 86.39%
- the integrity-gate self-test returns "OK no drift", exit 0
@ernestprovo23

Copy link
Copy Markdown
Member Author

Two corrections to the original PR description, now that the cap has actually run in CI:

1. Re-check committed example locks was not real upstream drift. I wrote above that server-everything had genuinely changed its surface and would need a human pin --approve. That was wrong — the cap turned that job green by itself. It was the SDK the whole time. No re-pin needed.

2. Hash-locked dev/CI install was never a lock-sync failure. The log says requirements-dev.lock is in sync with pyproject.toml; the job fails further down, on the pip-audit CVE gate, against a real advisory:

cryptography  49.0.0  PYSEC-2026-3552  Fix Versions: 50.0.0

That is the fail-closed CVE gate working as designed, so 4f0ec97 takes the upgrade rather than widening the gate. Regenerated with --upgrade-package cryptography so only that package moves — mcp stays at 1.28.1 under the new cap and nothing else in the closure shifts.

State before the CVE commit: 8 pass / 1 fail. Test suite, MCP integrity gate, Sigstore live sign+verify, and example locks all recovered from the cap alone.

Clean-venv verification after both commits: 843 passed, coverage 86.39%, pip_audit --skip-editable reports zero findings in the locked closure (the remaining hits are the venv's own bundled pip 24.0, which is not in the closure and which the CI runner does not ship), self-test returns OK no drift exit 0.

Classification note: with the CVE remediation this PR also classifies security-specific under rules/release-control.md, so it needs an approval receipt alongside #91.

@ernestprovo23
ernestprovo23 merged commit e994a8c into main Aug 24, 2026
10 checks passed
@ernestprovo23
ernestprovo23 deleted the fix/dse-1261-cap-mcp-sdk-v2 branch August 24, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant