PrefectHQ/fastmcp · error · RuntimeError
The client negotiated a modern protocol era (server/discover
Error message
The client negotiated a modern protocol era (server/discover), which has no InitializeResult. Inspect client.protocol_version, client.server_info, client.server_capabilities, and client.instructions for the metadata available in this mode, or construct the client with mode='legacy'.
What it means
In the modern protocol era ('server/discover' mode) there is no MCP InitializeResult; connection metadata lives in client.protocol_version, client.server_info, client.server_capabilities, and client.instructions instead. Calling `client.initialize()` after such a negotiation raises this RuntimeError, pointing you at those attributes or to legacy mode.
Source
Thrown at fastmcp_slim/fastmcp/client/client.py:940
Example:
```python
# With auto-initialization disabled
client = Client(server, auto_initialize=False)
async with client:
result = await client.initialize()
print(f"Server: {result.server_info.name}")
print(f"Instructions: {result.instructions}")
```
"""
if self.initialize_result is not None:
return self.initialize_result
await self._negotiate(timeout=timeout)
if self.initialize_result is None:
raise RuntimeError(
"The client negotiated a modern protocol era (server/discover), which has "
"no InitializeResult. Inspect client.protocol_version, client.server_info, "
"client.server_capabilities, and client.instructions for the metadata "
"available in this mode, or construct the client with mode='legacy'."
)
return self.initialize_result
async def __aenter__(self):
return await self._connect()
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self._disconnect()
async def _connect(self):
"""
Establish or reuse a session connection.
This method implements the reentrant context manager pattern:View on GitHub (pinned to 1f02114297)
Solutions
- Use client.protocol_version / client.server_info / client.server_capabilities / client.instructions instead of initialize_result
- Construct the client with mode='legacy' if you need the classic InitializeResult
- Branch on protocol_version and handle the modern-mode metadata path
Example fix
// before
async with client:
info = await client.initialize() # RuntimeError in modern mode
// after
async with client:
info = client.server_info # or: Client(transport, mode='legacy') then initialize() Defensive patterns
Strategy: fallback
Validate before calling
def uses_legacy_initialize(client) -> bool:
return client.protocol_version not in MODERN_PROTOCOL_VERSIONS Type guard
def has_initialize_result(client) -> bool:
return client.initialize_result is not None Try / catch
try:
info = await client.initialize()
except RuntimeError as e:
if "modern protocol era" in str(e):
info = client.server_info
else:
raise Prevention
- Read metadata from client.server_info/capabilities/instructions in modern mode
- Pin mode='legacy' when porting legacy initialize-based code
- Branch on client.protocol_version before calling initialize()
When it happens
Trigger: Creating `Client(..., mode='server')` (modern mode) and then awaiting `client.initialize()`; calling initialize() on a client whose auto-negotiation selected a modern protocol version (MCP 2026-07-28).
Common situations: Legacy initialize-flow code reused with a modern-mode client; reading initialize_result for server capabilities in a codebase migrated to the new protocol era.
Related errors
- logging/setLevel is not available on MCP 2026-07-28 connecti
- INVALID_PARAMS
- Unexpected elicitation action: {result.action}
- Error reading resource {uri!r}
- Error reading resource {uri!r}: {e}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/de5b399b60aedffd.
Report an issue: GitHub.