@@ -1849,6 +1849,57 @@ async def handle_completion(
18491849 assert result .completion .values == ["bold" , "italic" , "underline" ]
18501850
18511851
1852+ async def test_completion_handler_more_than_100_values_paginates () -> None :
1853+ """Issue #3649: When a completion handler returns >100 values, MCPServer caps values at 100
1854+ and sets total to the full count and has_more=True across both modern and legacy sessions.
1855+ """
1856+ mcp = MCPServer ()
1857+
1858+ @mcp .completion ()
1859+ async def complete (
1860+ ref : PromptReference , argument : CompletionArgument , context : CompletionContext | None
1861+ ) -> Completion :
1862+ return Completion (values = [f"option-{ i :03} " for i in range (150 )])
1863+
1864+ ref = PromptReference (type = "ref/prompt" , name = "test" )
1865+
1866+ # auto mode (2026-07-28 protocol with 100-item Field constraint)
1867+ async with Client (mcp , mode = "auto" ) as client :
1868+ result = await client .complete (ref = ref , argument = {"name" : "field" , "value" : "" })
1869+ assert len (result .completion .values ) == 100
1870+ assert result .completion .values [0 ] == "option-000"
1871+ assert result .completion .values [- 1 ] == "option-099"
1872+ assert result .completion .total == 150
1873+ assert result .completion .has_more is True
1874+
1875+ # legacy mode (2025-11-25 protocol)
1876+ async with Client (mcp , mode = "legacy" ) as client :
1877+ result = await client .complete (ref = ref , argument = {"name" : "field" , "value" : "" })
1878+ assert len (result .completion .values ) == 100
1879+ assert result .completion .values [0 ] == "option-000"
1880+ assert result .completion .values [- 1 ] == "option-099"
1881+ assert result .completion .total == 150
1882+ assert result .completion .has_more is True
1883+
1884+
1885+ async def test_completion_handler_more_than_100_values_preserves_explicit_total_and_has_more () -> None :
1886+ """Issue #3649: When a handler returns >100 values with explicit total or has_more, those values win."""
1887+ mcp = MCPServer ()
1888+
1889+ @mcp .completion ()
1890+ async def complete (
1891+ ref : PromptReference , argument : CompletionArgument , context : CompletionContext | None
1892+ ) -> Completion :
1893+ return Completion (values = [f"item-{ i } " for i in range (120 )], total = 500 , has_more = False )
1894+
1895+ ref = PromptReference (type = "ref/prompt" , name = "test" )
1896+ async with Client (mcp ) as client :
1897+ result = await client .complete (ref = ref , argument = {"name" : "field" , "value" : "" })
1898+ assert len (result .completion .values ) == 100
1899+ assert result .completion .total == 500
1900+ assert result .completion .has_more is False
1901+
1902+
18521903async def test_custom_resource_returning_the_wrong_type_is_a_crash (caplog : pytest .LogCaptureFixture ) -> None :
18531904 """SDK-defined: a Resource subclass whose read() returns something other than str or bytes is the
18541905 server's bug, so it is logged as a crash and answered with the generic -32603, not with
0 commit comments