1414from inline_snapshot import snapshot
1515
1616from mcp .server import Server , ServerRequestContext
17- from mcp .types import JSONRPCResponse , ListToolsResult , PaginatedRequestParams , Tool
17+ from mcp .types import (
18+ INVALID_REQUEST ,
19+ CallToolRequestParams ,
20+ CallToolResult ,
21+ JSONRPCError ,
22+ JSONRPCResponse ,
23+ ListToolsResult ,
24+ PaginatedRequestParams ,
25+ TextContent ,
26+ Tool ,
27+ )
1828from tests .interaction ._connect import (
1929 base_headers ,
2030 client_via_http ,
@@ -32,9 +42,25 @@ def _server() -> Server:
3242 """A minimal low-level server with one tool, so subsequent-request routing can be observed."""
3343
3444 async def list_tools (ctx : ServerRequestContext , params : PaginatedRequestParams | None ) -> ListToolsResult :
35- return ListToolsResult (tools = [Tool (name = "noop" , description = "Does nothing." , input_schema = {"type" : "object" })])
45+ return ListToolsResult (
46+ tools = [
47+ Tool (name = "noop" , description = "Does nothing." , input_schema = {"type" : "object" }),
48+ Tool (
49+ name = "client-name" ,
50+ description = "Reports the initialized client name." ,
51+ input_schema = {"type" : "object" },
52+ ),
53+ ]
54+ )
55+
56+ async def call_tool (ctx : ServerRequestContext , params : CallToolRequestParams ) -> CallToolResult :
57+ client_params = ctx .session .client_params
58+ assert client_params is not None
59+ assert params .name == "client-name"
60+ client_name = client_params .client_info .name
61+ return CallToolResult (content = [TextContent (text = client_name )], structured_content = {"clientName" : client_name })
3662
37- return Server ("hosted" , on_list_tools = list_tools )
63+ return Server ("hosted" , on_list_tools = list_tools , on_call_tool = call_tool )
3864
3965
4066@requirement ("hosting:session:create" )
@@ -142,20 +168,49 @@ async def test_terminating_one_session_leaves_others_working() -> None:
142168
143169
144170@requirement ("hosting:session:reinitialize" )
145- async def test_second_initialize_on_an_existing_session_is_accepted () -> None :
146- """A second initialize POST carrying an existing session ID is processed rather than rejected.
147-
148- See the divergence on the requirement: the entry expects a rejection, but the SDK forwards the
149- second initialize to the running server, which answers it as a fresh handshake.
150- """
171+ async def test_second_initialize_on_an_existing_session_is_rejected () -> None :
172+ """A second initialize POST carrying an existing session ID is rejected without changing client params."""
151173 async with mounted_app (_server ()) as (http , manager ):
152174 session_id = await initialize_via_http (http )
153- response , messages = await post_jsonrpc (http , initialize_body (request_id = 2 ), session_id = session_id )
175+ call_body : dict [str , object ] = {
176+ "jsonrpc" : "2.0" ,
177+ "id" : 2 ,
178+ "method" : "tools/call" ,
179+ "params" : {"name" : "client-name" },
180+ }
181+ first_call_response , first_call_messages = await post_jsonrpc (http , call_body , session_id = session_id )
182+
183+ response , messages = await post_jsonrpc (
184+ http , initialize_body (request_id = 3 , client_name = "reinitializer" ), session_id = session_id
185+ )
186+ second_call_body : dict [str , object ] = {
187+ "jsonrpc" : "2.0" ,
188+ "id" : 4 ,
189+ "method" : "tools/call" ,
190+ "params" : {"name" : "client-name" },
191+ }
192+ second_call_response , second_call_messages = await post_jsonrpc (
193+ http ,
194+ second_call_body ,
195+ session_id = session_id ,
196+ )
154197 assert len (manager ._server_instances ) == 1
155198
199+ assert first_call_response .status_code == 200
200+ assert isinstance (first_call_messages [0 ], JSONRPCResponse )
201+ first_call_result = CallToolResult .model_validate (first_call_messages [0 ].result )
202+ assert first_call_result .structured_content == {"clientName" : "raw" }
203+
156204 assert response .status_code == snapshot (200 )
157- assert isinstance (messages [0 ], JSONRPCResponse )
158- assert messages [0 ].id == 2
205+ assert isinstance (messages [0 ], JSONRPCError )
206+ assert messages [0 ].id == 3
207+ assert messages [0 ].error .code == INVALID_REQUEST
208+ assert messages [0 ].error .message == "Server is already initialized"
209+
210+ assert second_call_response .status_code == 200
211+ assert isinstance (second_call_messages [0 ], JSONRPCResponse )
212+ second_call_result = CallToolResult .model_validate (second_call_messages [0 ].result )
213+ assert second_call_result .structured_content == {"clientName" : "raw" }
159214
160215
161216@requirement ("hosting:stateless:no-session-id" )
0 commit comments