fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

Cannot Call Prompt with Optional Paramters

Open econeale opened this issue 7 months ago • 1 comments

Description

Hello! I'm trying to create a prompt with optional datetime parameters. However, whenever calling them I get a ValidationError. I'm using FastMCP 2.4.0 and debugging using the @wong2/mcp-cli. I'm running the MCP server with streamable-http.

Example Code

import datetime
import typing

import fastmcp
import pydantic

mcp = fastmcp.FastMCP()

@mcp.prompt()
def test_prompt1(
    start_datetime: typing.Annotated[
        typing.Optional[datetime.datetime],
        pydantic.Field(description="The start datetime of the image to search for."),
    ] = None,
) -> str:
    if start_datetime is None:
        return f"Hello, world!"
    else:
        return f"Hello, world! {start_datetime}"

@mcp.prompt()
def test_prompt2(
    start_datetime: typing.Optional[datetime.datetime] = pydantic.Field(description="The start datetime of the image to search for.", default=None),
) -> str:
    if start_datetime is None:
        return f"Hello, world!"
    else:   
        return f"Hello, world! {start_datetime}"

@mcp.prompt()
def test_prompt3(
    start_datetime: datetime.datetime | None = None,
) -> str:
    if start_datetime is None:
        return f"Hello, world!"
    else:
        return f"Hello, world! {start_datetime}"

if __name__ == "__main__":
    mcp.run()

Version Information

FastMCP version: 2.4.0
MCP version: 1.9.0
Python version: 3.12.3
Platform: Linux-5.15.167.4-microsoft-standard-WSL2-x86_64-with-glibc2.39
FastMCP root path: ~/virtualenvs/gF7ERA00-py3.12/lib/python3.12/site-packages

Additional Context

I thought this may have been related to #224 but it is not specific to Cursor.

econeale avatar May 22 '25 14:05 econeale

Hi! I tried to reproduce this on FastMCP 2.4.0 (both streamable-http and stdio), using your prompt and a minimal client. Everything worked as expected—no ValidationError—when I passed the datetime object as a string.

Here's a working example:

import asyncio, datetime
from fastmcp.client import Client

client = Client(transport="prompt_server.py")
# client = Client(transport="http://localhost:8080/mcp/")

async def main():
    async with client:
        # Pass datetime as ISO string
        print(await client.get_prompt(
            name="test_prompt1",
            arguments={"start_datetime": datetime.datetime.now().isoformat()}
        ))
        # Omit argument
        print(await client.get_prompt(name="test_prompt1"))

if __name__ == "__main__":
    asyncio.run(main())

Note: The client must send arguments as strings. If you pass a raw datetime object, you'll see a Pydantic error like:

arguments.start_datetime
  Input should be a valid string [type=string_type, input_value=datetime.datetime(...), input_type=datetime]

Hope this helps! If you’re still running into trouble, feel free to share your client code and the full error message—happy to help debug further.

davenpi avatar May 22 '25 19:05 davenpi

This is a valid bug that's not specific to datetime @davenpi. Using any Optional[xyz] typing makes most mainstream LLM clients (ie Claude Desktop) pass a string. It does not work no matter how explicit or prescriptive you are in the docstring - it always passes a string. That makes bool, int etc not possible, even though the client is fully capable of it. Removing optional but retaining a default value (effectively making it optional) fixes the problem immediately.

ref https://github.com/taylorwilsdon/google_workspace_mcp/issues/180 Was an interesting journey discovering this one. Seems like folks are mainly using strings (given it's a text based medium, not terribly surprising) so it's less likely to come up, but it was very hard to find the official FastMCP documentation suggesting params with default values as optional and there's no warning that Optional typing is not possible for non-str values otherwise.

taylorwilsdon avatar Aug 20 '25 23:08 taylorwilsdon

Claude has a significant bug with sending strings instead of typed objects, not just strings but also structured objects that should be sent with JSON!

jlowin avatar Aug 21 '25 11:08 jlowin