@@ -28,6 +28,25 @@ const getResourceSchema = z.object({
28
28
. describe ( 'URI of the resource to fetch in the format "scheme://path"' ) ,
29
29
} ) ;
30
30
31
+ // Parameters for listTools method
32
+ const listToolsSchema = z . object ( {
33
+ server : z
34
+ . string ( )
35
+ . optional ( )
36
+ . describe ( 'Optional server name to filter tools by' ) ,
37
+ } ) ;
38
+
39
+ // Parameters for executeTool method
40
+ const executeToolSchema = z . object ( {
41
+ uri : z
42
+ . string ( )
43
+ . describe ( 'URI of the tool to execute in the format "scheme://path"' ) ,
44
+ params : z
45
+ . record ( z . unknown ( ) )
46
+ . optional ( )
47
+ . describe ( 'Parameters to pass to the tool' ) ,
48
+ } ) ;
49
+
31
50
// Return type for listResources
32
51
const listResourcesReturnSchema = z . array (
33
52
z . object ( {
@@ -39,6 +58,20 @@ const listResourcesReturnSchema = z.array(
39
58
// Return type for getResource
40
59
const getResourceReturnSchema = z . string ( ) ;
41
60
61
+ // Return type for listTools
62
+ const listToolsReturnSchema = z . array (
63
+ z . object ( {
64
+ uri : z . string ( ) ,
65
+ name : z . string ( ) ,
66
+ description : z . string ( ) . optional ( ) ,
67
+ parameters : z . record ( z . unknown ( ) ) . optional ( ) ,
68
+ returns : z . record ( z . unknown ( ) ) . optional ( ) ,
69
+ } ) ,
70
+ ) ;
71
+
72
+ // Return type for executeTool - can be any JSON value
73
+ const executeToolReturnSchema = z . unknown ( ) ;
74
+
42
75
// Map to store MCP clients
43
76
const mcpClients = new Map < string , any > ( ) ;
44
77
@@ -87,7 +120,7 @@ export function createMcpTool(config: McpConfig): Tool {
87
120
return {
88
121
name : 'mcp' ,
89
122
description :
90
- 'Interact with Model Context Protocol (MCP) servers to retrieve resources' ,
123
+ 'Interact with Model Context Protocol (MCP) servers to retrieve resources and execute tools ' ,
91
124
parameters : z . discriminatedUnion ( 'method' , [
92
125
z . object ( {
93
126
method : z . literal ( 'listResources' ) ,
@@ -97,6 +130,14 @@ export function createMcpTool(config: McpConfig): Tool {
97
130
method : z . literal ( 'getResource' ) ,
98
131
params : getResourceSchema ,
99
132
} ) ,
133
+ z . object ( {
134
+ method : z . literal ( 'listTools' ) ,
135
+ params : listToolsSchema . optional ( ) ,
136
+ } ) ,
137
+ z . object ( {
138
+ method : z . literal ( 'executeTool' ) ,
139
+ params : executeToolSchema ,
140
+ } ) ,
100
141
] ) ,
101
142
parametersJsonSchema : zodToJsonSchema (
102
143
z . discriminatedUnion ( 'method' , [
@@ -108,15 +149,33 @@ export function createMcpTool(config: McpConfig): Tool {
108
149
method : z . literal ( 'getResource' ) ,
109
150
params : getResourceSchema ,
110
151
} ) ,
152
+ z . object ( {
153
+ method : z . literal ( 'listTools' ) ,
154
+ params : listToolsSchema . optional ( ) ,
155
+ } ) ,
156
+ z . object ( {
157
+ method : z . literal ( 'executeTool' ) ,
158
+ params : executeToolSchema ,
159
+ } ) ,
111
160
] ) ,
112
161
) ,
113
- returns : z . union ( [ listResourcesReturnSchema , getResourceReturnSchema ] ) ,
162
+ returns : z . union ( [
163
+ listResourcesReturnSchema ,
164
+ getResourceReturnSchema ,
165
+ listToolsReturnSchema ,
166
+ executeToolReturnSchema ,
167
+ ] ) ,
114
168
returnsJsonSchema : zodToJsonSchema (
115
- z . union ( [ listResourcesReturnSchema , getResourceReturnSchema ] ) ,
169
+ z . union ( [
170
+ listResourcesReturnSchema ,
171
+ getResourceReturnSchema ,
172
+ listToolsReturnSchema ,
173
+ executeToolReturnSchema ,
174
+ ] ) ,
116
175
) ,
117
176
118
177
execute : async ( { method, params } , { logger } ) => {
119
- // Extract the server name from a resource URI
178
+ // Extract the server name from a URI ( resource or tool)
120
179
function getServerNameFromUri ( uri : string ) : string | undefined {
121
180
const match = uri . match ( / ^ ( [ ^ : ] + ) : \/ \/ / ) ;
122
181
return match ? match [ 1 ] : undefined ;
@@ -180,6 +239,64 @@ export function createMcpTool(config: McpConfig): Tool {
180
239
logger . verbose ( `Fetching resource: ${ uri } ` ) ;
181
240
const resource = await client . resource ( uri ) ;
182
241
return resource . content ;
242
+ } else if ( method === 'listTools' ) {
243
+ // List available tools from MCP servers
244
+ const tools : any [ ] = [ ] ;
245
+ const serverFilter = params ?. server ;
246
+
247
+ // If a specific server is requested, only check that server
248
+ if ( serverFilter ) {
249
+ const client = mcpClients . get ( serverFilter ) ;
250
+ if ( client ) {
251
+ try {
252
+ logger . verbose ( `Fetching tools from server: ${ serverFilter } ` ) ;
253
+ const serverTools = await client . tools ( ) ;
254
+ tools . push ( ...( serverTools as any [ ] ) ) ;
255
+ } catch ( error ) {
256
+ logger . error (
257
+ `Failed to fetch tools from server ${ serverFilter } :` ,
258
+ error ,
259
+ ) ;
260
+ }
261
+ } else {
262
+ logger . warn ( `Server not found: ${ serverFilter } ` ) ;
263
+ }
264
+ } else {
265
+ // Otherwise, check all servers
266
+ for ( const [ serverName , client ] of mcpClients . entries ( ) ) {
267
+ try {
268
+ logger . verbose ( `Fetching tools from server: ${ serverName } ` ) ;
269
+ const serverTools = await client . tools ( ) ;
270
+ tools . push ( ...( serverTools as any [ ] ) ) ;
271
+ } catch ( error ) {
272
+ logger . error (
273
+ `Failed to fetch tools from server ${ serverName } :` ,
274
+ error ,
275
+ ) ;
276
+ }
277
+ }
278
+ }
279
+
280
+ return tools ;
281
+ } else if ( method === 'executeTool' ) {
282
+ // Execute a tool from an MCP server
283
+ const { uri, params : toolParams = { } } = params ;
284
+
285
+ // Parse the URI to determine which server to use
286
+ const serverName = getServerNameFromUri ( uri ) ;
287
+ if ( ! serverName ) {
288
+ throw new Error ( `Could not determine server from URI: ${ uri } ` ) ;
289
+ }
290
+
291
+ const client = mcpClients . get ( serverName ) ;
292
+ if ( ! client ) {
293
+ throw new Error ( `Server not found: ${ serverName } ` ) ;
294
+ }
295
+
296
+ // Use the MCP SDK to execute the tool
297
+ logger . verbose ( `Executing tool: ${ uri } with params:` , toolParams ) ;
298
+ const result = await client . tool ( uri , toolParams ) ;
299
+ return result ;
183
300
}
184
301
185
302
throw new Error ( `Unknown method: ${ method } ` ) ;
@@ -188,20 +305,36 @@ export function createMcpTool(config: McpConfig): Tool {
188
305
logParameters : ( params , { logger } ) => {
189
306
if ( params . method === 'listResources' ) {
190
307
logger . verbose (
191
- `Listing MCP resources${ params . params ?. server ? ` from server: ${ params . params . server } ` : '' } ` ,
308
+ `Listing MCP resources${
309
+ params . params ?. server ? ` from server: ${ params . params . server } ` : ''
310
+ } `,
192
311
) ;
193
312
} else if ( params . method === 'getResource' ) {
194
313
logger . verbose ( `Fetching MCP resource: ${ params . params . uri } ` ) ;
314
+ } else if ( params . method === 'listTools' ) {
315
+ logger . verbose (
316
+ `Listing MCP tools${
317
+ params . params ?. server ? ` from server: ${ params . params . server } ` : ''
318
+ } `,
319
+ ) ;
320
+ } else if ( params . method === 'executeTool' ) {
321
+ logger . verbose ( `Executing MCP tool: ${ params . params . uri } ` ) ;
195
322
}
196
323
} ,
197
324
198
325
logReturns : ( result , { logger } ) => {
199
326
if ( Array . isArray ( result ) ) {
200
- logger . verbose ( `Found ${ result . length } MCP resources` ) ;
201
- } else {
327
+ if ( result . length > 0 && 'description' in result [ 0 ] ) {
328
+ logger . verbose ( `Found ${ result . length } MCP tools` ) ;
329
+ } else {
330
+ logger . verbose ( `Found ${ result . length } MCP resources` ) ;
331
+ }
332
+ } else if ( typeof result === 'string' ) {
202
333
logger . verbose (
203
334
`Retrieved MCP resource content (${ result . length } characters)` ,
204
335
) ;
336
+ } else {
337
+ logger . verbose ( `Executed MCP tool and received result` ) ;
205
338
}
206
339
} ,
207
340
} ;
0 commit comments