@@ -15,7 +15,8 @@ import { SSEServerTransport } from "./sse.ts";
15
15
import { HttpServerTransport } from "./http.ts" ;
16
16
import { dereferenceSchema } from "./utils.ts" ;
17
17
import { WebSocketServerTransport } from "./websocket.ts" ;
18
-
18
+ import { compose , type RequestMiddleware } from "./middleware.ts" ;
19
+ import type { z } from "zod" ;
19
20
const idFromDefinition = ( definition : string ) => {
20
21
const [ _ , __ , id ] = definition . split ( "/" ) ;
21
22
return id ;
@@ -50,6 +51,10 @@ export interface Options<TManifest extends AppManifest> {
50
51
exclude ?: Array < keyof ( TManifest [ "actions" ] & TManifest [ "loaders" ] ) > ;
51
52
blocks ?: Array < keyof TManifest > ;
52
53
basePath ?: string ;
54
+ middlewares ?: {
55
+ listTools ?: ListToolsMiddleware [ ] ;
56
+ callTool ?: CallToolMiddleware [ ] ;
57
+ } ;
53
58
}
54
59
55
60
interface RootSchema extends JSONSchema7 {
@@ -183,26 +188,45 @@ export const getTools = <TManifest extends AppManifest>(
183
188
return tools . filter ( ( tool ) => tool !== undefined ) ;
184
189
} ;
185
190
191
+ export interface ListToolsResult {
192
+ tools : Tool [ ] ;
193
+ }
194
+
195
+ export type ListToolsMiddleware = RequestMiddleware <
196
+ z . infer < typeof ListToolsRequestSchema > ,
197
+ ListToolsResult
198
+ > ;
199
+
200
+ export type CallToolMiddleware = RequestMiddleware <
201
+ z . infer < typeof CallToolRequestSchema > ,
202
+ { content : { type : "text" ; text : string } [ ] }
203
+ > ;
204
+
186
205
function registerTools < TManifest extends AppManifest > (
187
206
mcp : McpServer ,
188
207
deco : Deco < TManifest > ,
189
208
options ?: Options < TManifest > ,
190
209
) {
191
210
// Add map to store slugified names to original names
192
211
let toolNames : null | Map < string , string > = null ;
193
- const loadTools = async ( ) => {
212
+ const loadTools : ListToolsMiddleware = async ( ) : Promise < ListToolsResult > => {
194
213
toolNames ??= new Map < string , string > ( ) ;
195
214
const meta = await deco . meta ( ) . then ( ( v ) => v ?. value ) ;
196
215
if ( ! meta ) return { tools : [ ] } ;
197
216
const schemas = meta . schema ;
198
217
return { tools : getTools ( toolNames , schemas , options ) } ;
199
218
} ;
200
219
201
- mcp . server . setRequestHandler ( ListToolsRequestSchema , async ( ) => {
202
- return await loadTools ( ) ;
220
+ const listTools : ListToolsMiddleware = compose (
221
+ ...( options ?. middlewares ?. listTools ?? [ ] ) ,
222
+ loadTools ,
223
+ ) ;
224
+
225
+ mcp . server . setRequestHandler ( ListToolsRequestSchema , async ( request ) => {
226
+ return await listTools ( request ) ;
203
227
} ) ;
204
228
205
- mcp . server . setRequestHandler ( CallToolRequestSchema , async ( req ) => {
229
+ const invokeTool = async ( req : z . infer < typeof CallToolRequestSchema > ) => {
206
230
IS_DEBUG && console . log ( req ) ;
207
231
try {
208
232
const state = await deco . prepareState ( {
@@ -213,7 +237,7 @@ function registerTools<TManifest extends AppManifest>(
213
237
} ) ;
214
238
// Use the original name from the map when invoking
215
239
if ( ! toolNames ) {
216
- await loadTools ( ) ;
240
+ await loadTools ( { request : { } } ) ;
217
241
}
218
242
const originalName = toolNames ! . get ( req . params . name ) ;
219
243
if ( ! originalName ) {
@@ -227,12 +251,21 @@ function registerTools<TManifest extends AppManifest>(
227
251
state ,
228
252
) ;
229
253
return {
230
- content : [ { type : "text" , text : JSON . stringify ( result ) } ] ,
254
+ content : [ { type : "text" as const , text : JSON . stringify ( result ) } ] ,
231
255
} ;
232
256
} catch ( err ) {
233
257
console . error ( err ) ;
234
258
throw err ;
235
259
}
260
+ } ;
261
+
262
+ const callToolMiddleware : CallToolMiddleware = compose (
263
+ ...( options ?. middlewares ?. callTool ?? [ ] ) ,
264
+ invokeTool ,
265
+ ) ;
266
+
267
+ mcp . server . setRequestHandler ( CallToolRequestSchema , async ( req ) => {
268
+ return await callToolMiddleware ( req ) ;
236
269
} ) ;
237
270
}
238
271
0 commit comments