Skip to content

Commit 6623a38

Browse files
committed
client: store server_info on ClientSession after initialization
After the initialize handshake, the InitializeResult contains serverInfo describing the connected server. Previously this data was returned from initialize() but not retained on the session, requiring callers to store it themselves. Add _server_info attribute set during initialize() and expose it via get_server_info(), mirroring the existing get_server_capabilities() pattern. Returns None before initialization, the Implementation object after. Github-Issue: #1018 Reported-by: mconflitti-pbc
1 parent e1fd62e commit 6623a38

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

src/mcp/client/session.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def __init__(
132132
self._message_handler = message_handler or _default_message_handler
133133
self._tool_output_schemas: dict[str, dict[str, Any] | None] = {}
134134
self._server_capabilities: types.ServerCapabilities | None = None
135+
self._server_info: types.Implementation | None = None
135136
self._experimental_features: ExperimentalClientFeatures | None = None
136137

137138
# Experimental: Task handlers (use defaults if not provided)
@@ -186,6 +187,7 @@ async def initialize(self) -> types.InitializeResult:
186187
raise RuntimeError(f"Unsupported protocol version from the server: {result.protocol_version}")
187188

188189
self._server_capabilities = result.capabilities
190+
self._server_info = result.server_info
189191

190192
await self.send_notification(types.InitializedNotification())
191193

@@ -198,6 +200,13 @@ def get_server_capabilities(self) -> types.ServerCapabilities | None:
198200
"""
199201
return self._server_capabilities
200202

203+
def get_server_info(self) -> types.Implementation | None:
204+
"""Return the server info received during initialization.
205+
206+
Returns None if the session has not been initialized yet.
207+
"""
208+
return self._server_info
209+
201210
@property
202211
def experimental(self) -> ExperimentalClientFeatures:
203212
"""Experimental APIs for tasks and other features.

tests/client/test_session.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,64 @@ async def mock_server():
607607
assert capabilities.tools.list_changed is False
608608

609609

610+
@pytest.mark.anyio
611+
async def test_get_server_info():
612+
"""Test that get_server_info returns None before init and server info after"""
613+
client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](1)
614+
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](1)
615+
616+
expected_server_info = Implementation(name="my-test-server", version="2.5.0")
617+
618+
async def mock_server():
619+
session_message = await client_to_server_receive.receive()
620+
jsonrpc_request = session_message.message
621+
assert isinstance(jsonrpc_request, JSONRPCRequest)
622+
request = client_request_adapter.validate_python(
623+
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
624+
)
625+
assert isinstance(request, InitializeRequest)
626+
627+
result = InitializeResult(
628+
protocol_version=LATEST_PROTOCOL_VERSION,
629+
capabilities=ServerCapabilities(),
630+
server_info=expected_server_info,
631+
)
632+
633+
async with server_to_client_send:
634+
await server_to_client_send.send(
635+
SessionMessage(
636+
JSONRPCResponse(
637+
jsonrpc="2.0",
638+
id=jsonrpc_request.id,
639+
result=result.model_dump(by_alias=True, mode="json", exclude_none=True),
640+
)
641+
)
642+
)
643+
await client_to_server_receive.receive()
644+
645+
async with (
646+
ClientSession(
647+
server_to_client_receive,
648+
client_to_server_send,
649+
) as session,
650+
anyio.create_task_group() as tg,
651+
client_to_server_send,
652+
client_to_server_receive,
653+
server_to_client_send,
654+
server_to_client_receive,
655+
):
656+
assert session.get_server_info() is None
657+
658+
tg.start_soon(mock_server)
659+
await session.initialize()
660+
661+
server_info = session.get_server_info()
662+
assert server_info is not None
663+
assert server_info == expected_server_info
664+
assert server_info.name == "my-test-server"
665+
assert server_info.version == "2.5.0"
666+
667+
610668
@pytest.mark.anyio
611669
@pytest.mark.parametrize(argnames="meta", argvalues=[None, {"toolMeta": "value"}])
612670
async def test_client_tool_call_with_meta(meta: RequestParamsMeta | None):

0 commit comments

Comments
 (0)