11import { z } from "zod" ;
22import { Tool as SDKTool } from "@modelcontextprotocol/sdk/types.js" ;
3+ import { ImageContent } from "../transports/utils/image-handler.js" ;
34
45export type ToolInputSchema < T > = {
56 [ K in keyof T ] : {
@@ -12,6 +13,22 @@ export type ToolInput<T extends ToolInputSchema<any>> = {
1213 [ K in keyof T ] : z . infer < T [ K ] [ "type" ] > ;
1314} ;
1415
16+ export type TextContent = {
17+ type : "text" ;
18+ text : string ;
19+ } ;
20+
21+ export type ErrorContent = {
22+ type : "error" ;
23+ text : string ;
24+ } ;
25+
26+ export type ToolContent = TextContent | ErrorContent | ImageContent ;
27+
28+ export type ToolResponse = {
29+ content : ToolContent [ ] ;
30+ } ;
31+
1532export interface ToolProtocol extends SDKTool {
1633 name : string ;
1734 description : string ;
@@ -25,9 +42,7 @@ export interface ToolProtocol extends SDKTool {
2542 } ;
2643 toolCall ( request : {
2744 params : { name : string ; arguments ?: Record < string , unknown > } ;
28- } ) : Promise < {
29- content : Array < { type : string ; text : string } > ;
30- } > ;
45+ } ) : Promise < ToolResponse > ;
3146}
3247
3348export abstract class MCPTool < TInput extends Record < string , any > = { } >
@@ -65,7 +80,7 @@ export abstract class MCPTool<TInput extends Record<string, any> = {}>
6580
6681 async toolCall ( request : {
6782 params : { name : string ; arguments ?: Record < string , unknown > } ;
68- } ) {
83+ } ) : Promise < ToolResponse > {
6984 try {
7085 const args = request . params . arguments || { } ;
7186 const validatedInput = await this . validateInput ( args ) ;
@@ -95,18 +110,76 @@ export abstract class MCPTool<TInput extends Record<string, any> = {}>
95110 return "string" ;
96111 }
97112
98- protected createSuccessResponse ( data : unknown ) {
113+ protected createSuccessResponse ( data : unknown ) : ToolResponse {
114+ if ( this . isImageContent ( data ) ) {
115+ return {
116+ content : [ data ] ,
117+ } ;
118+ }
119+
120+ if ( Array . isArray ( data ) ) {
121+ const validContent = data . filter ( item => this . isValidContent ( item ) ) as ToolContent [ ] ;
122+ if ( validContent . length > 0 ) {
123+ return {
124+ content : validContent ,
125+ } ;
126+ }
127+ }
128+
99129 return {
100130 content : [ { type : "text" , text : JSON . stringify ( data ) } ] ,
101131 } ;
102132 }
103133
104- protected createErrorResponse ( error : Error ) {
134+ protected createErrorResponse ( error : Error ) : ToolResponse {
105135 return {
106136 content : [ { type : "error" , text : error . message } ] ,
107137 } ;
108138 }
109139
140+ private isImageContent ( data : unknown ) : data is ImageContent {
141+ return (
142+ typeof data === "object" &&
143+ data !== null &&
144+ "type" in data &&
145+ data . type === "image" &&
146+ "data" in data &&
147+ "mimeType" in data &&
148+ typeof ( data as ImageContent ) . data === "string" &&
149+ typeof ( data as ImageContent ) . mimeType === "string"
150+ ) ;
151+ }
152+
153+ private isTextContent ( data : unknown ) : data is TextContent {
154+ return (
155+ typeof data === "object" &&
156+ data !== null &&
157+ "type" in data &&
158+ data . type === "text" &&
159+ "text" in data &&
160+ typeof ( data as TextContent ) . text === "string"
161+ ) ;
162+ }
163+
164+ private isErrorContent ( data : unknown ) : data is ErrorContent {
165+ return (
166+ typeof data === "object" &&
167+ data !== null &&
168+ "type" in data &&
169+ data . type === "error" &&
170+ "text" in data &&
171+ typeof ( data as ErrorContent ) . text === "string"
172+ ) ;
173+ }
174+
175+ private isValidContent ( data : unknown ) : data is ToolContent {
176+ return (
177+ this . isImageContent ( data ) ||
178+ this . isTextContent ( data ) ||
179+ this . isErrorContent ( data )
180+ ) ;
181+ }
182+
110183 protected async fetch < T > ( url : string , init ?: RequestInit ) : Promise < T > {
111184 const response = await fetch ( url , init ) ;
112185 if ( ! response . ok ) {
0 commit comments