modelcontextprotocol / modelcontextprotocol/python-sdk
pkg_version() in Server.create_initialization_options doesn't guard against importlib.metadata.version() returning None
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 24.3k
- Forks
- 4k
- Ø Merge
- 1 T. 1 Std.
- Gemergte PRs (30 T.)
- 31
Beschreibung
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
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne in mcp/server/lowlevel/server.py bei Server.create_initialization_options und dem von ihm aufgerufenen pkg_version-Fallback. Reproduziere die Rückgabe von None durch importlib.metadata.version("mcp"), überprüfe anschließend, dass die Initialisierung den bestehenden "unknown"-Fallback erzeugt statt eines Pydantic ValidationError, und dass normale Versionszeichenfolgen unverändert bleiben.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- backend
- Issue-Typ
- Bug
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Aktivitätsstatus
- Aktiv
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 78/100