Skip to content

Commit 10aa5ef

Browse files
committed
fix(mcpserver): paginate completion values exceeding 100 items (#3649)
1 parent 91941ed commit 10aa5ef

2 files changed

Lines changed: 64 additions & 1 deletion

File tree

‎src/mcp/server/mcpserver/server.py‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,8 +761,20 @@ async def handler(
761761
) -> CompleteResult:
762762
try:
763763
result = await func(params.ref, params.argument, params.context)
764+
if result is None:
765+
completion = Completion(values=[], total=None, has_more=None)
766+
elif hasattr(result, "values") and len(result.values) > 100:
767+
total = result.total if getattr(result, "total", None) is not None else len(result.values)
768+
has_more = result.has_more if getattr(result, "has_more", None) is not None else True
769+
completion = Completion(
770+
values=result.values[:100],
771+
total=total,
772+
has_more=has_more,
773+
)
774+
else:
775+
completion = result
764776
return CompleteResult(
765-
completion=result if result is not None else Completion(values=[], total=None, has_more=None),
777+
completion=completion,
766778
)
767779
except MCPError:
768780
raise

‎tests/server/mcpserver/test_server.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
18521903
async 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

Comments
 (0)