@@ -34,66 +34,28 @@ export class McpController {
3434 res . setHeader ( 'Access-Control-Allow-Methods' , 'GET, POST, OPTIONS' ) ;
3535 res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type, Authorization' ) ;
3636
37- // Send immediate keepalive to prevent timeout
38- res . write ( ': keepalive\n\n' ) ;
39-
40- // Store connection state
41- const connectionState = {
42- token : null as string | null ,
43- userId : null as string | null ,
44- } ;
45-
4637 try {
47- // Send initialization
48- this . sendSSEMessage ( res , 'message' , {
49- jsonrpc : '2.0' ,
50- result : this . mcpService . getInitializeResponse ( ) ,
51- } ) ;
38+ // Send immediate message endpoint event (ChatGPT protocol)
39+ // ChatGPT expects an "endpoint" event telling it where to POST requests
40+ const messageEndpoint = `${ req . protocol } ://${ req . get ( 'host' ) } /api/mcp/message` ;
41+ this . sendSSEMessage ( res , 'endpoint' , messageEndpoint ) ;
5242
53- // Send available tools (including login)
54- const tools = this . mcpService . listTools ( ) ;
55- this . sendSSEMessage ( res , 'message' , {
56- jsonrpc : '2.0' ,
57- result : { tools } ,
58- } ) ;
59-
60- // Send ready signal
61- this . sendSSEMessage ( res , 'message' , {
62- jsonrpc : '2.0' ,
63- result : {
64- status : 'ready' ,
65- toolCount : tools . length ,
66- message : 'Use the login tool to authenticate' ,
67- } ,
68- } ) ;
69-
70- // Handle incoming MCP requests
71- req . on ( 'data' , async ( chunk ) => {
72- try {
73- const request = JSON . parse ( chunk . toString ( ) ) as McpRequest ;
74- const response = await this . processMcpRequest ( request , connectionState ) ;
75- this . sendSSEMessage ( res , 'message' , response ) ;
76- } catch ( error ) {
77- this . sendSSEMessage ( res , 'error' , {
78- jsonrpc : '2.0' ,
79- error : {
80- code : MCP_ERROR_CODES . PARSE_ERROR ,
81- message : error . message || 'Failed to parse request' ,
82- } ,
83- } ) ;
84- }
85- } ) ;
43+ // Send immediate keepalive
44+ res . write ( ': keepalive\n\n' ) ;
8645
8746 // Keep connection alive
8847 const keepAlive = setInterval ( ( ) => {
89- res . write ( ': keep-alive \n\n' ) ;
90- } , 30000 ) ;
48+ res . write ( ': keepalive \n\n' ) ;
49+ } , 15000 ) ; // Every 15 seconds
9150
9251 // Cleanup on disconnect
9352 req . on ( 'close' , ( ) => {
9453 clearInterval ( keepAlive ) ;
9554 res . end ( ) ;
9655 } ) ;
56+
57+ // Keep the connection open indefinitely
58+ // ChatGPT will POST to /api/mcp/message for actual requests
9759 } catch ( error ) {
9860 this . sendSSEMessage ( res , 'error' , {
9961 error : error . message || 'Stream error' ,
@@ -103,7 +65,43 @@ export class McpController {
10365 }
10466
10567 /**
106- * MCP JSON-RPC Handler - Alternative POST endpoint
68+ * MCP Message Endpoint - ChatGPT posts requests here
69+ * POST /api/mcp/message
70+ */
71+ @Post ( 'message' )
72+ @ApiOperation ( {
73+ summary : 'MCP Message Handler' ,
74+ description :
75+ 'Handle MCP requests from ChatGPT connector. This is where ChatGPT POSTs tool calls.' ,
76+ } )
77+ @ApiResponse ( {
78+ status : 200 ,
79+ description : 'MCP response' ,
80+ } )
81+ async handleMcpMessage ( @Body ( ) request : McpRequest , @Res ( ) res : Response ) : Promise < void > {
82+ // Store connection state (stateless for ChatGPT - each request is independent)
83+ const connectionState = {
84+ token : null as string | null ,
85+ userId : null as string | null ,
86+ } ;
87+
88+ try {
89+ const response = await this . processMcpRequest ( request , connectionState ) ;
90+ res . json ( response ) ;
91+ } catch ( error ) {
92+ res . json ( {
93+ jsonrpc : '2.0' ,
94+ id : request . id ,
95+ error : {
96+ code : MCP_ERROR_CODES . INTERNAL_ERROR ,
97+ message : error . message || 'Internal server error' ,
98+ } ,
99+ } ) ;
100+ }
101+ }
102+
103+ /**
104+ * MCP JSON-RPC Handler - Alternative POST endpoint (for non-ChatGPT clients)
107105 * POST /api/mcp
108106 */
109107 @Post ( )
0 commit comments