Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions fastmcp_slim/fastmcp/utilities/openapi/director.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,13 @@ def _serialize_query_params(
if not value:
continue
if explode:
# form,explode=true on objects: each property becomes
# a separate query parameter.
# e.g. {"R": 100, "G": 200} → R=100&G=200
for k, v in value.items():
serialized[_query_scalar_to_str(k)] = _query_scalar_to_str(
v
)
# deepObject keeps the parent parameter name;
# form style emits each property as a bare key.
property_name = _query_scalar_to_str(k)
if param_info.style == "deepObject":
property_name = f"{key}[{property_name}]"
serialized[property_name] = _query_scalar_to_str(v)
else:
style = param_info.style or "form"
delimiter = self._STYLE_DELIMITERS.get(style, ",")
Expand Down
39 changes: 39 additions & 0 deletions tests/utilities/openapi/test_director.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,45 @@ def test_explode_true_dict_expands_to_separate_params(self, director):
assert "myAttribute=true" in url
assert "data=" not in url

def test_deep_object_explode_true_uses_bracket_notation(self, director):
route = HTTPRoute(
path="/items",
method="GET",
operation_id="list_items",
parameters=[
ParameterInfo(
name="filter",
location="query",
required=True,
schema={
"type": "object",
"properties": {
"eq": {"type": "string"},
"display name": {"type": "string"},
},
},
explode=True,
style="deepObject",
)
],
parameter_map={
"filter": {"location": "query", "openapi_name": "filter"},
},
)

request = director.build(
route,
{"filter": {"eq": "foo/bar", "display name": "active & ready"}},
"https://example.com",
)

assert request.url.params["filter[eq]"] == "foo/bar"
assert request.url.params["filter[display name]"] == "active & ready"
assert "filter%5Beq%5D=foo%2Fbar" in str(request.url)
assert "filter%5Bdisplay+name%5D=active+%26+ready" in str(request.url)
assert "eq" not in request.url.params
assert "display name" not in request.url.params

def test_explode_default_dict_expands_to_separate_params(self, director):
"""Default explode (None → true) on objects expands properties."""
route = HTTPRoute(
Expand Down
Loading