Skip to content

Commit c27f95c

Browse files
committed
Drop the removed-name hint machinery from mcp.types
The _REMOVED map and module __getattr__ intercepted the v1 names that no longer exist to name their replacement in the error. That is extra mechanism to maintain for a one-time migration nudge; a name that does not exist should simply fail to import like any other. Remove it and its tests, leaving mcp.types as a plain star mirror. The migration guide's removed-names table still names each replacement.
1 parent 6c5ab37 commit c27f95c

3 files changed

Lines changed: 5 additions & 72 deletions

File tree

docs/migration.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Every section heading below names the API it affects, so searching this page for
1414
|---|---|---|
1515
| `FastMCP` renamed to `MCPServer` | `ModuleNotFoundError: No module named 'mcp.server.fastmcp'` | [`FastMCP` renamed](#fastmcp-renamed-to-mcpserver) |
1616
| Fields renamed from camelCase to snake_case | `AttributeError: 'Tool' object has no attribute 'inputSchema'` | [snake_case fields](#field-names-changed-from-camelcase-to-snake_case) |
17-
| `mcp.types` names removed | `AttributeError: mcp.types.Content was removed in v2; use ContentBlock.` | [Removed types](#removed-type-aliases-and-classes) |
17+
| `mcp.types` names removed | `ImportError: cannot import name 'Content' from 'mcp.types'` | [Removed types](#removed-type-aliases-and-classes) |
1818
| `McpError` renamed to `MCPError` | `ImportError: cannot import name 'McpError' from 'mcp'` | [`McpError` renamed](#mcperror-renamed-to-mcperror) |
1919
| Resource URIs are `str`, not `AnyUrl` | `AttributeError: 'str' object has no attribute 'host'` | [URI type](#resource-uri-type-changed-from-anyurl-to-str) |
2020
| Message unions (`ServerNotification`, `JSONRPCMessage`, ...) are plain unions, not `RootModel` | `AttributeError: 'LoggingMessageNotification' object has no attribute 'root'` | [`RootModel` → unions](#replace-rootmodel-by-union-types-with-typeadapter-validation) |
@@ -216,11 +216,10 @@ SDK.** That is the point of the split: tooling and lightweight clients can depen
216216
protocol schema without pulling in `httpx2`, `starlette`, `uvicorn`, and the rest of the
217217
server/transport stack.
218218

219-
Reading a name that no longer exists (listed under
220-
[Removed type aliases and classes](#removed-type-aliases-and-classes)) as
221-
`mcp.types.Content` raises an `AttributeError` that names its replacement; a
222-
`from mcp.types import Content` of one raises a plain `ImportError` (Python discards the hint on
223-
that path, but it still fails fast).
219+
Names that no longer exist (listed under
220+
[Removed type aliases and classes](#removed-type-aliases-and-classes)) fail on import or
221+
attribute access with an ordinary `ImportError` / `AttributeError`; the table below names each
222+
replacement.
224223

225224
The supported import surface is the package plus its `jsonrpc`, `methods`, and `version`
226225
submodules, and each has both spellings: `mcp.types` / `mcp_types`, `mcp.types.jsonrpc` /

src/mcp/types/__init__.py

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,3 @@
2929
from . import jsonrpc as jsonrpc
3030
from . import methods as methods
3131
from . import version as version
32-
33-
# Names v1's mcp.types exposed that no longer exist. A bare "cannot import name"
34-
# leaves people grepping; naming the replacement finishes the migration step.
35-
_REMOVED = {
36-
"Content": "use ContentBlock",
37-
"ResourceReference": "use ResourceTemplateReference",
38-
"Cursor": "use str",
39-
"AnyFunction": "use Callable[..., Any]",
40-
"MethodT": "it was an internal TypeVar, not public API",
41-
"RequestParamsT": "it was an internal TypeVar, not public API",
42-
"NotificationParamsT": "it was an internal TypeVar, not public API",
43-
"ClientRequestType": "the union is now the bare name ClientRequest",
44-
"ClientNotificationType": "the union is now the bare name ClientNotification",
45-
"ClientResultType": "the union is now the bare name ClientResult",
46-
"ServerRequestType": "the union is now the bare name ServerRequest",
47-
"ServerNotificationType": "the union is now the bare name ServerNotification",
48-
"ServerResultType": "the union is now the bare name ServerResult",
49-
"TaskExecutionMode": "use the string literals directly",
50-
"TASK_FORBIDDEN": "use the string literal 'forbidden'",
51-
"TASK_OPTIONAL": "use the string literal 'optional'",
52-
"TASK_REQUIRED": "use the string literal 'required'",
53-
"TASK_STATUS_CANCELLED": "use the string literal 'cancelled'; TaskStatus remains",
54-
"TASK_STATUS_COMPLETED": "use the string literal 'completed'; TaskStatus remains",
55-
"TASK_STATUS_FAILED": "use the string literal 'failed'; TaskStatus remains",
56-
"TASK_STATUS_INPUT_REQUIRED": "use the string literal 'input_required'; TaskStatus remains",
57-
"TASK_STATUS_WORKING": "use the string literal 'working'; TaskStatus remains",
58-
}
59-
60-
61-
def __getattr__(name: str) -> object:
62-
if (hint := _REMOVED.get(name)) is not None:
63-
# AttributeError as PEP 562 requires, so hasattr() and getattr(..., default)
64-
# still take their fallback path. The cost: `from mcp.types import Content`
65-
# discards this message for CPython's generic "cannot import name" (still
66-
# fail-fast); attribute access, the far more common v1 spelling, keeps it.
67-
raise AttributeError(f"mcp.types.{name} was removed in v2; {hint}. See the migration guide.")
68-
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

tests/test_types.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -485,35 +485,6 @@ def test_mcp_types_submodules_mirror_mcp_types_submodules_exactly(mirror: Module
485485
_assert_mirrors(mirror, source)
486486

487487

488-
def test_mcp_types_attribute_access_names_the_replacement_for_a_removed_name():
489-
"""SDK-defined: reading a v1 name that no longer exists names its v2 replacement."""
490-
with pytest.raises(AttributeError) as exc_info:
491-
getattr(mcp.types, "Content")
492-
assert str(exc_info.value) == snapshot(
493-
"mcp.types.Content was removed in v2; use ContentBlock. See the migration guide."
494-
)
495-
# AttributeError (not ImportError) keeps the introspection contract for probing code.
496-
assert not hasattr(mcp.types, "Content")
497-
assert getattr(mcp.types, "Content", None) is None
498-
499-
500-
def test_mcp_types_from_import_of_a_removed_name_still_fails_fast():
501-
"""SDK-defined: `from mcp.types import <removed>` raises rather than importing a stale name.
502-
503-
CPython builds this ImportError itself and discards the module's hint text, so only the
504-
exception type is asserted; the hint is on the attribute-access path above.
505-
"""
506-
with pytest.raises(ImportError):
507-
exec("from mcp.types import Content")
508-
509-
510-
def test_mcp_types_unknown_attribute_raises_attribute_error():
511-
"""SDK-defined: an unknown attribute is a normal AttributeError, so `hasattr` returns False."""
512-
with pytest.raises(AttributeError):
513-
getattr(mcp.types, "not_a_protocol_type")
514-
assert not hasattr(mcp.types, "not_a_protocol_type")
515-
516-
517488
def test_bare_import_mcp_binds_the_types_submodule():
518489
"""SDK-defined: `import mcp` alone binds `mcp.types`, so v1's `mcp.types.Tool` idiom works.
519490

0 commit comments

Comments
 (0)