@@ -304,7 +304,7 @@ async def __aexit__(
304304
305305 async def send_request (
306306 self ,
307- request : types .ClientRequest ,
307+ request : types .ClientRequest | types . Request [ Any , Any ] ,
308308 result_type : type [ReceiveResultT ] | TypeAdapter [ReceiveResultT ],
309309 request_read_timeout_seconds : float | None = None ,
310310 metadata : ClientMessageMetadata | None = None ,
@@ -318,11 +318,22 @@ async def send_request(
318318 Raises:
319319 MCPError: Error response, read timeout, or connection closed.
320320 RuntimeError: Called before entering the context manager.
321+ ValueError: The request type declares `name_param` but the params
322+ carry no string value under that key for the `Mcp-Name` header.
321323 """
322324 data = request .model_dump (by_alias = True , mode = "json" , exclude_none = True )
323325 method : str = data ["method" ]
324326 opts : CallOptions = {}
325327 self ._stamp (data , opts )
328+ # Presence-keyed so the stamp's NAME_BEARING_METHODS rows win by ordering;
329+ # a missing/non-string name fails loud rather than omitting a MUST header.
330+ headers = opts .setdefault ("headers" , {})
331+ if (key := type (request ).name_param ) is not None and MCP_NAME_HEADER not in headers :
332+ params_data : dict [str , Any ] = data .get ("params" ) or {}
333+ name = params_data .get (key )
334+ if not isinstance (name , str ):
335+ raise ValueError (f"{ method } requires params[{ key !r} ] for Mcp-Name" )
336+ headers [MCP_NAME_HEADER ] = encode_header_value (name )
326337 timeout = (
327338 request_read_timeout_seconds
328339 if request_read_timeout_seconds is not None
@@ -766,7 +777,7 @@ async def call_tool(
766777 )
767778
768779 if isinstance (result , types .CallToolResult ) and not result .is_error :
769- await self ._validate_tool_result (name , result )
780+ await self .validate_tool_result (name , result )
770781
771782 if isinstance (result , types .InputRequiredResult ) and not allow_input_required :
772783 raise _input_required_unexpected ("call_tool" )
@@ -779,8 +790,17 @@ def _resolve_param_headers(self, name: str, arguments: Mapping[str, Any]) -> dic
779790 return {}
780791 return mcp_param_headers (header_map , arguments )
781792
782- async def _validate_tool_result (self , name : str , result : types .CallToolResult ) -> None :
783- """Validate the structured content of a tool result against its output schema."""
793+ async def validate_tool_result (self , name : str , result : types .CallToolResult ) -> None :
794+ """Revalidate a `CallToolResult` against the tool's declared output schema.
795+
796+ Fetches the tool listing first when `name` has no cached schema. Tools
797+ without an output schema (or not listed by the server) pass without
798+ validation.
799+
800+ Raises:
801+ RuntimeError: The result's structured content is missing or does
802+ not conform to the tool's output schema.
803+ """
784804 if name not in self ._tool_output_schemas :
785805 # refresh output schema cache
786806 await self .list_tools ()
@@ -970,7 +990,7 @@ async def _on_request(
970990 ctx = ClientRequestContext (
971991 session = self , request_id = dctx .request_id , meta = request .params .meta if request .params else None
972992 )
973- response = await self ._dispatch_input_request (ctx , request )
993+ response = await self .dispatch_input_request (ctx , request )
974994 client_response = ClientResponse .validate_python (response )
975995 if isinstance (client_response , types .ErrorData ):
976996 raise MCPError .from_error_data (client_response )
@@ -982,16 +1002,19 @@ async def _on_request(
9821002 raise MCPError (code = INTERNAL_ERROR , message = "Client callback returned an invalid result" ) from None
9831003 return dumped
9841004
985- async def _dispatch_input_request (
986- self , ctx : ClientRequestContext , req : types .InputRequest
1005+ async def dispatch_input_request (
1006+ self , ctx : ClientRequestContext , request : types .InputRequest
9871007 ) -> types .InputResponse | types .ErrorData :
988- """Route a server-initiated input request to the matching constructor callback.
1008+ """Route an input request through the client's callback table .
9891009
9901010 Shared by the legacy server→client RPC path (`_on_request`) and the
9911011 2026-07-28 multi-round-trip driver, which dispatches the embedded
9921012 `InputRequiredResult.input_requests` through the same callbacks.
1013+
1014+ Returns the callback's `InputResponse`, or `ErrorData` when the
1015+ callback declines — the refusal path; callers must handle that arm.
9931016 """
994- match req :
1017+ match request :
9951018 case types .CreateMessageRequest (params = p ):
9961019 return await self ._sampling_callback (ctx , p )
9971020 case types .ElicitRequest (params = p ):
0 commit comments