modelcontextprotocol / modelcontextprotocol/python-sdk
pkg_version() in Server.create_initialization_options doesn't guard against importlib.metadata.version() returning None
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 24.3k
- Fork
- 4k
- Merge trung bình
- 1 ngày 1 giờ
- Pull request đã merge (30 ngày)
- 31
Mô tả
Summary
Server.create_initialization_options in mcp/server/lowlevel/server.py:183 falls through to pkg_version("mcp") when self.version is falsy. The fallback function catches exceptions and returns "unknown", but doesn't guard against the (rare but real) case where importlib.metadata.version() returns None instead of raising or returning a string.
When that happens, None flows through to InitializationOptions.server_version — which is typed as str on the pydantic model — and pydantic raises ValidationError. The server dies before the stdio handshake completes; the MCP client sees a bare "Connection closed" with no useful diagnostic.
Reproduction
Environment where I hit this: Python 3.12 embedded distribution installed via WiX MSI, with mcp>=1.26,<2 pip-installed into the embedded Python's site-packages via the standard python.exe -m pip install command.
Probe from the affected Python:
from importlib.metadata import version
print(repr(version("mcp"))) # prints: None
The mcp-1.30.0.dist-info/METADATA file on disk correctly declares Version: 1.30.0. Root cause of the None return appears to be a downstream bug in the embedded-Python distribution or its pip metadata setup — a separate concern I'm investigating on my side.
Impact
Any FastMCP server that (a) doesn't set an explicit version (FastMCP doesn't accept a version= kwarg anyway) and (b) runs on a Python where importlib.metadata.version("mcp") returns None instead of a string, crashes on stdio startup:
Exception Group Traceback (most recent call last):
File "…/mcp/server/fastmcp/server.py", line 775, in run_stdio_async
self._mcp_server.create_initialization_options(),
File "…/mcp/server/lowlevel/server.py", line 181, in create_initialization_options
return InitializationOptions(
File "…/pydantic/main.py", line 263, in __init__
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for InitializationOptions
server_version
Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
Full traceback from an affected install:
2026-09-09 14:44:00,841 [INFO] __main__: Starting GRAccess MCP server (stdio transport)
+ Exception Group Traceback (most recent call last):
| File "<frozen runpy>", line 198, in _run_module_as_main
| File "…/mcp/server/fastmcp/server.py", line 312, in run
| anyio.run(self.run_stdio_async)
| File "…/mcp/server/fastmcp/server.py", line 771, in run_stdio_async
| async with stdio_server() as (read_stream, write_stream):
| File "…/mcp/server/lowlevel/server.py", line 181, in create_initialization_options
| return InitializationOptions(
| File "…/pydantic/main.py", line 263, in __init__
| validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
| pydantic_core._pydantic_core.ValidationError: 1 validation error for InitializationOptions
| server_version
| Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
Suggested fix
Guard the return of pkg_version against None — matches the existing "returns 'unknown' on failure" contract implied by the type annotation and the # pragma: no cover fallback:
def pkg_version(package: str) -> str:
try:
from importlib.metadata import version
v = version(package)
if v is not None:
return v
except Exception: # pragma: no cover
pass
return "unknown" # pragma: no cover
Doesn't change happy-path behavior for well-formed metadata; catches the pathological None case that pydantic then rejects.
Workaround
Setting mcp._mcp_server.version explicitly on the underlying server after FastMCP(...) construction bypasses the fallback entirely. That's what I ended up shipping in my own server.
Version
mcp:1.30.0- Python:
3.12embedded distribution on Windows Server 2022
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu tại mcp/server/lowlevel/server.py, ở Server.create_initialization_options và fallback pkg_version mà nó gọi. Tái hiện việc importlib.metadata.version("mcp") trả về None, sau đó xác minh rằng quá trình khởi tạo tạo ra fallback "unknown" hiện có thay vì một Pydantic ValidationError, và rằng các chuỗi phiên bản thông thường vẫn không thay đổi.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- backend
- Loại issue
- Lỗi
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức phù hợp với người mới
- 78/100