Skip to content

Commit db81ef5

Browse files
committed
Bind mcp.types on import mcp, restoring v1's fourth import idiom
v1's mcp/__init__ ran `from .types import ...`, which bound the `types` submodule on the package, so `import mcp` followed by `mcp.types.Tool` worked in every v1 release. With the module split out, nothing imported it eagerly, so that idiom raised `AttributeError: module 'mcp' has no attribute 'types'` and contradicted the guide's claim that v1 import lines need no change. Import the submodule from the package init, as v1 did, so all four v1 idioms hold. Also reword the migration note so `mcp.shared.version` is not implied to be the only removed `mcp.shared` import path.
1 parent 426fb79 commit db81ef5

3 files changed

Lines changed: 27 additions & 4 deletions

File tree

docs/migration.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,11 @@ package depends on `mcp-types` and continues to re-export the type names at the
175175
`from mcp import Tool` is unchanged. The package's API reference is at
176176
[`mcp_types`](api/mcp_types/index.md).
177177

178-
`import mcp.types` and `from mcp.types import ...` keep working: `mcp.types` is a permanent
179-
alias that mirrors `mcp_types` exactly (every name is the same object), so v1's most common
180-
import line needs no change if you already depend on `mcp`. Only `mcp.shared.version` was
181-
removed; import from `mcp_types.version` instead. Reading a name that no longer exists (listed
178+
`import mcp.types`, `from mcp.types import ...`, and `import mcp` followed by `mcp.types.Tool`
179+
all keep working: `mcp.types` is a permanent alias that mirrors `mcp_types` exactly (every
180+
name is the same object), so v1's import lines need no change if you already depend on `mcp`.
181+
The old `mcp.shared.version` module was removed; import from `mcp_types.version` instead.
182+
Reading a name that no longer exists (listed
182183
under [Removed type aliases and classes](#removed-type-aliases-and-classes)) as
183184
`mcp.types.Content` raises an `AttributeError` that names its replacement; a
184185
`from mcp.types import Content` of one raises a plain `ImportError` (Python discards the hint on

src/mcp/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
)
5959
from mcp_types import Role as SamplingRole
6060

61+
# Bind the `mcp.types` submodule on the package, as v1's `from .types import
62+
# ...` did, so `import mcp` followed by `mcp.types.Tool` keeps working.
63+
from . import types as types
6164
from .client._input_required import InputRequiredRoundsExceededError
6265
from .client.client import Client
6366
from .client.session import ClientSession

tests/test_types.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import subprocess
2+
import sys
13
from typing import Any
24

35
import mcp_types
@@ -484,3 +486,20 @@ def test_mcp_types_unknown_attribute_raises_attribute_error():
484486
with pytest.raises(AttributeError):
485487
getattr(mcp.types, "not_a_protocol_type")
486488
assert not hasattr(mcp.types, "not_a_protocol_type")
489+
490+
491+
def test_bare_import_mcp_binds_the_types_submodule():
492+
"""SDK-defined: `import mcp` alone binds `mcp.types`, so v1's `mcp.types.Tool` idiom works.
493+
494+
A fresh interpreter is required to observe `import mcp` in isolation: this test process
495+
has already imported `mcp.types`, and reloading `mcp` here would rebind classes that other
496+
tests hold references to.
497+
"""
498+
result = subprocess.run(
499+
[sys.executable, "-c", "import mcp; print(mcp.types.Tool.__name__)"],
500+
capture_output=True,
501+
text=True,
502+
check=False,
503+
)
504+
assert result.returncode == 0, result.stderr
505+
assert result.stdout == snapshot("Tool\n")

0 commit comments

Comments
 (0)