@@ -390,8 +390,8 @@ def parse_request(
390390 Decorator to handle Claude Code bug where requests are double-serialized as strings.
391391
392392 Automatically parses string requests to Pydantic models before calling
393- the tool function.
394- This eliminates the need for manual parsing code in every tool function .
393+ the tool function. Also modifies the function's type annotations to accept
394+ str | RequestModel to pass FastMCP validation .
395395
396396 See: https://github.com/anthropics/claude-code/issues/5504
397397
@@ -406,15 +406,17 @@ def parse_request(
406406 @mcp_auth_hook
407407 @parse_request(ListChartsRequest)
408408 async def list_charts(
409- request: ListChartsRequest, ctx: Context
409+ request: ListChartsRequest, ctx: Context # Keep clean type hint
410410 ) -> ChartList:
411- # Decorator handles string conversion automatically
411+ # Decorator handles string conversion and type annotation
412412 await ctx.info(f"Listing charts: page={request.page}")
413413 ...
414414
415415 Note:
416416 - Works with both async and sync functions
417417 - Request must be the first positional argument
418+ - Modifies __annotations__ to accept str | RequestModel for FastMCP
419+ - Function implementation can use clean RequestModel type hint
418420 - If request is already a model instance, it passes through unchanged
419421 - Handles JSON string parsing with helpful error messages
420422 """
@@ -429,7 +431,7 @@ async def async_wrapper(request: Any, *args: Any, **kwargs: Any) -> Any:
429431 parsed_request = parse_json_or_model (request , request_class , "request" )
430432 return await func (parsed_request , * args , ** kwargs )
431433
432- return async_wrapper
434+ wrapper = async_wrapper
433435 else :
434436
435437 @wraps (func )
@@ -439,6 +441,15 @@ def sync_wrapper(request: Any, *args: Any, **kwargs: Any) -> Any:
439441 parsed_request = parse_json_or_model (request , request_class , "request" )
440442 return func (parsed_request , * args , ** kwargs )
441443
442- return sync_wrapper
444+ wrapper = sync_wrapper
445+
446+ # Modify the wrapper's annotations to accept str | RequestModel
447+ # This allows FastMCP to accept string inputs while keeping the
448+ # original function's type hints clean
449+ if hasattr (wrapper , "__annotations__" ):
450+ # Create union type: str | RequestModel
451+ wrapper .__annotations__ ["request" ] = str | request_class
452+
453+ return wrapper
443454
444455 return decorator
0 commit comments