11import { Hono } from "hono" ;
22import "../../types/hono" ;
33import { handleJsonRpc , BridgeMode } from "../../services/mcp-http-bridge" ;
4+ import { buildCorsHeaders } from "../../utils/cors" ;
45
56// In-memory SSE session store per serverId:sessionId
67type Session = {
@@ -16,40 +17,49 @@ const latestSessionByServer: Map<string, string> = new Map();
1617function createHttpHandler ( mode : BridgeMode , routePrefix : string ) {
1718 const router = new Hono ( ) ;
1819
19- router . options ( "/:serverId" , ( c ) =>
20- c . body ( null , 204 , {
21- "Access-Control-Allow-Origin" : "*" ,
22- "Access-Control-Allow-Methods" : "GET,POST,HEAD,OPTIONS" ,
23- "Access-Control-Allow-Headers" :
24- "*, Authorization, Content-Type, Accept, Accept-Language" ,
25- "Access-Control-Expose-Headers" : "*" ,
26- "Access-Control-Max-Age" : "86400" ,
27- } ) ,
28- ) ;
20+ const handlePreflight = ( c : any ) => {
21+ const originHeader = c . req . header ( "origin" ) ;
22+ const { headers, allowedOrigin } = buildCorsHeaders ( originHeader , {
23+ allowMethods : "GET,POST,HEAD,OPTIONS" ,
24+ allowHeaders :
25+ "Authorization, Content-Type, Accept, Accept-Language, X-MCPJam-Endpoint-Base" ,
26+ exposeHeaders : "*" ,
27+ maxAge : "86400" ,
28+ allowCredentials : true ,
29+ allowPrivateNetwork : true ,
30+ requestPrivateNetwork :
31+ c . req . header ( "access-control-request-private-network" ) === "true" ,
32+ } ) ;
33+
34+ if ( originHeader && ! allowedOrigin ) {
35+ return c . json ( { error : "Origin not allowed" } , 403 , { Vary : "Origin" } ) ;
36+ }
37+
38+ headers . Vary = `${ headers . Vary } , Access-Control-Request-Headers` ;
39+ return c . body ( null , 204 , headers ) ;
40+ } ;
41+
42+ router . options ( "/:serverId" , handlePreflight ) ;
2943
3044 // Wildcard variants to tolerate trailing paths (e.g., /mcp)
31- router . options ( "/:serverId/*" , ( c ) =>
32- c . body ( null , 204 , {
33- "Access-Control-Allow-Origin" : "*" ,
34- "Access-Control-Allow-Methods" : "GET,POST,HEAD,OPTIONS" ,
35- "Access-Control-Allow-Headers" :
36- "*, Authorization, Content-Type, Accept, Accept-Language" ,
37- "Access-Control-Expose-Headers" : "*" ,
38- "Access-Control-Max-Age" : "86400" ,
39- } ) ,
40- ) ;
45+ router . options ( "/:serverId/*" , handlePreflight ) ;
4146
4247 async function handleHttp ( c : any ) {
4348 const serverId = c . req . param ( "serverId" ) ;
4449 const method = c . req . method ;
50+ const originHeader = c . req . header ( "origin" ) ;
51+ const { headers : corsHeaders } = buildCorsHeaders ( originHeader , {
52+ exposeHeaders : "*" ,
53+ allowCredentials : true ,
54+ } ) ;
4555
4656 // SSE endpoint for clients that probe/subscribe via GET; HEAD advertises event-stream
4757 if ( method === "HEAD" ) {
4858 return c . body ( null , 200 , {
59+ ...corsHeaders ,
4960 "Content-Type" : "text/event-stream" ,
5061 "Cache-Control" : "no-cache" ,
5162 Connection : "keep-alive" ,
52- "Access-Control-Allow-Origin" : "*" ,
5363 "X-Accel-Buffering" : "no" ,
5464 } ) ;
5565 }
@@ -127,18 +137,17 @@ function createHttpHandler(mode: BridgeMode, routePrefix: string) {
127137 } ,
128138 } ) ;
129139 return c . body ( stream as any , 200 , {
140+ ...corsHeaders ,
130141 "Content-Type" : "text/event-stream" ,
131142 "Cache-Control" : "no-cache" ,
132143 Connection : "keep-alive" ,
133- "Access-Control-Allow-Origin" : "*" ,
134- "Access-Control-Expose-Headers" : "*" ,
135144 "X-Accel-Buffering" : "no" ,
136145 "Transfer-Encoding" : "chunked" ,
137146 } ) ;
138147 }
139148
140149 if ( method !== "POST" ) {
141- return c . json ( { error : "Unsupported request" } , 400 ) ;
150+ return c . json ( { error : "Unsupported request" } , 400 , corsHeaders ) ;
142151 }
143152
144153 // Parse JSON body (best effort)
@@ -172,17 +181,21 @@ function createHttpHandler(mode: BridgeMode, routePrefix: string) {
172181 ) ;
173182 if ( ! response ) {
174183 // Notification → 202 Accepted
175- return c . body ( "Accepted" , 202 , { "Access-Control-Allow-Origin" : "*" } ) ;
184+ return c . body ( "Accepted" , 202 , corsHeaders ) ;
176185 }
177186 return c . body ( JSON . stringify ( response ) , 200 , {
187+ ...corsHeaders ,
178188 "Content-Type" : "application/json" ,
179- "Access-Control-Allow-Origin" : "*" ,
180- "Access-Control-Expose-Headers" : "*" ,
181189 } ) ;
182190 }
183191
184192 // Endpoint to receive client messages for SSE transport: /:serverId/messages?sessionId=...
185193 router . post ( "/:serverId/messages" , async ( c ) => {
194+ const originHeader = c . req . header ( "origin" ) ;
195+ const { headers : corsHeaders } = buildCorsHeaders ( originHeader , {
196+ exposeHeaders : "*" ,
197+ allowCredentials : true ,
198+ } ) ;
186199 const serverId = c . req . param ( "serverId" ) ;
187200 const url = new URL ( c . req . url ) ;
188201 const sessionId = url . searchParams . get ( "sessionId" ) || "" ;
@@ -195,7 +208,7 @@ function createHttpHandler(mode: BridgeMode, routePrefix: string) {
195208 }
196209 }
197210 if ( ! sess ) {
198- return c . json ( { error : "Invalid session" } , 400 ) ;
211+ return c . json ( { error : "Invalid session" } , 400 , corsHeaders ) ;
199212 }
200213 let body : any ;
201214 try {
@@ -242,15 +255,9 @@ function createHttpHandler(mode: BridgeMode, routePrefix: string) {
242255 } catch { }
243256 }
244257 // 202 Accepted per SSE transport semantics
245- return c . body ( "Accepted" , 202 , {
246- "Access-Control-Allow-Origin" : "*" ,
247- "Access-Control-Expose-Headers" : "*" ,
248- } ) ;
258+ return c . body ( "Accepted" , 202 , corsHeaders ) ;
249259 } catch ( e : any ) {
250- return c . body ( "Error" , 400 , {
251- "Access-Control-Allow-Origin" : "*" ,
252- "Access-Control-Expose-Headers" : "*" ,
253- } ) ;
260+ return c . body ( "Error" , 400 , corsHeaders ) ;
254261 }
255262 } ) ;
256263
0 commit comments