@@ -242,10 +242,37 @@ if (useHttp) {
242242 logger . info ( `Starting HTTP server on port ${ port } ` ) ;
243243
244244 const httpServer = createServer ( async ( req , res ) => {
245- // Enable CORS
246- res . setHeader ( 'Access-Control-Allow-Origin' , '*' ) ;
245+ // Validate Origin header as required by MCP spec
246+ const origin = req . headers . origin ;
247+ const allowedOrigins = [
248+ 'http://localhost:3000' ,
249+ 'http://127.0.0.1:3000' ,
250+ 'https://mcp.socket.dev' ,
251+ 'https://mcp.socket-staging.dev'
252+ ] ;
253+
254+ const isValidOrigin = ! origin || allowedOrigins . includes ( origin ) ;
255+
256+ if ( origin && ! isValidOrigin ) {
257+ logger . warn ( `Rejected request from invalid origin: ${ origin } ` ) ;
258+ res . writeHead ( 403 , { 'Content-Type' : 'application/json' } ) ;
259+ res . end ( JSON . stringify ( {
260+ jsonrpc : '2.0' ,
261+ error : { code : - 32000 , message : 'Forbidden: Invalid origin' } ,
262+ id : null
263+ } ) ) ;
264+ return ;
265+ }
266+
267+ // Set CORS headers for valid origins
268+ if ( origin && isValidOrigin ) {
269+ res . setHeader ( 'Access-Control-Allow-Origin' , origin ) ;
270+ } else {
271+ res . setHeader ( 'Access-Control-Allow-Origin' , 'http://localhost:3000' ) ;
272+ }
247273 res . setHeader ( 'Access-Control-Allow-Methods' , 'GET, POST, DELETE, OPTIONS' ) ;
248- res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type, mcp-session-id' ) ;
274+ res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type, mcp-session-id, Accept, Last-Event-ID' ) ;
275+ res . setHeader ( 'Access-Control-Expose-Headers' , 'mcp-session-id' ) ;
249276
250277 if ( req . method === 'OPTIONS' ) {
251278 res . writeHead ( 200 ) ;
@@ -269,6 +296,19 @@ if (useHttp) {
269296
270297 if ( url . pathname === '/' ) {
271298 if ( req . method === 'POST' ) {
299+ // Validate Accept header as required by MCP spec
300+ const acceptHeader = req . headers . accept ;
301+ if ( ! acceptHeader || ( ! acceptHeader . includes ( 'application/json' ) && ! acceptHeader . includes ( 'text/event-stream' ) ) ) {
302+ logger . warn ( `Invalid Accept header: ${ acceptHeader } ` ) ;
303+ res . writeHead ( 400 , { 'Content-Type' : 'application/json' } ) ;
304+ res . end ( JSON . stringify ( {
305+ jsonrpc : '2.0' ,
306+ error : { code : - 32000 , message : 'Bad Request: Accept header must include application/json or text/event-stream' } ,
307+ id : null
308+ } ) ) ;
309+ return ;
310+ }
311+
272312 // Handle JSON-RPC messages
273313 let body = '' ;
274314 req . on ( 'data' , chunk => body += chunk ) ;
@@ -277,6 +317,18 @@ if (useHttp) {
277317 const jsonData = JSON . parse ( body ) ;
278318 const sessionId = req . headers [ 'mcp-session-id' ] as string ;
279319
320+ // Validate session ID format if provided (must contain only visible ASCII characters)
321+ if ( sessionId && ! / ^ [ \x21 - \x7E ] + $ / . test ( sessionId ) ) {
322+ logger . warn ( `Invalid session ID format: ${ sessionId } ` ) ;
323+ res . writeHead ( 400 , { 'Content-Type' : 'application/json' } ) ;
324+ res . end ( JSON . stringify ( {
325+ jsonrpc : '2.0' ,
326+ error : { code : - 32000 , message : 'Bad Request: Session ID must contain only visible ASCII characters' } ,
327+ id : jsonData . id || null
328+ } ) ) ;
329+ return ;
330+ }
331+
280332 let transport : StreamableHTTPServerTransport ;
281333
282334 if ( sessionId && transports [ sessionId ] ) {
@@ -298,6 +350,8 @@ if (useHttp) {
298350 onsessioninitialized : ( id ) => {
299351 transports [ id ] = transport ;
300352 logger . info ( `Session initialized: ${ id } ` ) ;
353+ // Set session ID in response headers as required by MCP spec
354+ res . setHeader ( 'mcp-session-id' , id ) ;
301355 }
302356 } ) ;
303357
@@ -343,14 +397,54 @@ if (useHttp) {
343397 } ) ;
344398
345399 } else if ( req . method === 'GET' ) {
400+ // Validate Accept header for SSE as required by MCP spec
401+ const acceptHeader = req . headers . accept ;
402+ if ( ! acceptHeader || ! acceptHeader . includes ( 'text/event-stream' ) ) {
403+ logger . warn ( `GET request without text/event-stream Accept header: ${ acceptHeader } ` ) ;
404+ res . writeHead ( 405 , { 'Content-Type' : 'application/json' } ) ;
405+ res . end ( JSON . stringify ( {
406+ jsonrpc : '2.0' ,
407+ error : { code : - 32000 , message : 'Method Not Allowed: GET requires Accept: text/event-stream' } ,
408+ id : null
409+ } ) ) ;
410+ return ;
411+ }
412+
346413 // Handle SSE streams
347414 const sessionId = req . headers [ 'mcp-session-id' ] as string ;
415+
416+ // Validate session ID format
417+ if ( sessionId && ! / ^ [ \x21 - \x7E ] + $ / . test ( sessionId ) ) {
418+ logger . warn ( `Invalid session ID format in GET request: ${ sessionId } ` ) ;
419+ res . writeHead ( 400 , { 'Content-Type' : 'application/json' } ) ;
420+ res . end ( JSON . stringify ( {
421+ jsonrpc : '2.0' ,
422+ error : { code : - 32000 , message : 'Bad Request: Session ID must contain only visible ASCII characters' } ,
423+ id : null
424+ } ) ) ;
425+ return ;
426+ }
427+
348428 if ( ! sessionId || ! transports [ sessionId ] ) {
349- res . writeHead ( 400 ) ;
350- res . end ( 'Invalid or missing session ID' ) ;
429+ logger . warn ( `SSE request with invalid session ID: ${ sessionId } ` ) ;
430+ res . writeHead ( 400 , { 'Content-Type' : 'application/json' } ) ;
431+ res . end ( JSON . stringify ( {
432+ jsonrpc : '2.0' ,
433+ error : { code : - 32000 , message : 'Bad Request: Invalid or missing session ID for SSE stream' } ,
434+ id : null
435+ } ) ) ;
351436 return ;
352437 }
353438
439+ // Check for Last-Event-ID header for resumability (optional MCP feature)
440+ const lastEventId = req . headers [ 'last-event-id' ] as string ;
441+ if ( lastEventId ) {
442+ logger . info ( `SSE resumability requested with Last-Event-ID: ${ lastEventId } ` ) ;
443+ // Note: Actual resumability implementation would require message storage
444+ // For now, we log the request but don't implement full resumability
445+ }
446+
447+ // Let the transport handle SSE headers and response
354448 const transport = transports [ sessionId ] ;
355449 await transport . handleRequest ( req , res ) ;
356450
0 commit comments