@@ -71,11 +71,11 @@ async def main():
7171import warnings
7272from collections .abc import AsyncIterator , Awaitable , Callable , Iterable
7373from contextlib import AbstractAsyncContextManager , AsyncExitStack , asynccontextmanager
74- from typing import Any , Generic , TypeVar
74+ from typing import Any , Generic , TypeVar , cast
7575
7676import anyio
7777from anyio .streams .memory import MemoryObjectReceiveStream , MemoryObjectSendStream
78- from pydantic import AnyUrl
78+ from pydantic import AnyUrl , BaseModel
7979
8080import mcp .types as types
8181from mcp .server .lowlevel .helper_types import ReadResourceContents
@@ -399,30 +399,119 @@ def decorator(
399399 ...,
400400 Awaitable [
401401 Iterable [
402- types .TextContent
403- | types .DataContent
404- | types .ImageContent
405- | types .EmbeddedResource
402+ types .TextContent | types .ImageContent | types .EmbeddedResource
406403 ]
404+ | BaseModel
405+ | dict [str , Any ]
407406 ],
408407 ],
408+ schema_func : Callable [..., dict [str , Any ] | None ] | None = None ,
409409 ):
410410 logger .debug ("Registering handler for CallToolRequest" )
411411
412- async def handler (req : types .CallToolRequest ):
413- try :
414- results = await func (req .params .name , (req .params .arguments or {}))
412+ def handle_result_without_schema (req : types .CallToolRequest , result : Any ):
413+ if type (result ) is dict or isinstance (result , BaseModel ):
414+ error = f"""Tool { req .params .name } has no outputSchema and
415+ must return content"""
415416 return types .ServerResult (
416- types .CallToolResult (content = list (results ), isError = False )
417+ types .CallToolResult (
418+ content = [
419+ types .TextContent (
420+ type = "text" ,
421+ text = error ,
422+ )
423+ ],
424+ structuredContent = None ,
425+ isError = True ,
426+ )
427+ )
428+ else :
429+ content_result = cast (
430+ Iterable [
431+ types .TextContent
432+ | types .ImageContent
433+ | types .EmbeddedResource
434+ ],
435+ result ,
436+ )
437+ return types .ServerResult (
438+ types .CallToolResult (
439+ content = list (content_result ),
440+ structuredContent = None ,
441+ isError = False ,
442+ )
417443 )
418- except Exception as e :
444+
445+ def handle_result_with_schema (
446+ req : types .CallToolRequest , result : Any , schema : dict [str , Any ]
447+ ):
448+ if isinstance (result , BaseModel ):
449+ model_result = result .model_dump ()
419450 return types .ServerResult (
420451 types .CallToolResult (
421- content = [types .TextContent (type = "text" , text = str (e ))],
452+ content = [],
453+ structuredContent = model_result ,
454+ isError = False ,
455+ )
456+ )
457+ elif type (result ) is dict [str , Any ]:
458+ return types .ServerResult (
459+ types .CallToolResult (
460+ content = [], structuredContent = result , isError = False
461+ )
462+ )
463+ else :
464+ error = f"""Tool { req .params .name } has outputSchema and "
465+ must return structured content"""
466+ return types .ServerResult (
467+ types .CallToolResult (
468+ content = [
469+ types .TextContent (
470+ type = "text" ,
471+ text = error ,
472+ )
473+ ],
474+ structuredContent = None ,
422475 isError = True ,
423476 )
424477 )
425478
479+ def handle_error (e : Exception ):
480+ return types .ServerResult (
481+ types .CallToolResult (
482+ content = [types .TextContent (type = "text" , text = str (e ))],
483+ structuredContent = None ,
484+ isError = True ,
485+ )
486+ )
487+
488+ if schema_func is None :
489+
490+ async def handler (req : types .CallToolRequest ):
491+ try :
492+ result = await func (
493+ req .params .name , (req .params .arguments or {})
494+ )
495+ return handle_result_without_schema (req , result )
496+ except Exception as e :
497+ return handle_error (e )
498+
499+ else :
500+
501+ async def handler (req : types .CallToolRequest ):
502+ try :
503+ result = await func (
504+ req .params .name , (req .params .arguments or {})
505+ )
506+ schema = schema_func (req .params .name )
507+
508+ if schema :
509+ return handle_result_with_schema (req , result , schema )
510+ else :
511+ return handle_result_without_schema (req , result )
512+ except Exception as e :
513+ return handle_error (e )
514+
426515 self .request_handlers [types .CallToolRequest ] = handler
427516 return func
428517
0 commit comments