@@ -13,9 +13,81 @@ import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js
1313import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
1414import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js" ;
1515import cors from "cors" ;
16- import type { Request , Response } from "express" ;
16+ import type { Request , Response , NextFunction } from "express" ;
1717import { createServer } from "./server.js" ;
1818
19+ /**
20+ * Normalize Accept header for lenient MCP compatibility.
21+ * The SDK requires 'application/json, text/event-stream' but some clients send '*\/*'.
22+ * We must patch rawHeaders because @hono/node-server reads from there, not req.headers.
23+ */
24+ function normalizeAcceptHeader ( req : Request , _res : Response , next : NextFunction ) : void {
25+ const accept = req . headers . accept ;
26+ if ( ! accept || accept === "*/*" ) {
27+ const normalized = "application/json, text/event-stream" ;
28+ req . headers . accept = normalized ;
29+
30+ // Patch rawHeaders for @hono /node-server compatibility
31+ const nodeReq = req as unknown as { rawHeaders : string [ ] } ;
32+ const newRawHeaders : string [ ] = [ ] ;
33+ let found = false ;
34+ for ( let i = 0 ; i < nodeReq . rawHeaders . length ; i += 2 ) {
35+ if ( nodeReq . rawHeaders [ i ] . toLowerCase ( ) === "accept" ) {
36+ newRawHeaders . push ( nodeReq . rawHeaders [ i ] , normalized ) ;
37+ found = true ;
38+ } else {
39+ newRawHeaders . push ( nodeReq . rawHeaders [ i ] , nodeReq . rawHeaders [ i + 1 ] ) ;
40+ }
41+ }
42+ if ( ! found ) {
43+ newRawHeaders . push ( "Accept" , normalized ) ;
44+ }
45+ Object . defineProperty ( nodeReq , "rawHeaders" , { value : newRawHeaders } ) ;
46+ }
47+ next ( ) ;
48+ }
49+
50+ /**
51+ * HTTP logging middleware - logs full request and response details.
52+ */
53+ function httpLogger ( req : Request , res : Response , next : NextFunction ) : void {
54+ const startTime = Date . now ( ) ;
55+ const reqId = Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) ;
56+
57+ // Log request
58+ console . log ( `\n[${ reqId } ] ← ${ req . method } ${ req . url } ` ) ;
59+ console . log ( `[${ reqId } ] Headers:` , JSON . stringify ( req . headers , null , 2 ) ) ;
60+ if ( req . body && Object . keys ( req . body ) . length > 0 ) {
61+ console . log ( `[${ reqId } ] Body:` , JSON . stringify ( req . body , null , 2 ) ) ;
62+ }
63+
64+ // Capture response
65+ const originalWrite = res . write . bind ( res ) ;
66+ const originalEnd = res . end . bind ( res ) ;
67+ const chunks : Buffer [ ] = [ ] ;
68+
69+ res . write = function ( chunk : any , ...args : any [ ] ) : boolean {
70+ if ( chunk ) chunks . push ( Buffer . isBuffer ( chunk ) ? chunk : Buffer . from ( chunk ) ) ;
71+ return originalWrite ( chunk , ...args ) ;
72+ } ;
73+
74+ res . end = function ( chunk ?: any , ...args : any [ ] ) : Response {
75+ if ( chunk ) chunks . push ( Buffer . isBuffer ( chunk ) ? chunk : Buffer . from ( chunk ) ) ;
76+ const duration = Date . now ( ) - startTime ;
77+ const body = Buffer . concat ( chunks ) . toString ( "utf8" ) ;
78+
79+ console . log ( `[${ reqId } ] → ${ res . statusCode } (${ duration } ms)` ) ;
80+ console . log ( `[${ reqId } ] Headers:` , JSON . stringify ( res . getHeaders ( ) , null , 2 ) ) ;
81+ if ( body ) {
82+ console . log ( `[${ reqId } ] Body:` , body . length > 2000 ? body . slice ( 0 , 2000 ) + "..." : body ) ;
83+ }
84+
85+ return originalEnd ( chunk , ...args ) ;
86+ } ;
87+
88+ next ( ) ;
89+ }
90+
1991export interface ServerOptions {
2092 port : number ;
2193 name ?: string ;
@@ -32,8 +104,10 @@ export async function startServer(
32104
33105 const app = createMcpExpressApp ( { host : "0.0.0.0" } ) ;
34106 app . use ( cors ( ) ) ;
107+ app . use ( normalizeAcceptHeader ) ;
108+ app . use ( httpLogger ) ;
35109
36- app . all ( "/mcp" , async ( req : Request , res : Response ) => {
110+ app . post ( "/mcp" , async ( req : Request , res : Response ) => {
37111 const server = createServer ( ) ;
38112 const transport = new StreamableHTTPServerTransport ( {
39113 sessionIdGenerator : undefined ,
@@ -59,6 +133,23 @@ export async function startServer(
59133 }
60134 } ) ;
61135
136+ // GET and DELETE not supported in stateless mode
137+ app . get ( "/mcp" , ( _req : Request , res : Response ) => {
138+ res . status ( 405 ) . json ( {
139+ jsonrpc : "2.0" ,
140+ error : { code : - 32000 , message : "Method not allowed in stateless mode" } ,
141+ id : null ,
142+ } ) ;
143+ } ) ;
144+
145+ app . delete ( "/mcp" , ( _req : Request , res : Response ) => {
146+ res . status ( 405 ) . json ( {
147+ jsonrpc : "2.0" ,
148+ error : { code : - 32000 , message : "Method not allowed in stateless mode" } ,
149+ id : null ,
150+ } ) ;
151+ } ) ;
152+
62153 const httpServer = app . listen ( port , ( err ) => {
63154 if ( err ) {
64155 console . error ( "Failed to start server:" , err ) ;
0 commit comments