|
From: Strands Engineering <engineering@strands.dev> |
|
Date: Thu, 16 Jan 2025 10:30:00 +0000 |
|
Subject: [PATCH] Fix MCP tool property normalization bug with anyOf schemas |
|
|
|
Fix critical issue in MCP tool property normalization where the |
|
`_normalize_property` function incorrectly sets default type to "string" |
|
for properties that use `anyOf` JSON Schema construct, causing tool |
|
input validation errors when models return string-encoded JSON. |
|
|
|
**Problem:** |
|
- MCP server describes complex types using `anyOf` (e.g., `List[str] | None`) |
|
- `_normalize_property` function always calls `setdefault("type", "string")` |
|
- This overrides the proper `anyOf` type specification |
|
- Results in models returning string-encoded JSON instead of proper types |
|
- Causes tool input validation failures |
|
|
|
**Root Cause:** |
|
In `_normalize_property` function, line 91 unconditionally sets: |
|
```python |
|
prop_def.setdefault("type", "string") # Problem line |
|
``` |
|
|
|
This conflicts with `anyOf` specifications where no default type should be set. |
|
|
|
**Solution:** |
|
Only set default type when no type specification exists (no `anyOf`, `oneOf`, etc.) |
|
|
|
**Expected vs Actual Schema:** |
|
|
|
Before fix (broken): |
|
```json |
|
{ |
|
"properties": { |
|
"items": { |
|
"anyOf": [ |
|
{"items": {"type": "string"}, "type": "array"}, |
|
{"type": "null"} |
|
], |
|
"type": "string" // ← This breaks validation! |
|
} |
|
} |
|
} |
|
``` |
|
|
|
After fix (correct): |
|
```json |
|
{ |
|
"properties": { |
|
"items": { |
|
"anyOf": [ |
|
{"items": {"type": "string"}, "type": "array"}, |
|
{"type": "null"} |
|
] |
|
} |
|
} |
|
} |
|
``` |
|
|
|
Fixes: Issue #1190 |
|
--- |
|
strands/tools/tools.py | 8 ++++++-- |
|
1 file changed, 6 insertions(+), 2 deletions(-) |
|
|
|
diff --git a/strands/tools/tools.py b/strands/tools/tools.py |
|
index abc1234..def5678 100644 |
|
--- a/strands/tools/tools.py |
|
+++ b/strands/tools/tools.py |
|
@@ -88,8 +88,12 @@ def _normalize_property(prop_def: dict) -> dict: |
|
Ensures all property definitions have consistent structure. |
|
""" |
|
|
|
- # Set default type to string if not specified |
|
- prop_def.setdefault("type", "string") |
|
+ # Only set default type if no type specification exists |
|
+ # Don't set default type when anyOf, oneOf, allOf, or $ref is present |
|
+ # as these define the type structure themselves |
|
+ has_type_spec = any(key in prop_def for key in ["anyOf", "oneOf", "allOf", "$ref", "type"]) |
|
+ if not has_type_spec: |
|
+ prop_def.setdefault("type", "string") |
|
|
|
# Ensure description exists |
|
prop_def.setdefault("description", "") |
|
-- |
|
2.39.0 |