@@ -49,6 +49,7 @@ import {
4949 type ListResourceTemplatesResult ,
5050 type ListPromptsResult ,
5151} from "@modelcontextprotocol/sdk/types.js" ;
52+ import type { AnySchema } from "@modelcontextprotocol/sdk/server/zod-compat.js" ;
5253import {
5354 ZodRawShapeCompat ,
5455 getObjectShape ,
@@ -98,6 +99,8 @@ export interface ToolDefinition {
9899 name : string ;
99100 description : string ;
100101 inputSchema ?: ToolInputSchema ;
102+ /** Optional Zod object schema for tool output; when set, handler must return structuredContent. */
103+ outputSchema ?: unknown ;
101104 handler : (
102105 params : Record < string , any > ,
103106 context ?: TestServerContext ,
@@ -470,37 +473,43 @@ export function createMcpServer(config: ServerConfig): McpServer {
470473 {
471474 description : tool . description ,
472475 inputSchema : tool . inputSchema ,
476+ ...( tool . outputSchema != null && {
477+ outputSchema : tool . outputSchema as AnySchema ,
478+ } ) ,
473479 } ,
474480 async ( args , extra ) => {
475481 const result = await tool . handler (
476482 args as Record < string , any > ,
477483 context ,
478484 extra ,
479485 ) ;
480- // Handle different return types from tool handlers
481- // If handler returns content array directly (like get-annotated-message), use it
486+ const rawStructured =
487+ result &&
488+ typeof result === "object" &&
489+ "structuredContent" in result
490+ ? ( result as { structuredContent ?: unknown } ) . structuredContent
491+ : undefined ;
492+ const structuredContent =
493+ rawStructured !== undefined && rawStructured !== null
494+ ? ( rawStructured as Record < string , unknown > )
495+ : undefined ;
496+ // If handler returns content array, use it; otherwise build content from message or stringify
497+ let content : Array < { type : "text" ; text : string } > ;
482498 if ( result && Array . isArray ( result . content ) ) {
483- return { content : result . content } ;
484- }
485- // If handler returns message (like echo), format it
486- if ( result && typeof result . message === "string" ) {
487- return {
488- content : [
489- {
490- type : "text" ,
491- text : result . message ,
492- } ,
493- ] ,
494- } ;
495- }
496- // Otherwise, stringify the result
497- return {
498- content : [
499+ content = result . content as Array < { type : "text" ; text : string } > ;
500+ } else if ( result && typeof result . message === "string" ) {
501+ content = [ { type : "text" as const , text : result . message } ] ;
502+ } else {
503+ content = [
499504 {
500- type : "text" ,
501- text : JSON . stringify ( result ) ,
505+ type : "text" as const ,
506+ text : JSON . stringify ( result ?? { } ) ,
502507 } ,
503- ] ,
508+ ] ;
509+ }
510+ return {
511+ content,
512+ ...( structuredContent !== undefined && { structuredContent } ) ,
504513 } ;
505514 } ,
506515 ) ;
0 commit comments