fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

Optional string parameters for tools get [not] parsed as JSON in MCP Inspector

Open gaieges opened this issue 5 months ago • 4 comments

Description

Hey folks,

Not sure whether this is a MCP Inspector issue or FastMCP so figured I'd start here.

I've got a simple tool function (below) that takes an optional role_name which seems to not be getting populated in the request coming in BEFORE even my middleware can take a look at it:

Image

It's clearly different in the MCP Inspector UI as it seems to expect JSON for this field now instead of just a plain old string:

Image

It seems like even the calls from MCP inspector is being sent as null, even when the role_name field is populated:

Image

The spec on the MCP Inspector side seems correct:

Image

Example Code

@mcp.tool
async def netsuite_create_new_user(
    first_name: str,
    last_name: str,
    email: str,
    job_title: str,
    role_name: str | None = None,
) -> str:
    return await create_new_user(first_name, last_name, email, job_title, role_name)

Version Information

fastmcp==2.10.1
Python 3.12.8

Additional Context

No response

gaieges avatar Jul 02 '25 16:07 gaieges

I'm encountering the same issue where an optional string parameter in a tool call is being set to None—likely because it's expecting JSON format—even though a string value is provided. It only works correctly when the string is explicitly wrapped in double quotes, like "string_content".

vcse59 avatar Jul 04 '25 04:07 vcse59

Same issue , needs a fix

sagar-catomz avatar Jul 17 '25 14:07 sagar-catomz

@jlowin Is there any news? I have the same issue. ;)

Alwinator avatar Sep 08 '25 14:09 Alwinator

Same issue. Doesn't matter if I add explicit arguments and types to their function using the Optional or Union objects in the typing library, or if I use the python 3.10+ syntax with the pipe operator.

@mcp.tool()
def get_incidents(
    limit: int = 10,  # Optional - has default value
    offset: int = 0,  # Optional - has default value
    state: Optional[str] = None,  # Optional - can be None
    assigned_to: str | None = None,  # Optional - can be None
    category: Union[str, None] = None,  # Optional - can be None
    priority: Optional[str] = None,  # Optional - can be None
    number: Optional[str] = None,  # Optional - can be None
    date_filters: Optional[dict] = None,  # Optional - can be None
    ) -> dict:
    ...

produces this output in the MCP Inspector

Image

Version Information

fastmcp==2.12.5
Python 3.12.10

matt-davis27 avatar Oct 29 '25 16:10 matt-davis27