@@ -625,6 +625,55 @@ async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams |
625625 assert response .status_code == 200
626626
627627
628+ async def test_accept_q0_for_the_required_type_is_not_acceptable () -> None :
629+ """RFC 7231 5.3.1: a media-range weighted q=0 is explicitly "not acceptable" -- a client
630+ that sends `application/json;q=0` has ruled JSON out even though it's the only other type
631+ on the line, so a JSON-mode request must get 406, not 200."""
632+
633+ async def list_tools (ctx : ServerRequestContext , params : PaginatedRequestParams | None ) -> ListToolsResult :
634+ return ListToolsResult (tools = [], ttl_ms = 0 , cache_scope = "public" ) # pragma: no cover
635+
636+ async with _asgi_client (
637+ Server ("test" , on_list_tools = list_tools ),
638+ json_response = True ,
639+ accept = "application/json;q=0, text/event-stream;q=1.0" ,
640+ ) as http :
641+ response = await http .post ("/mcp" , json = _list_tools_body (), headers = {MCP_METHOD_HEADER : "tools/list" })
642+ assert response .status_code == 406
643+
644+
645+ async def test_accept_q0_for_a_specific_type_overrides_a_present_wildcard () -> None :
646+ """A specific "not acceptable" entry is more specific than `*/*` and must win: the client
647+ is still rejecting JSON even though it also (redundantly) accepts everything."""
648+
649+ async def list_tools (ctx : ServerRequestContext , params : PaginatedRequestParams | None ) -> ListToolsResult :
650+ return ListToolsResult (tools = [], ttl_ms = 0 , cache_scope = "public" ) # pragma: no cover
651+
652+ async with _asgi_client (
653+ Server ("test" , on_list_tools = list_tools ),
654+ json_response = True ,
655+ accept = "application/json;q=0, */*" ,
656+ ) as http :
657+ response = await http .post ("/mcp" , json = _list_tools_body (), headers = {MCP_METHOD_HEADER : "tools/list" })
658+ assert response .status_code == 406
659+
660+
661+ async def test_accept_nonzero_q_for_the_required_type_is_still_acceptable () -> None :
662+ """A low but nonzero weight is still a "yes" -- q only gates acceptability at 0, this SDK
663+ doesn't rank multiple acceptable representations by preference."""
664+
665+ async def list_tools (ctx : ServerRequestContext , params : PaginatedRequestParams | None ) -> ListToolsResult :
666+ return ListToolsResult (tools = [], ttl_ms = 0 , cache_scope = "public" )
667+
668+ async with _asgi_client (
669+ Server ("test" , on_list_tools = list_tools ),
670+ json_response = True ,
671+ accept = "application/json;q=0.1" ,
672+ ) as http :
673+ response = await http .post ("/mcp" , json = _list_tools_body (), headers = {MCP_METHOD_HEADER : "tools/list" })
674+ assert response .status_code == 200
675+
676+
628677async def test_late_notify_after_terminal_dropped () -> None :
629678 """SDK-defined: a `notify()` after the SSE sink has closed is silently dropped — the closed
630679 stream must not propagate as an exception out of the dispatch context."""
0 commit comments