Skip to content

fix: handle Optional[str] in pre_parse_json to prevent incorrect type coercion#2554

Open
Maanik23 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Maanik23:fix/pre-parse-json-optional-str
Open

fix: handle Optional[str] in pre_parse_json to prevent incorrect type coercion#2554
Maanik23 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Maanik23:fix/pre-parse-json-optional-str

Conversation

@Maanik23
Copy link
Copy Markdown

@Maanik23 Maanik23 commented May 7, 2026

Fixes #381

Summary

The pre_parse_json method in func_metadata.py uses field_info.annotation is not str to decide whether to attempt JSON parsing of string values. This check fails for union types like Optional[str] (str | None) because the annotation object is str | None, not str.

This causes string values like "1.2" or '{"key": "value"}' to be parsed via json.loads into float/dict, which then fails Pydantic validation with Input should be a valid string.

Reproducer (from issue)

@server.tool()
async def getinfo(cid: Optional[str] = Field(..., description="xxx.")) -> bool:
    ...

# Client calls with: {"cid": "1.2"}
# Result: ValidationError - Input should be a valid string [input_value=1.2, input_type=float]

Changes

src/mcp/server/mcpserver/utilities/func_metadata.py

  • Added _annotation_accepts_str() helper that correctly identifies str-only unions (Optional[str], str | None) as string-accepting annotations
  • Uses is_union_origin (already imported) and get_origin/get_args (already imported) for introspection
  • Preserves existing behavior for unions with other complex types (e.g. str | list[str] still gets JSON pre-parsing)

tests/server/mcpserver/test_func_metadata.py

  • Added test_optional_str_preserves_json_string: numeric strings, JSON objects, and JSON arrays all stay as str when annotation is Optional[str]
  • Added test_pipe_none_str_preserves_json_string: same coverage for str | None syntax
  • Added test_optional_str_runtime_validation: end-to-end call_fn_with_arg_validation with Optional[str] param

Backward compatibility

… coercion

The pre_parse_json check ield_info.annotation is not str fails for union
types like Optional[str] (str | None), causing string values like '1.2' or
'{key: value}' to be incorrectly parsed into float/dict via json.loads,
which then fails Pydantic validation.

Add _annotation_accepts_str() helper that correctly identifies str-only unions
(Optional[str], str | None) as string-accepting annotations, while still
allowing JSON pre-parsing for unions with other complex types (str | list[str]).

Fixes modelcontextprotocol#381
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fastmcp tool parameter parsing type error

1 participant