Refactor array parameter formatting to reduce code duplication

Extract common array formatting logic into format_array_parameter utility function.
Addresses automated review feedback about code duplication between path and query parameter handling.
This commit is contained in:
Jeremiah Lowin 2025-07-01 19:15:24 -04:00
commit ff0c513611
2 changed files with 64 additions and 68 deletions

View file

@ -27,6 +27,7 @@ from fastmcp.utilities.logging import get_logger
from fastmcp.utilities.openapi import (
HTTPRoute,
_combine_schemas,
format_array_parameter,
format_description_with_responses,
)
@ -296,46 +297,10 @@ class OpenAPITool(Tool):
if is_array:
# Format array values as comma-separated string
# This follows the OpenAPI 'simple' style (default for path)
if all(
isinstance(item, str | int | float | bool)
for item in param_value
):
# Handle simple array types
path = path.replace(
f"{{{param_name}}}", ",".join(str(v) for v in param_value)
)
else:
# Handle complex array types (containing objects/dicts)
try:
# Try to create a simple representation without Python syntax artifacts
formatted_parts = []
for item in param_value:
if isinstance(item, dict):
# For objects, serialize key-value pairs
item_parts = []
for k, v in item.items():
item_parts.append(f"{k}:{v}")
formatted_parts.append(".".join(item_parts))
else:
# Fallback for other complex types
formatted_parts.append(str(item))
# Join parts with commas
formatted_value = ",".join(formatted_parts)
path = path.replace(f"{{{param_name}}}", formatted_value)
except Exception as e:
logger.warning(
f"Failed to format complex array path parameter '{param_name}': {e}"
)
# Fallback to string representation, but remove Python syntax artifacts
str_value = (
str(param_value)
.replace("[", "")
.replace("]", "")
.replace("'", "")
.replace('"', "")
)
path = path.replace(f"{{{param_name}}}", str_value)
formatted_value = format_array_parameter(
param_value, param_name, is_query_parameter=False
)
path = path.replace(f"{{{param_name}}}", str(formatted_value))
continue
# Default handling for non-array parameters or non-array schemas
@ -365,34 +330,11 @@ class OpenAPITool(Tool):
# as multiple parameters with the same name
query_params[p.name] = param_value
else:
# For arrays of simple types (strings, numbers, etc.), join with commas
if all(
isinstance(item, str | int | float | bool)
for item in param_value
):
query_params[p.name] = ",".join(str(v) for v in param_value)
else:
# For complex types, try to create a simpler representation
try:
# Try to create a simple string representation
formatted_parts = []
for item in param_value:
if isinstance(item, dict):
# For objects, serialize key-value pairs
item_parts = []
for k, v in item.items():
item_parts.append(f"{k}:{v}")
formatted_parts.append(".".join(item_parts))
else:
formatted_parts.append(str(item))
query_params[p.name] = ",".join(formatted_parts)
except Exception as e:
logger.warning(
f"Failed to format complex array query parameter '{p.name}': {e}"
)
# Fallback to string representation
query_params[p.name] = param_value
# Format array as comma-separated string when explode=False
formatted_value = format_array_parameter(
param_value, p.name, is_query_parameter=True
)
query_params[p.name] = formatted_value
else:
# Non-array parameters are passed as is
query_params[p.name] = param_value

View file

@ -39,6 +39,60 @@ ParameterLocation = Literal["path", "query", "header", "cookie"]
JsonSchema = dict[str, Any]
def format_array_parameter(
values: list, parameter_name: str, is_query_parameter: bool = False
) -> str | list:
"""
Format an array parameter according to OpenAPI specifications.
Args:
values: List of values to format
parameter_name: Name of the parameter (for error messages)
is_query_parameter: If True, can return list for explode=True behavior
Returns:
String (comma-separated) or list (for query params with explode=True)
"""
# For arrays of simple types (strings, numbers, etc.), join with commas
if all(isinstance(item, str | int | float | bool) for item in values):
return ",".join(str(v) for v in values)
# For complex types, try to create a simpler representation
try:
# Try to create a simple string representation
formatted_parts = []
for item in values:
if isinstance(item, dict):
# For objects, serialize key-value pairs
item_parts = []
for k, v in item.items():
item_parts.append(f"{k}:{v}")
formatted_parts.append(".".join(item_parts))
else:
formatted_parts.append(str(item))
return ",".join(formatted_parts)
except Exception as e:
param_type = "query" if is_query_parameter else "path"
logger.warning(
f"Failed to format complex array {param_type} parameter '{parameter_name}': {e}"
)
if is_query_parameter:
# For query parameters, fallback to original list
return values
else:
# For path parameters, fallback to string representation without Python syntax
str_value = (
str(values)
.replace("[", "")
.replace("]", "")
.replace("'", "")
.replace('"', "")
)
return str_value
class ParameterInfo(FastMCPBaseModel):
"""Represents a single parameter for an HTTP operation in our IR."""