Fix TypeError for MCP tools with nullable JSON Schema params
MCP servers (e.g. Zapier) return JSON Schema union types like `"type": ["string", "null"]` for nullable parameters. The existing `validate_params()` and `cast_params()` methods expected only simple strings as `type`, causing `TypeError: unhashable type: 'list'` on every MCP tool call with nullable parameters. Add `_resolve_type()` helper that extracts the first non-null type from union types, and use it in `_cast_value()` and `_validate()`. Also handle `None` values correctly when the schema declares a nullable type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Xubin Ren
parent
dd7e3e499f
commit
0b1beb0e9f
@@ -21,6 +21,20 @@ class Tool(ABC):
|
||||
"object": dict,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _resolve_type(t: Any) -> str | None:
|
||||
"""Resolve JSON Schema type to a simple string.
|
||||
|
||||
JSON Schema allows ``"type": ["string", "null"]`` (union types).
|
||||
We extract the first non-null type so validation/casting works.
|
||||
"""
|
||||
if isinstance(t, list):
|
||||
for item in t:
|
||||
if item != "null":
|
||||
return item
|
||||
return None
|
||||
return t
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
@@ -78,7 +92,7 @@ class Tool(ABC):
|
||||
|
||||
def _cast_value(self, val: Any, schema: dict[str, Any]) -> Any:
|
||||
"""Cast a single value according to schema."""
|
||||
target_type = schema.get("type")
|
||||
target_type = self._resolve_type(schema.get("type"))
|
||||
|
||||
if target_type == "boolean" and isinstance(val, bool):
|
||||
return val
|
||||
@@ -131,7 +145,11 @@ class Tool(ABC):
|
||||
return self._validate(params, {**schema, "type": "object"}, "")
|
||||
|
||||
def _validate(self, val: Any, schema: dict[str, Any], path: str) -> list[str]:
|
||||
t, label = schema.get("type"), path or "parameter"
|
||||
raw_type = schema.get("type")
|
||||
nullable = isinstance(raw_type, list) and "null" in raw_type
|
||||
t, label = self._resolve_type(raw_type), path or "parameter"
|
||||
if nullable and val is None:
|
||||
return []
|
||||
if t == "integer" and (not isinstance(val, int) or isinstance(val, bool)):
|
||||
return [f"{label} should be integer"]
|
||||
if t == "number" and (
|
||||
|
||||
Reference in New Issue
Block a user