@@ -212,6 +212,60 @@ def normalize_vector3(value: Any, param_name: str = "vector") -> tuple[list[floa
212212 return None , f"{ param_name } must be a list, dict, or string, got { type (value ).__name__ } "
213213
214214
215+ def normalize_string_list (value : Any , param_name : str = "list" ) -> tuple [list [str ] | None , str | None ]:
216+ """
217+ Normalize a string list parameter that might be a JSON string or plain string.
218+
219+ Handles various input formats from MCP clients/LLMs:
220+ - None -> (None, None)
221+ - list/tuple of strings -> (list, None)
222+ - JSON string '["a", "b", "c"]' -> parsed and normalized
223+ - Plain non-JSON string "foo" -> treated as ["foo"]
224+
225+ Returns:
226+ Tuple of (parsed_list, error_message). If error_message is set, parsed_list is None.
227+ """
228+ if value is None :
229+ return None , None
230+
231+ # Already a list/tuple - validate and return
232+ if isinstance (value , (list , tuple )):
233+ # Ensure all elements are strings
234+ if all (isinstance (item , str ) for item in value ):
235+ return list (value ), None
236+ return None , f"{ param_name } must contain only strings, got mixed types"
237+
238+ # Try parsing as JSON string (immediate parsing for string input)
239+ if isinstance (value , str ):
240+ val_trimmed = value .strip ()
241+ # Check for obviously invalid values
242+ if val_trimmed in ("[object Object]" , "undefined" , "null" , "" ):
243+ return None , f"{ param_name } received invalid value: '{ value } '. Expected a JSON array like [\" item1\" , \" item2\" ]"
244+
245+ # Check if it looks like a JSON array but will fail to parse
246+ looks_like_json_array = (val_trimmed .startswith ("[" ) and val_trimmed .endswith ("]" ))
247+
248+ parsed = parse_json_payload (value )
249+ # If parsing succeeded and result is a list, validate and return
250+ if isinstance (parsed , list ):
251+ # Validate all elements are strings
252+ if all (isinstance (item , str ) for item in parsed ):
253+ return parsed , None
254+ return None , f"{ param_name } must contain only strings, got: { parsed } "
255+ # If parsing returned the original string but it looked like a JSON array,
256+ # it's malformed JSON - return error instead of treating as single item
257+ if parsed == value and looks_like_json_array :
258+ return None , f"{ param_name } has invalid JSON syntax: '{ value } '. Expected a valid JSON array like [\" item1\" , \" item2\" ]"
259+ # If parsing returned the original string (plain non-JSON), treat as single item
260+ if parsed == value :
261+ # Treat as single-element list
262+ return [value ], None
263+
264+ return None , f"{ param_name } must be a JSON array (list), got string that parsed to { type (parsed ).__name__ } "
265+
266+ return None , f"{ param_name } must be a list or JSON string, got { type (value ).__name__ } "
267+
268+
215269def normalize_color (value : Any , output_range : str = "float" ) -> tuple [list [float ] | None , str | None ]:
216270 """
217271 Normalize a color parameter to [r, g, b, a] format.
0 commit comments