diff --git a/src/fastmcp/utilities/openapi/director.py b/src/fastmcp/utilities/openapi/director.py index c3225af92..b0b29f0ba 100644 --- a/src/fastmcp/utilities/openapi/director.py +++ b/src/fastmcp/utilities/openapi/director.py @@ -75,14 +75,19 @@ class RequestDirector: json_body: dict[str, Any] | list[Any] | None = None content: str | bytes | None = None - # Step 5: Determine the declared content type from the OpenAPI spec + # Step 5: Determine the declared content type from the OpenAPI spec. + # Strip parameters (e.g. "; charset=utf-8") for dispatch matching. declared_content_type: str | None = None if route.request_body and route.request_body.content_schema: - declared_content_type = next(iter(route.request_body.content_schema)) + raw_ct = next(iter(route.request_body.content_schema)) + declared_content_type = raw_ct.split(";")[0].strip().lower() - # httpx requires cookie values to be strings + # httpx requires cookie values to be strings; use OpenAPI-style + # serialization (e.g. true/false for booleans, not True/False) cookies = ( - {k: str(v) for k, v in cookie_params.items()} if cookie_params else None + {k: _query_scalar_to_str(v) for k, v in cookie_params.items()} + if cookie_params + else None ) # Step 6: Handle request body — dispatch on declared content type. diff --git a/tests/utilities/openapi/test_director.py b/tests/utilities/openapi/test_director.py index 82035a79c..acd50425f 100644 --- a/tests/utilities/openapi/test_director.py +++ b/tests/utilities/openapi/test_director.py @@ -1403,7 +1403,7 @@ class TestCookieParameters: assert "xyz789" in request.headers.get("cookie", "") def test_cookie_non_string_value_stringified(self, director): - """Non-string cookie values (e.g. int) must be stringified for httpx.""" + """Non-string cookie values (e.g. int, bool) use OpenAPI serialization.""" route = HTTPRoute( path="/api", method="GET", @@ -1415,11 +1415,20 @@ class TestCookieParameters: required=True, schema={"type": "integer"}, ), + ParameterInfo( + name="debug", + location="cookie", + required=False, + schema={"type": "boolean"}, + ), ], parameter_map={ "version": {"location": "cookie", "openapi_name": "version"}, + "debug": {"location": "cookie", "openapi_name": "debug"}, }, ) - request = director.build(route, {"version": 3}) - assert "version" in request.headers.get("cookie", "") - assert "3" in request.headers.get("cookie", "") + request = director.build(route, {"version": 3, "debug": True}) + cookie = request.headers.get("cookie", "") + assert "version=3" in cookie + # Booleans use OpenAPI convention (true/false, not True/False) + assert "debug=true" in cookie