Tighten the public API surface: root exports, listen types, dead-code removal - #3203
Tighten the public API surface: root exports, listen types, dead-code removal#3203maxisbey wants to merge 1 commit into
Conversation
… removal Root `mcp` package: export `MCPServer` (alongside the existing `Client`, so `from mcp import MCPServer, Client` works) and `NoBackChannelError`; drop the v1-carryover `SamplingRole` alias (`mcp.types.Role`). `mcp.client` now re-exports the types `Client.listen()` yields and raises: `Subscription`, `SubscriptionLost`, `ListenNotSupportedError`. Remove dead surface: `StreamableHTTPError`/`ResumptionError` (never raised from a reachable path) and `StreamableHTTPTransport.get_session_id()` on the client transport; the no-op `RegistrationRequest`/`TokenSuccessResponse` auth-handler aliases; the `validate_extension_identifier` re-export alias; the unreachable `__main__` guard in `mcp/cli/__init__.py`. The unused lowlevel server `Context` moves to the private `mcp.server._context` module. `_handle_resumption_request` now takes the resumption token as a required argument instead of re-deriving it and carrying an unreachable no-token branch. Enable ruff `RUF022` and sort every `__all__`; `mcp_types/__init__.py` is exempted since its `__all__` is grouped by section comments on purpose.
📚 Documentation preview
|
There was a problem hiding this comment.
1 issue found across 26 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/mcp/server/extension.py">
<violation number="1" location="src/mcp/server/extension.py:31">
P2: The server-extension validator is still publicly importable, so this removal is not actually effective. This direct import creates the same module attribute as the removed alias, and `MCPServer` still imports it through `mcp.server.extension`. Consider importing it privately here and changing `mcp.server.mcpserver.server` to import the shared helper directly, so the old import path raises `ImportError` as intended.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| # Re-exported from `mcp.shared.extension` (shared with the client surface) for existing importers. | ||
| from mcp.shared.extension import validate_extension_identifier as validate_extension_identifier | ||
| from mcp.shared.extension import validate_extension_identifier |
There was a problem hiding this comment.
P2: The server-extension validator is still publicly importable, so this removal is not actually effective. This direct import creates the same module attribute as the removed alias, and MCPServer still imports it through mcp.server.extension. Consider importing it privately here and changing mcp.server.mcpserver.server to import the shared helper directly, so the old import path raises ImportError as intended.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/mcp/server/extension.py, line 31:
<comment>The server-extension validator is still publicly importable, so this removal is not actually effective. This direct import creates the same module attribute as the removed alias, and `MCPServer` still imports it through `mcp.server.extension`. Consider importing it privately here and changing `mcp.server.mcpserver.server` to import the shared helper directly, so the old import path raises `ImportError` as intended.</comment>
<file context>
@@ -28,9 +28,7 @@
-
-# Re-exported from `mcp.shared.extension` (shared with the client surface) for existing importers.
-from mcp.shared.extension import validate_extension_identifier as validate_extension_identifier
+from mcp.shared.extension import validate_extension_identifier
if TYPE_CHECKING:
</file context>
There was a problem hiding this comment.
Beyond the inline finding, two other candidate concerns were examined and ruled out: (1) the removed RegistrationRequest/TokenSuccessResponse auth-handler aliases have no references anywhere in docs/ and were internal no-op aliases, so their removal needs no migration entry; (2) the _handle_resumption_request refactor preserves behavior — the old code routed on bool(metadata and metadata.resumption_token) and the new code on the same truthiness of the extracted token, so the deleted no-token branch was genuinely unreachable.
Extended reasoning...
The single reported bug (the docs/migration.md entry for the mcp.server.context.Context privatization that the PR description claims but never adds) was verified against the post-PR migration guide: no matching entry exists, so the inline comment stands. Beyond that, I checked the two most plausible additional risks. The auth-handler alias removal is not a documented breaking change — neither name appears in docs/, both lived in internal handler modules with explicit no-op-alias comments. The resumption-token refactor in streamable_http.py is behavior-preserving under truthiness (None and empty string both route to the plain POST path before and after), confirming the removed ResumptionError branch was dead code. The remaining diff is mechanical RUF022 all sorting, export additions with matching docs updates, and test relocation — none of which changes runtime behavior.
|
|
||
| - Sampling: `ServerSession.create_message()`, `ClientPeer.sample()` | ||
| - Roots: `ServerSession.list_roots()`, `ClientPeer.list_roots()`, `ClientSession.send_roots_list_changed()`, `Client.send_roots_list_changed()` | ||
| - Logging: `ServerSession.send_log_message()`, `Connection.log()`, `ClientSession.set_logging_level()`, `Client.set_logging_level()`, `mcp.server.context.Context.log()` (the lowlevel `Context`), and the `MCPServer` `Context` helpers `log()`, `debug()`, `info()`, `warning()`, `error()` | ||
| - Logging: `ServerSession.send_log_message()`, `Connection.log()`, `ClientSession.set_logging_level()`, `Client.set_logging_level()`, and the `MCPServer` `Context` helpers `log()`, `debug()`, `info()`, `warning()`, `error()` |
There was a problem hiding this comment.
🔴 The breaking change moving the lowlevel Context class from the public mcp.server.context module to the private mcp.server._context module is not documented in docs/migration.md, even though the PR description states it is. AGENTS.md requires all breaking changes to be documented there — please add a short migration entry (grouped with the existing lowlevel-server/context sections) noting the class is now private and that from mcp.server.context import Context raises ImportError.
Extended reasoning...
What the bug is. This PR moves the lowlevel server Context class out of the public mcp.server.context module into a new private mcp.server._context module. That is a breaking change: any code doing from mcp.server.context import Context now raises ImportError. The PR description explicitly claims this is covered: "Yes, all documented in docs/migration.md: ... mcp.server.context.Context is now private" — but no such entry exists in the guide.
Verification. Grepping the post-PR docs/migration.md for _context, Context is now private, and related phrases finds nothing. The PR's migration.md edits are only: the SamplingRole table row, the MCPServer top-level export note, the new StreamableHTTPError/ResumptionError/get_session_id() section, the NoBackChannelError export note, and — at lines 2780–2783 — the removal of the mcp.server.context.Context.log() mention from the SEP-2577 deprecated-logging list. That removed mention also confirms the class was documented public surface before this PR: the guide referenced it as live API, and the PR deleted the reference rather than documenting the move.
Why this matters. AGENTS.md states: "Breaking changes (including those softened by a backwards-compatibility shim) must be documented in docs/migration.md." This is an explicit repo-mandated requirement, and the guide already carries intra-v2 pre-release notes (since 2.0.0b2, since 2.0.0rc1), so "Context never existed in v1" does not excuse the omission. Notably, the three other removals in this same PR (SamplingRole; StreamableHTTPError/ResumptionError/get_session_id()) each received a dedicated migration section — the omission looks like an oversight, not a policy decision, and the author's own PR description shows the entry was intended.
Concrete walk-through. (1) A v2-RC user has middleware or tests importing from mcp.server.context import Context (a public, documented path before this PR — the migration guide itself referenced mcp.server.context.Context.log()). (2) They upgrade past this PR. (3) The import raises ImportError. (4) They open docs/migration.md — the canonical record of every breaking change per AGENTS.md — and search for Context or mcp.server.context: no entry explains the class moved to mcp.server._context or that it is now private. They are left to bisect the SDK source.
Mitigating factors considered. One verifier noted the class's own new docstring says "Private for now: ServerRunner does not construct it", so real-world importers are likely rare, and the fix is docs-only. That argues for lower impact, but the repo's own instructions make documenting breaking changes in migration.md a hard requirement, and the PR affirmatively (and incorrectly) claims compliance — this is not a stale-description case, since the entry was clearly intended and simply never written.
How to fix. Add a short section to docs/migration.md (grouped near the existing lowlevel-Server/context sections, e.g. after the RequestContext split section) stating: the lowlevel Context class moved from mcp.server.context to the private mcp.server._context module and is no longer public API; from mcp.server.context import Context now raises ImportError; ServerRequestContext and the middleware types (CallNext, HandlerResult, ServerMiddleware) in mcp.server.context are unchanged; handlers receive a ServerRequestContext, which is the supported surface.
There was a problem hiding this comment.
migration.md is for breaking changes from v1 to v2. this class wasn't in v1 so there's no reason for it to be in migration.md
Cleanup pass over the v2 public surface: a few missing exports, a few dead ones, and some no-op aliases.
Motivation and Context
mcp: exportsMCPServernext to the existingClient, sofrom mcp import MCPServer, Clientworks (thefirst-steps.mdtip claiming otherwise is removed). Also exportsNoBackChannelError, which every server-initiated request path can raise on a 2026-07-28 connection. Drops the v1-carryoverSamplingRolealias (usemcp.types.Role).mcp.client: re-exports the typesClient.listen()yields and raises —Subscription,SubscriptionLost,ListenNotSupportedError— which previously required reaching intomcp.client.subscriptions.StreamableHTTPError/ResumptionError(never raised from a code path a caller could catch) and the unusedStreamableHTTPTransport.get_session_id(); the no-opRegistrationRequest/TokenSuccessResponseauth-handler aliases; thevalidate_extension_identifierre-export alias inmcp.server.extension; the unreachable__main__guard inmcp/cli/__init__.py.Contextis not constructed byServerRunneryet, so it moves to the privatemcp.server._contextmodule until it's wired back in._handle_resumption_requestnow takes the resumption token as a required argument instead of re-deriving it, which is what let the unreachable no-token branch (and the two exception classes) go away.RUF022and sorts every__all__;mcp_types/__init__.pyis exempted since its__all__is grouped by section comments on purpose.How Has This Been Tested?
Full
./scripts/test(100% coverage held,strict-no-coverclean), plus a smoke test of the public boundary in a fresh interpreter: root/mcp.clientimports resolve, removed names raiseImportError, andfrom mcp import MCPServer, Clientround-trips a tool call both in-memory and over Streamable HTTP.Breaking Changes
Yes, all documented in
docs/migration.md:SamplingRoleis gone from the root package,StreamableHTTPError/ResumptionError/get_session_id()are gone frommcp.client.streamable_http, andmcp.server.context.Contextis now private (ServerRequestContextand the middleware types are unchanged).Types of changes
Checklist
Additional context
AI Disclaimer