@@ -198,42 +198,6 @@ def _wire_key(fn: Callable[..., Any]) -> str:
198198 return f"{ fn .__module__ } :{ fn .__qualname__ } "
199199
200200
201- @pytest .mark .anyio
202- async def test_resolver_returns_value_directly_without_eliciting ():
203- mcp = MCPServer (name = "Direct" , request_state_security = RequestStateSecurity .ephemeral ())
204-
205- async def login (ctx : Context ) -> Login | Elicit [Login ]:
206- username = (ctx .headers or {}).get ("x-github-user" )
207- if username : # pragma: no cover - no headers on in-memory transport
208- return Login (username = username )
209- return Login (username = "from-resolver" )
210-
211- @mcp .tool ()
212- async def whoami (login : Annotated [Login , Resolve (login )]) -> str :
213- return login .username
214-
215- async def never (context : ClientRequestContext , params : ElicitRequestParams ) -> ElicitResult : # pragma: no cover
216- raise AssertionError ("should not elicit" )
217-
218- async with Client (mcp , mode = "legacy" , elicitation_callback = never ) as client :
219- assert await _text (client , "whoami" , {}) == "from-resolver"
220-
221-
222- @pytest .mark .anyio
223- async def test_resolver_elicits_and_injects_unwrapped_model_on_accept ():
224- mcp = MCPServer (name = "Accept" , request_state_security = RequestStateSecurity .ephemeral ())
225-
226- async def login (ctx : Context ) -> Login | Elicit [Login ]:
227- return Elicit ("GitHub username?" , Login )
228-
229- @mcp .tool ()
230- async def whoami (login : Annotated [Login , Resolve (login )]) -> str :
231- return login .username
232-
233- async with Client (mcp , mode = "legacy" , elicitation_callback = _accept ({"username" : "octocat" })) as client :
234- assert await _text (client , "whoami" , {}) == "octocat"
235-
236-
237201@pytest .mark .anyio
238202async def test_consumer_receives_result_union_and_branches ():
239203 mcp = MCPServer (name = "Union" , request_state_security = RequestStateSecurity .ephemeral ())
@@ -253,43 +217,6 @@ async def whoami(login: Annotated[ElicitationResult[Login], Resolve(login)]) ->
253217 assert await _text (client , "whoami" , {}) == "hi octocat"
254218
255219
256- @pytest .mark .anyio
257- async def test_decline_reaches_union_consumer_without_aborting ():
258- mcp = MCPServer (name = "UnionDecline" , request_state_security = RequestStateSecurity .ephemeral ())
259-
260- async def login (ctx : Context ) -> Login | Elicit [Login ]:
261- return Elicit ("GitHub username?" , Login )
262-
263- @mcp .tool ()
264- async def whoami (
265- login : Annotated [AcceptedElicitation [Login ] | DeclinedElicitation | CancelledElicitation , Resolve (login )],
266- ) -> str :
267- if isinstance (login , DeclinedElicitation ):
268- return "declined gracefully"
269- raise NotImplementedError
270-
271- async with Client (mcp , mode = "legacy" , elicitation_callback = _decline ) as client :
272- assert await _text (client , "whoami" , {}) == "declined gracefully"
273-
274-
275- @pytest .mark .anyio
276- async def test_decline_aborts_when_consumer_wants_unwrapped ():
277- mcp = MCPServer (name = "UnwrappedDecline" , request_state_security = RequestStateSecurity .ephemeral ())
278-
279- async def login (ctx : Context ) -> Login | Elicit [Login ]:
280- return Elicit ("GitHub username?" , Login )
281-
282- @mcp .tool ()
283- async def whoami (login : Annotated [Login , Resolve (login )]) -> str :
284- raise NotImplementedError # pragma: no cover - never reached
285-
286- async with Client (mcp , mode = "legacy" , elicitation_callback = _decline ) as client :
287- result = await client .call_tool ("whoami" , {})
288- assert result .is_error
289- assert isinstance (result .content [0 ], TextContent )
290- assert "decline" in result .content [0 ].text
291-
292-
293220@pytest .mark .anyio
294221async def test_nested_resolver_sees_dependency_and_tool_args ():
295222 mcp = MCPServer (name = "Nested" , request_state_security = RequestStateSecurity .ephemeral ())
@@ -369,22 +296,6 @@ async def never(context: ClientRequestContext, params: ElicitRequestParams) -> E
369296 assert await _text (client , "whoami" , {}) == "sync-user"
370297
371298
372- def test_resolved_params_absent_from_input_schema ():
373- async def login (ctx : Context ) -> Login :
374- return Login (username = "x" ) # pragma: no cover - only the schema is inspected
375-
376- async def tool (
377- repo : Annotated [str , Field (description = "repo name" )],
378- login : Annotated [Login , Resolve (login )],
379- ) -> str :
380- return repo # pragma: no cover - only the schema is inspected
381-
382- built = Tool .from_function (tool )
383- properties = built .parameters ["properties" ]
384- assert "repo" in properties
385- assert "login" not in properties
386-
387-
388299def test_cycle_detection_raises_at_registration ():
389300 async def a (dep : Login ) -> Login :
390301 return dep # pragma: no cover
@@ -1140,28 +1051,6 @@ async def empty_accept(context: ClientRequestContext, params: ElicitRequestParam
11401051 assert "no content" in result .content [0 ].text
11411052
11421053
1143- @pytest .mark .anyio
1144- async def test_eliciting_tool_without_client_capability_is_a_protocol_error ():
1145- # The server must not send an `input_requests` entry the client has not declared
1146- # capability for: with no `elicitation` declared (no callback), the call fails as
1147- # a -32021 protocol error, not a CallToolResult execution failure.
1148- mcp = MCPServer (name = "NoElicitationCapability" , request_state_security = RequestStateSecurity .ephemeral ())
1149-
1150- async def ask (ctx : Context ) -> Elicit [Login ]:
1151- return Elicit ("user?" , Login )
1152-
1153- @mcp .tool ()
1154- async def tool (login : Annotated [Login , Resolve (ask )]) -> str :
1155- return login .username # pragma: no cover
1156-
1157- async with Client (mcp ) as client :
1158- with pytest .raises (MCPError ) as exc_info :
1159- await client .session .call_tool ("tool" , {}, allow_input_required = True )
1160- assert exc_info .value .code == MISSING_REQUIRED_CLIENT_CAPABILITY
1161- assert exc_info .value .error .data is not None
1162- assert "elicitation" in exc_info .value .error .data ["requiredCapabilities" ]
1163-
1164-
11651054@pytest .mark .anyio
11661055async def test_independent_nested_deps_batch_into_one_round ():
11671056 mcp = MCPServer (name = "NestedBatch" , request_state_security = RequestStateSecurity .ephemeral ())
@@ -2802,6 +2691,7 @@ async def quote(title: str, price: Annotated[int, Resolve(price_of)]) -> str:
28022691 async with Client (mcp ) as client :
28032692 result = await client .call_tool ("quote" , {"title" : "Dune" })
28042693
2694+ assert not result .is_error
28052695 assert result .content == [TextContent (type = "text" , text = "Dune: 42" )]
28062696
28072697
@@ -2824,6 +2714,7 @@ async def quote(title: str, total: Annotated[int, Resolve(with_tax)]) -> str:
28242714 async with Client (mcp ) as client :
28252715 result = await client .call_tool ("quote" , {"title" : "Dune" })
28262716
2717+ assert not result .is_error
28272718 assert result .content == [TextContent (type = "text" , text = "Dune costs 20" )]
28282719
28292720
@@ -2843,6 +2734,7 @@ async def quote(title: str, price: Annotated[int, Resolve(price_of)]) -> str:
28432734 async with Client (mcp ) as client :
28442735 result = await client .call_tool ("quote" , {"title" : "Dune" , "price" : 999 })
28452736
2737+ assert not result .is_error
28462738 assert result .content == [TextContent (type = "text" , text = "Dune: 42" )]
28472739
28482740
@@ -2853,11 +2745,11 @@ async def test_resolved_parameters_are_absent_from_the_advertised_tool_schema():
28532745 mcp = MCPServer (name = "Walk" , request_state_security = RequestStateSecurity .ephemeral ())
28542746
28552747 def price_of (title : str ) -> int :
2856- return 42 # pragma: no cover - only the schema is inspected
2748+ raise NotImplementedError # pragma: no cover - only the schema is inspected
28572749
28582750 @mcp .tool ()
28592751 async def quote (title : str , price : Annotated [int , Resolve (price_of )]) -> str :
2860- return f" { title } : { price } " # pragma: no cover - only the schema is inspected
2752+ raise NotImplementedError # pragma: no cover - only the schema is inspected
28612753
28622754 async with Client (mcp ) as client :
28632755 (advertised ,) = (await client .list_tools ()).tools
@@ -2879,11 +2771,6 @@ async def test_an_elicit_marker_asks_the_client_and_injects_the_accepted_answer(
28792771 callback and the tool receives the validated model, identically over the 2025
28802772 back-channel and the 2026 multi-round-trip flow. SDK-defined injection contract."""
28812773 mcp = MCPServer (name = "Walk" , request_state_security = RequestStateSecurity .ephemeral ())
2882- asked : list [str ] = []
2883-
2884- async def on_elicit (context : ClientRequestContext , params : ElicitRequestParams ) -> ElicitResult :
2885- asked .append (params .message )
2886- return ElicitResult (action = "accept" , content = {"username" : "octocat" })
28872774
28882775 def ask (ctx : Context ) -> Elicit [Login ]:
28892776 return Elicit ("GitHub username?" , Login )
@@ -2892,9 +2779,18 @@ def ask(ctx: Context) -> Elicit[Login]:
28922779 async def whoami (login : Annotated [Login , Resolve (ask )]) -> str :
28932780 return login .username
28942781
2782+ asked : list [str ] = []
2783+
2784+ async def on_elicit (context : ClientRequestContext , params : ElicitRequestParams ) -> ElicitResult :
2785+ asked .append (params .message )
2786+ return ElicitResult (action = "accept" , content = {"username" : "octocat" })
2787+
28952788 async with Client (mcp , mode = mode , elicitation_callback = on_elicit ) as client :
2789+ # The parametrize is only a real era matrix while the arms negotiate different revisions.
2790+ assert client .protocol_version == ("2025-11-25" if mode == "legacy" else "2026-07-28" )
28962791 result = await client .call_tool ("whoami" , {})
28972792
2793+ assert not result .is_error
28982794 assert result .content == [TextContent (type = "text" , text = "octocat" )]
28992795 assert asked == ["GitHub username?" ]
29002796
@@ -2907,28 +2803,21 @@ async def test_a_declined_elicitation_fails_the_call_for_a_plain_model_consumer(
29072803 the SDK's contract for plain-model consumers."""
29082804 mcp = MCPServer (name = "Walk" , request_state_security = RequestStateSecurity .ephemeral ())
29092805
2910- async def on_elicit (context : ClientRequestContext , params : ElicitRequestParams ) -> ElicitResult :
2911- return ElicitResult (action = "decline" )
2912-
29132806 def ask (ctx : Context ) -> Elicit [Login ]:
29142807 return Elicit ("GitHub username?" , Login )
29152808
29162809 @mcp .tool ()
29172810 async def whoami (login : Annotated [Login , Resolve (ask )]) -> str :
2918- return login . username # pragma: no cover - the decline aborts before the body
2811+ raise NotImplementedError # pragma: no cover - the decline aborts before the body
29192812
2920- async with Client (mcp , mode = mode , elicitation_callback = on_elicit ) as client :
2813+ async with Client (mcp , mode = mode , elicitation_callback = _decline ) as client :
29212814 result = await client .call_tool ("whoami" , {})
29222815
29232816 assert result .is_error
2924- assert result .content == [
2925- TextContent (
2926- type = "text" ,
2927- text = (
2928- "Error executing tool whoami: Resolver for parameter 'login' could not resolve: elicitation was decline"
2929- ),
2930- )
2931- ]
2817+ assert isinstance (result .content [0 ], TextContent )
2818+ assert result .content [0 ].text == snapshot (
2819+ "Error executing tool whoami: Resolver for parameter 'login' could not resolve: elicitation was decline"
2820+ )
29322821
29332822
29342823@pytest .mark .anyio
@@ -2940,9 +2829,6 @@ async def test_a_declined_elicitation_reaches_a_consumer_that_asked_for_the_outc
29402829 of aborting the call. SDK-defined consumer-annotation contract."""
29412830 mcp = MCPServer (name = "Walk" , request_state_security = RequestStateSecurity .ephemeral ())
29422831
2943- async def on_elicit (context : ClientRequestContext , params : ElicitRequestParams ) -> ElicitResult :
2944- return ElicitResult (action = "decline" )
2945-
29462832 def ask (ctx : Context ) -> Elicit [Login ]:
29472833 return Elicit ("GitHub username?" , Login )
29482834
@@ -2952,7 +2838,7 @@ async def whoami(login: Annotated[ElicitationResult[Login], Resolve(ask)]) -> st
29522838 return login .data .username # pragma: no cover - declined in this test
29532839 return "anonymous"
29542840
2955- async with Client (mcp , mode = mode , elicitation_callback = on_elicit ) as client :
2841+ async with Client (mcp , mode = mode , elicitation_callback = _decline ) as client :
29562842 result = await client .call_tool ("whoami" , {})
29572843
29582844 assert not result .is_error
@@ -2972,7 +2858,7 @@ def ask(ctx: Context) -> Elicit[Login]:
29722858
29732859 @mcp .tool ()
29742860 async def whoami (login : Annotated [Login , Resolve (ask )]) -> str :
2975- return login . username # pragma: no cover - the gate refuses before the body
2861+ raise NotImplementedError # pragma: no cover - the gate refuses before the body
29762862
29772863 async with Client (mcp , mode = mode ) as client :
29782864 with pytest .raises (MCPError ) as exc_info :
0 commit comments