@@ -221,6 +221,337 @@ async def test_template(name: str) -> str:
221221 assert list_templates_requests [0 ].params ["cursor" ] == ""
222222
223223
224+ async def test_list_tools_params_parameter (stream_spy : Callable [[], StreamSpyCollection ]):
225+ """Test that the params parameter works correctly for list_tools.
226+
227+ This tests the new params parameter API (non-deprecated) to ensure
228+ it correctly handles all parameter combinations.
229+ """
230+ server = FastMCP ("test" )
231+
232+ # Create a couple of test tools
233+ @server .tool (name = "test_tool_1" )
234+ async def test_tool_1 () -> str :
235+ """First test tool"""
236+ return "Result 1"
237+
238+ @server .tool (name = "test_tool_2" )
239+ async def test_tool_2 () -> str :
240+ """Second test tool"""
241+ return "Result 2"
242+
243+ async with create_session (server ._mcp_server ) as client_session :
244+ spies = stream_spy ()
245+
246+ # Test without params parameter (omitted)
247+ _ = await client_session .list_tools ()
248+ list_tools_requests = spies .get_client_requests (method = "tools/list" )
249+ assert len (list_tools_requests ) == 1
250+ assert list_tools_requests [0 ].params is None
251+
252+ spies .clear ()
253+
254+ # Test with params=None
255+ _ = await client_session .list_tools (params = None )
256+ list_tools_requests = spies .get_client_requests (method = "tools/list" )
257+ assert len (list_tools_requests ) == 1
258+ assert list_tools_requests [0 ].params is None
259+
260+ spies .clear ()
261+
262+ # Test with empty params (for strict servers)
263+ _ = await client_session .list_tools (params = types .PaginatedRequestParams ())
264+ list_tools_requests = spies .get_client_requests (method = "tools/list" )
265+ assert len (list_tools_requests ) == 1
266+ assert list_tools_requests [0 ].params is not None
267+ assert list_tools_requests [0 ].params .get ("cursor" ) is None
268+
269+ spies .clear ()
270+
271+ # Test with params containing cursor
272+ _ = await client_session .list_tools (params = types .PaginatedRequestParams (cursor = "some_cursor_value" ))
273+ list_tools_requests = spies .get_client_requests (method = "tools/list" )
274+ assert len (list_tools_requests ) == 1
275+ assert list_tools_requests [0 ].params is not None
276+ assert list_tools_requests [0 ].params ["cursor" ] == "some_cursor_value"
277+
278+
279+ async def test_list_resources_params_parameter (stream_spy : Callable [[], StreamSpyCollection ]):
280+ """Test that the params parameter works correctly for list_resources.
281+
282+ This tests the new params parameter API (non-deprecated) to ensure
283+ it correctly handles all parameter combinations.
284+ """
285+ server = FastMCP ("test" )
286+
287+ # Create a test resource
288+ @server .resource ("resource://test/data" )
289+ async def test_resource () -> str :
290+ """Test resource"""
291+ return "Test data"
292+
293+ async with create_session (server ._mcp_server ) as client_session :
294+ spies = stream_spy ()
295+
296+ # Test without params parameter (omitted)
297+ _ = await client_session .list_resources ()
298+ list_resources_requests = spies .get_client_requests (method = "resources/list" )
299+ assert len (list_resources_requests ) == 1
300+ assert list_resources_requests [0 ].params is None
301+
302+ spies .clear ()
303+
304+ # Test with params=None
305+ _ = await client_session .list_resources (params = None )
306+ list_resources_requests = spies .get_client_requests (method = "resources/list" )
307+ assert len (list_resources_requests ) == 1
308+ assert list_resources_requests [0 ].params is None
309+
310+ spies .clear ()
311+
312+ # Test with empty params (for strict servers)
313+ _ = await client_session .list_resources (params = types .PaginatedRequestParams ())
314+ list_resources_requests = spies .get_client_requests (method = "resources/list" )
315+ assert len (list_resources_requests ) == 1
316+ assert list_resources_requests [0 ].params is not None
317+ assert list_resources_requests [0 ].params .get ("cursor" ) is None
318+
319+ spies .clear ()
320+
321+ # Test with params containing cursor
322+ _ = await client_session .list_resources (params = types .PaginatedRequestParams (cursor = "some_cursor" ))
323+ list_resources_requests = spies .get_client_requests (method = "resources/list" )
324+ assert len (list_resources_requests ) == 1
325+ assert list_resources_requests [0 ].params is not None
326+ assert list_resources_requests [0 ].params ["cursor" ] == "some_cursor"
327+
328+
329+ async def test_list_prompts_params_parameter (stream_spy : Callable [[], StreamSpyCollection ]):
330+ """Test that the params parameter works correctly for list_prompts.
331+
332+ This tests the new params parameter API (non-deprecated) to ensure
333+ it correctly handles all parameter combinations.
334+ """
335+ server = FastMCP ("test" )
336+
337+ # Create a test prompt
338+ @server .prompt ()
339+ async def test_prompt (name : str ) -> str :
340+ """Test prompt"""
341+ return f"Hello, { name } !"
342+
343+ async with create_session (server ._mcp_server ) as client_session :
344+ spies = stream_spy ()
345+
346+ # Test without params parameter (omitted)
347+ _ = await client_session .list_prompts ()
348+ list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
349+ assert len (list_prompts_requests ) == 1
350+ assert list_prompts_requests [0 ].params is None
351+
352+ spies .clear ()
353+
354+ # Test with params=None
355+ _ = await client_session .list_prompts (params = None )
356+ list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
357+ assert len (list_prompts_requests ) == 1
358+ assert list_prompts_requests [0 ].params is None
359+
360+ spies .clear ()
361+
362+ # Test with empty params (for strict servers)
363+ _ = await client_session .list_prompts (params = types .PaginatedRequestParams ())
364+ list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
365+ assert len (list_prompts_requests ) == 1
366+ assert list_prompts_requests [0 ].params is not None
367+ assert list_prompts_requests [0 ].params .get ("cursor" ) is None
368+
369+ spies .clear ()
370+
371+ # Test with params containing cursor
372+ _ = await client_session .list_prompts (params = types .PaginatedRequestParams (cursor = "some_cursor" ))
373+ list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
374+ assert len (list_prompts_requests ) == 1
375+ assert list_prompts_requests [0 ].params is not None
376+ assert list_prompts_requests [0 ].params ["cursor" ] == "some_cursor"
377+
378+
379+ async def test_list_resource_templates_params_parameter (stream_spy : Callable [[], StreamSpyCollection ]):
380+ """Test that the params parameter works correctly for list_resource_templates.
381+
382+ This tests the new params parameter API (non-deprecated) to ensure
383+ it correctly handles all parameter combinations.
384+ """
385+ server = FastMCP ("test" )
386+
387+ # Create a test resource template
388+ @server .resource ("resource://test/{name}" )
389+ async def test_template (name : str ) -> str :
390+ """Test resource template"""
391+ return f"Data for { name } "
392+
393+ async with create_session (server ._mcp_server ) as client_session :
394+ spies = stream_spy ()
395+
396+ # Test without params parameter (omitted)
397+ _ = await client_session .list_resource_templates ()
398+ list_templates_requests = spies .get_client_requests (method = "resources/templates/list" )
399+ assert len (list_templates_requests ) == 1
400+ assert list_templates_requests [0 ].params is None
401+
402+ spies .clear ()
403+
404+ # Test with params=None
405+ _ = await client_session .list_resource_templates (params = None )
406+ list_templates_requests = spies .get_client_requests (method = "resources/templates/list" )
407+ assert len (list_templates_requests ) == 1
408+ assert list_templates_requests [0 ].params is None
409+
410+ spies .clear ()
411+
412+ # Test with empty params (for strict servers)
413+ _ = await client_session .list_resource_templates (params = types .PaginatedRequestParams ())
414+ list_templates_requests = spies .get_client_requests (method = "resources/templates/list" )
415+ assert len (list_templates_requests ) == 1
416+ assert list_templates_requests [0 ].params is not None
417+ assert list_templates_requests [0 ].params .get ("cursor" ) is None
418+
419+ spies .clear ()
420+
421+ # Test with params containing cursor
422+ _ = await client_session .list_resource_templates (params = types .PaginatedRequestParams (cursor = "some_cursor" ))
423+ list_templates_requests = spies .get_client_requests (method = "resources/templates/list" )
424+ assert len (list_templates_requests ) == 1
425+ assert list_templates_requests [0 ].params is not None
426+ assert list_templates_requests [0 ].params ["cursor" ] == "some_cursor"
427+
428+
429+ @pytest .mark .filterwarnings ("ignore::DeprecationWarning" )
430+ async def test_list_tools_params_takes_precedence_over_cursor (
431+ stream_spy : Callable [[], StreamSpyCollection ],
432+ ):
433+ """Test that params parameter takes precedence over cursor parameter.
434+
435+ When both cursor and params are provided, params should be used and
436+ cursor should be ignored, ensuring safe migration path.
437+ """
438+ server = FastMCP ("test" )
439+
440+ @server .tool (name = "test_tool" )
441+ async def test_tool () -> str :
442+ """Test tool"""
443+ return "Result"
444+
445+ async with create_session (server ._mcp_server ) as client_session :
446+ spies = stream_spy ()
447+
448+ # Call with both cursor and params - params should take precedence
449+ _ = await client_session .list_tools (
450+ cursor = "old_cursor" ,
451+ params = types .PaginatedRequestParams (cursor = "new_cursor" ),
452+ )
453+ list_tools_requests = spies .get_client_requests (method = "tools/list" )
454+ assert len (list_tools_requests ) == 1
455+ # Verify params takes precedence (new_cursor should be used, not old_cursor)
456+ assert list_tools_requests [0 ].params is not None
457+ assert list_tools_requests [0 ].params ["cursor" ] == "new_cursor"
458+
459+
460+ @pytest .mark .filterwarnings ("ignore::DeprecationWarning" )
461+ async def test_list_resources_params_takes_precedence_over_cursor (
462+ stream_spy : Callable [[], StreamSpyCollection ],
463+ ):
464+ """Test that params parameter takes precedence over cursor parameter.
465+
466+ When both cursor and params are provided, params should be used and
467+ cursor should be ignored, ensuring safe migration path.
468+ """
469+ server = FastMCP ("test" )
470+
471+ @server .resource ("resource://test/data" )
472+ async def test_resource () -> str :
473+ """Test resource"""
474+ return "Test data"
475+
476+ async with create_session (server ._mcp_server ) as client_session :
477+ spies = stream_spy ()
478+
479+ # Call with both cursor and params - params should take precedence
480+ _ = await client_session .list_resources (
481+ cursor = "old_cursor" ,
482+ params = types .PaginatedRequestParams (cursor = "new_cursor" ),
483+ )
484+ list_resources_requests = spies .get_client_requests (method = "resources/list" )
485+ assert len (list_resources_requests ) == 1
486+ # Verify params takes precedence (new_cursor should be used, not old_cursor)
487+ assert list_resources_requests [0 ].params is not None
488+ assert list_resources_requests [0 ].params ["cursor" ] == "new_cursor"
489+
490+
491+ @pytest .mark .filterwarnings ("ignore::DeprecationWarning" )
492+ async def test_list_prompts_params_takes_precedence_over_cursor (
493+ stream_spy : Callable [[], StreamSpyCollection ],
494+ ):
495+ """Test that params parameter takes precedence over cursor parameter.
496+
497+ When both cursor and params are provided, params should be used and
498+ cursor should be ignored, ensuring safe migration path.
499+ """
500+ server = FastMCP ("test" )
501+
502+ @server .prompt ()
503+ async def test_prompt (name : str ) -> str :
504+ """Test prompt"""
505+ return f"Hello, { name } !"
506+
507+ async with create_session (server ._mcp_server ) as client_session :
508+ spies = stream_spy ()
509+
510+ # Call with both cursor and params - params should take precedence
511+ _ = await client_session .list_prompts (
512+ cursor = "old_cursor" ,
513+ params = types .PaginatedRequestParams (cursor = "new_cursor" ),
514+ )
515+ list_prompts_requests = spies .get_client_requests (method = "prompts/list" )
516+ assert len (list_prompts_requests ) == 1
517+ # Verify params takes precedence (new_cursor should be used, not old_cursor)
518+ assert list_prompts_requests [0 ].params is not None
519+ assert list_prompts_requests [0 ].params ["cursor" ] == "new_cursor"
520+
521+
522+ @pytest .mark .filterwarnings ("ignore::DeprecationWarning" )
523+ async def test_list_resource_templates_params_takes_precedence_over_cursor (
524+ stream_spy : Callable [[], StreamSpyCollection ],
525+ ):
526+ """Test that params parameter takes precedence over cursor parameter.
527+
528+ When both cursor and params are provided, params should be used and
529+ cursor should be ignored, ensuring safe migration path.
530+ """
531+ server = FastMCP ("test" )
532+
533+ @server .resource ("resource://test/{name}" )
534+ async def test_template (name : str ) -> str :
535+ """Test resource template"""
536+ return f"Data for { name } "
537+
538+ async with create_session (server ._mcp_server ) as client_session :
539+ spies = stream_spy ()
540+
541+ # Call with both cursor and params - params should take precedence
542+ _ = await client_session .list_resource_templates (
543+ cursor = "old_cursor" ,
544+ params = types .PaginatedRequestParams (cursor = "new_cursor" ),
545+ )
546+ list_templates_requests = spies .get_client_requests (
547+ method = "resources/templates/list"
548+ )
549+ assert len (list_templates_requests ) == 1
550+ # Verify params takes precedence (new_cursor should be used, not old_cursor)
551+ assert list_templates_requests [0 ].params is not None
552+ assert list_templates_requests [0 ].params ["cursor" ] == "new_cursor"
553+
554+
224555async def test_list_tools_with_strict_server_validation ():
225556 """Test that list_tools works with strict servers require a params field,
226557 even if it is empty.
0 commit comments