Repository: jlowin/fastmcp (python)

Issue:
String args containing ints are failing validation
Minimal repro:

```python
from fastmcp import FastMCP


mcp = FastMCP("Demo 🚀")

@mcp.tool()
def concat_strings(a: str, b: str) -> str:
    """
    Concatenate two strings together.
    """
    return a + b
```

When this is called with e.g. `"a"` and `"1"`, a Pydantic error is returned:

```
================================ Human Message =================================

can you concatenate the string "a" and the string "2" for me?
================================== Ai Message ==================================

[{'text': 'I\'ll help you concatenate the strings "a" and "2" using the `concat_strings` function.', 'type': 'text'}, {'id': 'toolu_01DkxqVusERHFyBrZyYMNHuZ', 'input': {'a': 'a', 'b': '2'}, 'name': 'concat_strings', 'type': 'tool_use'}]
Tool Calls:
  concat_strings (toolu_01DkxqVusERHFyBrZyYMNHuZ)
 Call ID: toolu_01DkxqVusERHFyBrZyYMNHuZ
  Args:
    a: a
    b: 2
...
================================= Tool Message =================================
Name: concat_strings

Error: ExceptionGroup('unhandled errors in a TaskGroup', [ExceptionGroup('unhandled errors in a TaskGroup', [ToolException([TextContent(type='text', text='Error executing tool concat_strings: 1 validation error for concat_stringsArguments\nb\n  Input should be a valid string [type=string_type, input_value=2, input_type=int]\n    For further information visit https://errors.pydantic.dev/2.10/v/string_type')])])])
 Please fix your mistakes.
...
```

Pull request description:
fix: handle strings containing numbers correctly
Fixes #62.

Patch:
diff --git a/src/fastmcp/utilities/func_metadata.py b/src/fastmcp/utilities/func_metadata.py
index 9bd4921..8bced29 100644
--- a/src/fastmcp/utilities/func_metadata.py
+++ b/src/fastmcp/utilities/func_metadata.py
@@ -91,7 +91,7 @@ class FuncMetadata(BaseModel):
                     pre_parsed = json.loads(data[field_name])
                 except json.JSONDecodeError:
                     continue  # Not JSON - skip
-                if isinstance(pre_parsed, str):
+                if isinstance(pre_parsed, (str, int, float)):
                     # This is likely that the raw value is e.g. `"hello"` which we
                     # Should really be parsed as '"hello"' in Python - but if we parse
                     # it as JSON it'll turn into just 'hello'. So we skip it.


Test patch:
diff --git a/tests/test_func_metadata.py b/tests/test_func_metadata.py
index d0429a1..eebcb04 100644
--- a/tests/test_func_metadata.py
+++ b/tests/test_func_metadata.py
@@ -174,6 +174,21 @@ def test_str_vs_list_str():
     assert result["str_or_list"] == ["hello", "world"]
 
 
+def test_str_vs_int():
+    """
+    Test that string values are kept as strings even when they contain numbers,
+    while numbers are parsed correctly.
+    """
+
+    def func_with_str_and_int(a: str, b: int):
+        return a
+
+    meta = func_metadata(func_with_str_and_int)
+    result = meta.pre_parse_json({"a": "123", "b": 123})
+    assert result["a"] == "123"
+    assert result["b"] == 123
+
+
 def test_skip_names():
     """Test that skipped parameters are not included in the model"""