1- """Completion interactions against the low-level Server, driven through the public Client API."""
1+ """Completion interactions against the low-level Server, driven through the public client API."""
22
33import pytest
44from inline_snapshot import snapshot
55
6- from mcp import MCPError , types
7- from mcp .server import Server , ServerRequestContext
6+ from mcp import McpError
7+ from mcp .server . lowlevel import Server
88from mcp .types import (
99 INVALID_PARAMS ,
1010 METHOD_NOT_FOUND ,
1111 CompleteResult ,
1212 Completion ,
13+ CompletionArgument ,
14+ CompletionContext ,
1315 ErrorData ,
1416 PromptReference ,
1517 ResourceTemplateReference ,
@@ -27,16 +29,20 @@ async def test_complete_prompt_argument(connect: Connect) -> None:
2729
2830 The returned values are filtered by the argument's value, proving the value reached the handler.
2931 """
30-
31- async def completion (ctx : ServerRequestContext , params : types .CompleteRequestParams ) -> CompleteResult :
32- assert isinstance (params .ref , PromptReference )
33- assert params .ref .name == "code_review"
34- assert params .argument .name == "language"
32+ server = Server ("completer" )
33+
34+ @server .completion ()
35+ async def completion (
36+ ref : PromptReference | ResourceTemplateReference ,
37+ argument : CompletionArgument ,
38+ context : CompletionContext | None ,
39+ ) -> Completion | None :
40+ assert isinstance (ref , PromptReference )
41+ assert ref .name == "code_review"
42+ assert argument .name == "language"
3543 candidates = ["python" , "pytorch" , "ruby" ]
36- matches = [candidate for candidate in candidates if candidate .startswith (params .argument .value )]
37- return CompleteResult (completion = Completion (values = matches , total = len (matches ), hasMore = False ))
38-
39- server = Server ("completer" , on_completion = completion )
44+ matches = [candidate for candidate in candidates if candidate .startswith (argument .value )]
45+ return Completion (values = matches , total = len (matches ), hasMore = False )
4046
4147 async with connect (server ) as client :
4248 result = await client .complete (
@@ -51,14 +57,18 @@ async def completion(ctx: ServerRequestContext, params: types.CompleteRequestPar
5157@requirement ("completion:resource-template-arg" )
5258async def test_complete_resource_template_variable (connect : Connect ) -> None :
5359 """Completing a URI template variable delivers the template URI and variable name to the handler."""
54-
55- async def completion (ctx : ServerRequestContext , params : types .CompleteRequestParams ) -> CompleteResult :
56- assert isinstance (params .ref , ResourceTemplateReference )
57- assert params .ref .uri == "github://repos/{owner}/{repo}"
58- assert params .argument .name == "owner"
59- return CompleteResult (completion = Completion (values = [f"{ params .argument .value } contextprotocol" ]))
60-
61- server = Server ("completer" , on_completion = completion )
60+ server = Server ("completer" )
61+
62+ @server .completion ()
63+ async def completion (
64+ ref : PromptReference | ResourceTemplateReference ,
65+ argument : CompletionArgument ,
66+ context : CompletionContext | None ,
67+ ) -> Completion | None :
68+ assert isinstance (ref , ResourceTemplateReference )
69+ assert ref .uri == "github://repos/{owner}/{repo}"
70+ assert argument .name == "owner"
71+ return Completion (values = [f"{ argument .value } contextprotocol" ])
6272
6373 async with connect (server ) as client :
6474 result = await client .complete (
@@ -75,14 +85,18 @@ async def test_complete_receives_context_arguments(connect: Connect) -> None:
7585
7686 The returned value is derived from the context, proving it arrived.
7787 """
78-
79- async def completion (ctx : ServerRequestContext , params : types .CompleteRequestParams ) -> CompleteResult :
80- assert params .argument .name == "repo"
81- assert params .context is not None
82- assert params .context .arguments is not None
83- return CompleteResult (completion = Completion (values = [f"{ params .context .arguments ['owner' ]} /python-sdk" ]))
84-
85- server = Server ("completer" , on_completion = completion )
88+ server = Server ("completer" )
89+
90+ @server .completion ()
91+ async def completion (
92+ ref : PromptReference | ResourceTemplateReference ,
93+ argument : CompletionArgument ,
94+ context : CompletionContext | None ,
95+ ) -> Completion | None :
96+ assert argument .name == "repo"
97+ assert context is not None
98+ assert context .arguments is not None
99+ return Completion (values = [f"{ context .arguments ['owner' ]} /python-sdk" ])
86100
87101 async with connect (server ) as client :
88102 result = await client .complete (
@@ -102,15 +116,19 @@ async def test_completion_against_an_unknown_ref_is_rejected_with_invalid_params
102116 against); rejecting an unknown ref is the handler's job, and this test pins the spec-recommended
103117 way to do it.
104118 """
119+ server = Server ("completer" )
105120
106- async def completion (ctx : ServerRequestContext , params : types .CompleteRequestParams ) -> CompleteResult :
107- assert isinstance (params .ref , PromptReference )
108- raise MCPError (code = INVALID_PARAMS , message = f"Unknown prompt: { params .ref .name !r} " )
109-
110- server = Server ("completer" , on_completion = completion )
121+ @server .completion ()
122+ async def completion (
123+ ref : PromptReference | ResourceTemplateReference ,
124+ argument : CompletionArgument ,
125+ context : CompletionContext | None ,
126+ ) -> Completion | None :
127+ assert isinstance (ref , PromptReference )
128+ raise McpError (ErrorData (code = INVALID_PARAMS , message = f"Unknown prompt: { ref .name !r} " ))
111129
112130 async with connect (server ) as client :
113- with pytest .raises (MCPError ) as exc_info :
131+ with pytest .raises (McpError ) as exc_info :
114132 await client .complete (PromptReference (type = "ref/prompt" , name = "ghost" ), argument = {"name" : "x" , "value" : "" })
115133
116134 assert exc_info .value .error .code == INVALID_PARAMS
@@ -123,9 +141,11 @@ async def test_complete_without_handler_is_method_not_found(connect: Connect) ->
123141 server = Server ("incomplete" )
124142
125143 async with connect (server ) as client :
126- assert client .initialize_result .capabilities .completions is None
144+ capabilities = client .get_server_capabilities ()
145+ assert capabilities is not None
146+ assert capabilities .completions is None
127147
128- with pytest .raises (MCPError ) as exc_info :
148+ with pytest .raises (McpError ) as exc_info :
129149 await client .complete (
130150 PromptReference (type = "ref/prompt" , name = "anything" ), argument = {"name" : "topic" , "value" : "" }
131151 )
0 commit comments