22
33import pytest
44
5+ from mcp .server import Server
56from mcp .server .fastmcp import FastMCP
67from mcp .shared .memory import create_connected_server_and_client_session as create_session
8+ from mcp .types import ListToolsRequest , ListToolsResult
79
810from .conftest import StreamSpyCollection
911
@@ -36,15 +38,15 @@ async def test_tool_2() -> str:
3638 _ = await client_session .list_tools ()
3739 list_tools_requests = spies .get_client_requests (method = "tools/list" )
3840 assert len (list_tools_requests ) == 1
39- assert list_tools_requests [0 ].params is None
41+ assert list_tools_requests [0 ].params == {}
4042
4143 spies .clear ()
4244
4345 # Test with cursor=None
4446 _ = await client_session .list_tools (cursor = None )
4547 list_tools_requests = spies .get_client_requests (method = "tools/list" )
4648 assert len (list_tools_requests ) == 1
47- assert list_tools_requests [0 ].params is None
49+ assert list_tools_requests [0 ].params == {}
4850
4951 spies .clear ()
5052
@@ -86,15 +88,15 @@ async def test_resource() -> str:
8688 _ = await client_session .list_resources ()
8789 list_resources_requests = spies .get_client_requests (method = "resources/list" )
8890 assert len (list_resources_requests ) == 1
89- assert list_resources_requests [0 ].params is None
91+ assert list_resources_requests [0 ].params == {}
9092
9193 spies .clear ()
9294
9395 # Test with cursor=None
9496 _ = await client_session .list_resources (cursor = None )
9597 list_resources_requests = spies .get_client_requests (method = "resources/list" )
9698 assert len (list_resources_requests ) == 1
97- assert list_resources_requests [0 ].params is None
99+ assert list_resources_requests [0 ].params == {}
98100
99101 spies .clear ()
100102
@@ -135,15 +137,15 @@ async def test_prompt(name: str) -> str:
135137 _ = await client_session .list_prompts ()
136138 list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
137139 assert len (list_prompts_requests ) == 1
138- assert list_prompts_requests [0 ].params is None
140+ assert list_prompts_requests [0 ].params == {}
139141
140142 spies .clear ()
141143
142144 # Test with cursor=None
143145 _ = await client_session .list_prompts (cursor = None )
144146 list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
145147 assert len (list_prompts_requests ) == 1
146- assert list_prompts_requests [0 ].params is None
148+ assert list_prompts_requests [0 ].params == {}
147149
148150 spies .clear ()
149151
@@ -185,15 +187,15 @@ async def test_template(name: str) -> str:
185187 _ = await client_session .list_resource_templates ()
186188 list_templates_requests = spies .get_client_requests (method = "resources/templates/list" )
187189 assert len (list_templates_requests ) == 1
188- assert list_templates_requests [0 ].params is None
190+ assert list_templates_requests [0 ].params == {}
189191
190192 spies .clear ()
191193
192194 # Test with cursor=None
193195 _ = await client_session .list_resource_templates (cursor = None )
194196 list_templates_requests = spies .get_client_requests (method = "resources/templates/list" )
195197 assert len (list_templates_requests ) == 1
196- assert list_templates_requests [0 ].params is None
198+ assert list_templates_requests [0 ].params == {}
197199
198200 spies .clear ()
199201
@@ -212,3 +214,35 @@ async def test_template(name: str) -> str:
212214 assert len (list_templates_requests ) == 1
213215 assert list_templates_requests [0 ].params is not None
214216 assert list_templates_requests [0 ].params ["cursor" ] == ""
217+
218+
219+ async def test_list_tools_with_strict_server_validation ():
220+ """Test that list_tools works with strict servers require a params field,
221+ even if it is empty.
222+
223+ Some MCP servers may implement strict JSON-RPC validation that requires
224+ the params field to always be present in requests, even if empty {}.
225+
226+ This test ensures such servers are supported by the client SDK for list_resources
227+ requests without a cursor.
228+ """
229+
230+ server = Server ("strict_server" )
231+
232+ @server .list_tools ()
233+ async def handle_list_tools (request : ListToolsRequest ) -> ListToolsResult :
234+ """Strict handler that validates params field exists"""
235+
236+ # Simulate strict server validation
237+ if request .params is None :
238+ raise ValueError (
239+ "Strict server validation failed: params field must be present. "
240+ "Expected params: {} for requests without cursor."
241+ )
242+
243+ # Return empty tools list
244+ return ListToolsResult (tools = [])
245+
246+ async with create_session (server ) as client_session :
247+ result = await client_session .list_tools ()
248+ assert result is not None
0 commit comments