Skip to content

Commit 7eb47a3

Browse files
committed
Update mcp.controller.ts
1 parent c1c9635 commit 7eb47a3

File tree

1 file changed

+77
-1
lines changed

1 file changed

+77
-1
lines changed

src/api/controllers/mcp.controller.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Controller, Post, Get, Body, UseGuards, HttpException, HttpStatus, Req, Res } from '@nestjs/common';
22
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
3-
import { Response } from 'express';
3+
import { Response, Request } from 'express';
44
import { FirebaseAuthGuard } from '../../auth/firebase-auth.guard';
55
import { User } from '../../shared/decorators/user.decorator';
66
import { UserContext } from '../../auth/firebase.strategy';
@@ -53,6 +53,82 @@ export class McpController {
5353
}
5454
}
5555

56+
/**
57+
* MCP SSE Endpoint - For ChatGPT streaming
58+
* GET /api/mcp/sse
59+
* Authorization: Bearer {FIREBASE_TOKEN}
60+
*/
61+
@Get('sse')
62+
@UseGuards(FirebaseAuthGuard)
63+
@ApiBearerAuth()
64+
@ApiOperation({
65+
summary: 'MCP Protocol SSE Stream',
66+
description: 'Server-Sent Events endpoint for ChatGPT to stream MCP protocol',
67+
})
68+
async streamMcpEvents(@User() user: UserContext, @Req() req: Request, @Res() res: Response): Promise<void> {
69+
// Set SSE headers
70+
res.setHeader('Content-Type', 'text/event-stream');
71+
res.setHeader('Cache-Control', 'no-cache');
72+
res.setHeader('Connection', 'keep-alive');
73+
res.setHeader('Access-Control-Allow-Origin', '*');
74+
75+
try {
76+
// Send initialization
77+
this.sendSSEMessage(res, 'message', {
78+
jsonrpc: '2.0',
79+
result: this.mcpService.getInitializeResponse(),
80+
});
81+
82+
// Load and send resources
83+
const resources = await this.mcpService.listResources(user);
84+
this.sendSSEMessage(res, 'message', {
85+
jsonrpc: '2.0',
86+
result: { resources },
87+
});
88+
89+
// Load and send tools
90+
const tools = this.mcpService.listTools();
91+
this.sendSSEMessage(res, 'message', {
92+
jsonrpc: '2.0',
93+
result: { tools },
94+
});
95+
96+
// Send ready signal
97+
this.sendSSEMessage(res, 'message', {
98+
jsonrpc: '2.0',
99+
result: {
100+
status: 'ready',
101+
resourceCount: resources.length,
102+
toolCount: tools.length,
103+
},
104+
});
105+
106+
// Keep connection alive
107+
const keepAlive = setInterval(() => {
108+
res.write(': keep-alive\n\n');
109+
}, 30000);
110+
111+
// Cleanup on disconnect
112+
req.on('close', () => {
113+
clearInterval(keepAlive);
114+
res.end();
115+
});
116+
} catch (error) {
117+
this.sendSSEMessage(res, 'error', {
118+
error: error.message || 'Stream error',
119+
});
120+
res.end();
121+
}
122+
}
123+
124+
/**
125+
* Helper: Send SSE formatted message
126+
*/
127+
private sendSSEMessage(res: Response, eventType: string, data: any): void {
128+
const message = `event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`;
129+
res.write(message);
130+
}
131+
56132
/**
57133
* MCP Initialize - Without authentication (first call)
58134
*/

0 commit comments

Comments
 (0)