11import type { H3Event } from "../../event.ts" ;
22import type { JsonRpcMethod , JsonRpcRequest } from "../json-rpc.ts" ;
3- import type { McpHandlerOptions } from "../mcp.ts" ;
3+ import type { MaybeLazy , McpHandlerOptions } from "../mcp.ts" ;
4+ import type { McpToolDefinition , McpResourceDefinition , McpPromptDefinition } from "../mcp.ts" ;
45import { processJsonRpcBody , createJsonRpcError , createMethodMap } from "../json-rpc.ts" ;
56import { HTTPError } from "../../error.ts" ;
67
7- const MCP_PROTOCOL_VERSION = "2025-03-26" ;
8+ const MCP_PROTOCOL_VERSION = "2025-06-18" ;
9+ const SUPPORTED_PROTOCOL_VERSIONS = new Set ( [ "2025-06-18" , "2025-03-26" ] ) ;
10+
11+ export interface McpResolvedOptions {
12+ name : string ;
13+ version : string ;
14+ title ?: string ;
15+ instructions ?: string ;
16+ tools : ( ) => Promise < McpToolDefinition < any > [ ] | undefined > ;
17+ resources : ( ) => Promise < McpResourceDefinition [ ] | undefined > ;
18+ prompts : ( ) => Promise < McpPromptDefinition [ ] | undefined > ;
19+ }
20+
21+ export function resolveMcpOptions ( options : McpHandlerOptions ) : McpResolvedOptions {
22+ return {
23+ name : options . name ,
24+ version : options . version ,
25+ title : options . title ,
26+ instructions : options . instructions ,
27+ tools : _resolveLazyArray ( options . tools ) ,
28+ resources : _resolveLazyArray ( options . resources ) ,
29+ prompts : _resolveLazyArray ( options . prompts ) ,
30+ } ;
31+ }
832
933export async function handleMcpRequest (
10- options : McpHandlerOptions ,
34+ options : McpResolvedOptions ,
1135 event : H3Event ,
1236) : Promise < Response > {
1337 const method = event . req . method ;
@@ -23,6 +47,13 @@ export async function handleMcpRequest(
2347 } ) ;
2448 }
2549
50+ const protocolVersion = event . req . headers . get ( "mcp-protocol-version" ) ;
51+ if ( protocolVersion && ! SUPPORTED_PROTOCOL_VERSIONS . has ( protocolVersion ) ) {
52+ return new Response ( `Unsupported MCP protocol version: ${ protocolVersion } ` , {
53+ status : 400 ,
54+ } ) ;
55+ }
56+
2657 const methods = buildMcpMethodMap ( options ) ;
2758 const methodMap = createMethodMap ( methods ) ;
2859
@@ -50,26 +81,57 @@ export async function handleMcpRequest(
5081
5182// --- Internal helpers ---
5283
53- function buildMcpMethodMap ( options : McpHandlerOptions ) : Record < string , JsonRpcMethod > {
84+ function _resolveLazyArray < T > ( items : MaybeLazy < T > [ ] | undefined ) : ( ) => Promise < T [ ] | undefined > {
85+ if ( ! items ?. length ) {
86+ return ( ) => Promise . resolve ( undefined ) ;
87+ }
88+ let cached : Promise < T [ ] > | undefined ;
89+ return ( ) => {
90+ if ( ! cached ) {
91+ cached = Promise . all (
92+ items . map ( ( item ) => ( typeof item === "function" ? ( item as ( ) => T | Promise < T > ) ( ) : item ) ) ,
93+ ) ;
94+ }
95+ return cached ;
96+ } ;
97+ }
98+
99+ function buildMcpMethodMap ( options : McpResolvedOptions ) : Record < string , JsonRpcMethod > {
54100 const methods : Record < string , JsonRpcMethod > = { } ;
55101
56102 // initialize
57- methods [ "initialize" ] = ( ) => {
103+ methods [ "initialize" ] = async ( ) => {
58104 const capabilities : Record < string , unknown > = { } ;
59- if ( options . tools ?. length ) {
105+ const [ tools , resources , prompts ] = await Promise . all ( [
106+ options . tools ( ) ,
107+ options . resources ( ) ,
108+ options . prompts ( ) ,
109+ ] ) ;
110+ if ( tools ?. length ) {
60111 capabilities . tools = { } ;
61112 }
62- if ( options . resources ?. length ) {
113+ if ( resources ?. length ) {
63114 capabilities . resources = { } ;
64115 }
65- if ( options . prompts ?. length ) {
116+ if ( prompts ?. length ) {
66117 capabilities . prompts = { } ;
67118 }
68- return {
119+ const serverInfo : Record < string , string > = {
120+ name : options . name ,
121+ version : options . version ,
122+ } ;
123+ if ( options . title !== undefined ) {
124+ serverInfo . title = options . title ;
125+ }
126+ const result : Record < string , unknown > = {
69127 protocolVersion : MCP_PROTOCOL_VERSION ,
70- serverInfo : { name : options . name , version : options . version } ,
128+ serverInfo,
71129 capabilities,
72130 } ;
131+ if ( options . instructions !== undefined ) {
132+ result . instructions = options . instructions ;
133+ }
134+ return result ;
73135 } ;
74136
75137 // ping
@@ -79,79 +141,80 @@ function buildMcpMethodMap(options: McpHandlerOptions): Record<string, JsonRpcMe
79141 methods [ "notifications/initialized" ] = ( ) => undefined ;
80142
81143 // tools
82- if ( options . tools ?. length ) {
83- const tools = options . tools ;
84-
85- methods [ "tools/list" ] = ( ) => ( {
86- tools : tools . map ( ( tool ) => {
144+ methods [ "tools/list" ] = async ( ) => {
145+ const tools = await options . tools ( ) ;
146+ return {
147+ tools : ( tools ?? [ ] ) . map ( ( tool ) => {
87148 const entry : Record < string , unknown > = {
88149 name : tool . name ,
89150 inputSchema : tool . inputSchema ?? { type : "object" } ,
90151 } ;
91152 if ( tool . title !== undefined ) entry . title = tool . title ;
92153 if ( tool . description !== undefined ) entry . description = tool . description ;
154+ if ( tool . outputSchema !== undefined ) entry . outputSchema = tool . outputSchema ;
93155 if ( tool . annotations !== undefined ) entry . annotations = tool . annotations ;
94156 return entry ;
95157 } ) ,
96- } ) ;
97-
98- methods [ "tools/call" ] = async ( req : JsonRpcRequest , event : H3Event ) => {
99- const params = req . params as Record < string , unknown > | undefined ;
100- const name = params ?. name as string ;
101- const args = ( params ?. arguments ?? { } ) as Record < string , unknown > ;
102-
103- const tool = tools . find ( ( t ) => t . name === name ) ;
104- if ( ! tool ) {
105- throw new HTTPError ( { status : 404 , message : `Tool not found: ${ name } ` } ) ;
106- }
107-
108- if ( tool . inputSchema ) {
109- return await ( tool . handler as ( args : Record < string , unknown > , event : H3Event ) => unknown ) (
110- args ,
111- event ,
112- ) ;
113- }
114- return await ( tool . handler as ( event : H3Event ) => unknown ) ( event ) ;
115158 } ;
116- }
159+ } ;
117160
118- // resources
119- if ( options . resources ?. length ) {
120- const resources = options . resources ;
161+ methods [ "tools/call" ] = async ( req : JsonRpcRequest , event : H3Event ) => {
162+ const tools = await options . tools ( ) ;
163+ const params = req . params as Record < string , unknown > | undefined ;
164+ const name = params ?. name as string ;
165+ const args = ( params ?. arguments ?? { } ) as Record < string , unknown > ;
166+
167+ const tool = tools ?. find ( ( t ) => t . name === name ) ;
168+ if ( ! tool ) {
169+ throw new HTTPError ( { status : 404 , message : `Tool not found: ${ name } ` } ) ;
170+ }
121171
122- methods [ "resources/list" ] = ( ) => ( {
123- resources : resources . map ( ( r ) => {
172+ if ( tool . inputSchema ) {
173+ return await ( tool . handler as ( args : Record < string , unknown > , event : H3Event ) => unknown ) (
174+ args ,
175+ event ,
176+ ) ;
177+ }
178+ return await ( tool . handler as ( event : H3Event ) => unknown ) ( event ) ;
179+ } ;
180+
181+ // resources
182+ methods [ "resources/list" ] = async ( ) => {
183+ const resources = await options . resources ( ) ;
184+ return {
185+ resources : ( resources ?? [ ] ) . map ( ( r ) => {
124186 const entry : Record < string , unknown > = {
125187 name : r . name ,
126188 uri : r . uri ,
127189 } ;
128190 if ( r . title !== undefined ) entry . title = r . title ;
129191 if ( r . description !== undefined ) entry . description = r . description ;
130192 if ( r . mimeType !== undefined ) entry . mimeType = r . mimeType ;
193+ if ( r . size !== undefined ) entry . size = r . size ;
131194 return entry ;
132195 } ) ,
133- } ) ;
196+ } ;
197+ } ;
134198
135- methods [ "resources/read" ] = async ( req : JsonRpcRequest , event : H3Event ) => {
136- const params = req . params as Record < string , unknown > | undefined ;
137- const uriStr = params ?. uri as string ;
138- const uri = new URL ( uriStr ) ;
199+ methods [ "resources/read" ] = async ( req : JsonRpcRequest , event : H3Event ) => {
200+ const resources = await options . resources ( ) ;
201+ const params = req . params as Record < string , unknown > | undefined ;
202+ const uriStr = params ?. uri as string ;
203+ const uri = new URL ( uriStr ) ;
139204
140- const resource = resources . find ( ( r ) => r . uri === uri . toString ( ) ) ;
141- if ( ! resource ) {
142- throw new HTTPError ( { status : 404 , message : `Resource not found: ${ uriStr } ` } ) ;
143- }
205+ const resource = resources ? .find ( ( r ) => r . uri === uri . toString ( ) ) ;
206+ if ( ! resource ) {
207+ throw new HTTPError ( { status : 404 , message : `Resource not found: ${ uriStr } ` } ) ;
208+ }
144209
145- return await resource . handler ( uri , event ) ;
146- } ;
147- }
210+ return await resource . handler ( uri , event ) ;
211+ } ;
148212
149213 // prompts
150- if ( options . prompts ?. length ) {
151- const prompts = options . prompts ;
152-
153- methods [ "prompts/list" ] = ( ) => ( {
154- prompts : prompts . map ( ( p ) => {
214+ methods [ "prompts/list" ] = async ( ) => {
215+ const prompts = await options . prompts ( ) ;
216+ return {
217+ prompts : ( prompts ?? [ ] ) . map ( ( p ) => {
155218 const entry : Record < string , unknown > = {
156219 name : p . name ,
157220 } ;
@@ -160,27 +223,28 @@ function buildMcpMethodMap(options: McpHandlerOptions): Record<string, JsonRpcMe
160223 if ( p . args ?. length ) entry . arguments = p . args ;
161224 return entry ;
162225 } ) ,
163- } ) ;
164-
165- methods [ "prompts/get" ] = async ( req : JsonRpcRequest , event : H3Event ) => {
166- const params = req . params as Record < string , unknown > | undefined ;
167- const name = params ?. name as string ;
168- const args = ( params ?. arguments ?? { } ) as Record < string , string > ;
169-
170- const prompt = prompts . find ( ( p ) => p . name === name ) ;
171- if ( ! prompt ) {
172- throw new HTTPError ( { status : 404 , message : `Prompt not found: ${ name } ` } ) ;
173- }
174-
175- if ( prompt . args ?. length ) {
176- return await ( prompt . handler as ( args : Record < string , string > , event : H3Event ) => unknown ) (
177- args ,
178- event ,
179- ) ;
180- }
181- return await ( prompt . handler as ( event : H3Event ) => unknown ) ( event ) ;
182226 } ;
183- }
227+ } ;
228+
229+ methods [ "prompts/get" ] = async ( req : JsonRpcRequest , event : H3Event ) => {
230+ const prompts = await options . prompts ( ) ;
231+ const params = req . params as Record < string , unknown > | undefined ;
232+ const name = params ?. name as string ;
233+ const args = ( params ?. arguments ?? { } ) as Record < string , string > ;
234+
235+ const prompt = prompts ?. find ( ( p ) => p . name === name ) ;
236+ if ( ! prompt ) {
237+ throw new HTTPError ( { status : 404 , message : `Prompt not found: ${ name } ` } ) ;
238+ }
239+
240+ if ( prompt . args ?. length ) {
241+ return await ( prompt . handler as ( args : Record < string , string > , event : H3Event ) => unknown ) (
242+ args ,
243+ event ,
244+ ) ;
245+ }
246+ return await ( prompt . handler as ( event : H3Event ) => unknown ) ( event ) ;
247+ } ;
184248
185249 return methods ;
186250}
0 commit comments