diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index c692d27..be2eddc 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -43,10 +43,13 @@ async function bootstrap() { .addTag('admin', 'Admin panel operations') .addTag('notifications', 'Notification history & preferences') .addTag('analytics', 'Market reports & price analytics') + .addTag('reviews', 'Property reviews & ratings') + .addTag('mcp', 'Model Context Protocol server transport') .build(); const document = SwaggerModule.createDocument(app, swaggerConfig); SwaggerModule.setup('api/v1/docs', app, document, { swaggerOptions: { persistAuthorization: true }, + jsonDocumentUrl: 'api/v1/docs-json', }); // ── Security Headers (Helmet) ── @@ -55,8 +58,8 @@ async function bootstrap() { contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], - scriptSrc: ["'self'"], - styleSrc: ["'self'", "'unsafe-inline'"], + scriptSrc: ["'self'", "'unsafe-inline'", 'https://cdn.jsdelivr.net'], + styleSrc: ["'self'", "'unsafe-inline'", 'https://cdn.jsdelivr.net'], imgSrc: ["'self'", 'data:', 'https:', 'blob:'], connectSrc: ["'self'"], fontSrc: ["'self'", 'data:'], diff --git a/apps/api/src/modules/mcp/presentation/mcp-transport.controller.ts b/apps/api/src/modules/mcp/presentation/mcp-transport.controller.ts index 0ea7213..a0e6214 100644 --- a/apps/api/src/modules/mcp/presentation/mcp-transport.controller.ts +++ b/apps/api/src/modules/mcp/presentation/mcp-transport.controller.ts @@ -10,9 +10,12 @@ import { HttpStatus, UseGuards, } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; import type { Request, Response } from 'express'; import { JwtAuthGuard, CurrentUser, type JwtPayload } from '@modules/auth'; +@ApiTags('mcp') +@ApiBearerAuth('JWT') @Controller('mcp') @UseGuards(JwtAuthGuard) export class McpTransportController { @@ -21,11 +24,19 @@ export class McpTransportController { constructor(private readonly registry: McpRegistryService) {} @Get('servers') + @ApiOperation({ summary: 'List available MCP servers' }) + @ApiResponse({ status: 200, description: 'List of registered MCP server names' }) + @ApiResponse({ status: 401, description: 'Unauthorized' }) listServers(): { servers: string[] } { return { servers: this.registry.getServerNames() }; } @Get(':serverName/sse') + @ApiOperation({ summary: 'Open SSE connection to an MCP server' }) + @ApiParam({ name: 'serverName', description: 'Name of the MCP server to connect to' }) + @ApiResponse({ status: 200, description: 'SSE stream established' }) + @ApiResponse({ status: 404, description: 'MCP server not found' }) + @ApiResponse({ status: 401, description: 'Unauthorized' }) async handleSse( @Param('serverName') serverName: string, @CurrentUser() _user: JwtPayload, @@ -54,6 +65,12 @@ export class McpTransportController { } @Post(':serverName/messages') + @ApiOperation({ summary: 'Send a message to an MCP server session' }) + @ApiParam({ name: 'serverName', description: 'Name of the MCP server' }) + @ApiResponse({ status: 200, description: 'Message processed' }) + @ApiResponse({ status: 400, description: 'Missing sessionId query parameter' }) + @ApiResponse({ status: 404, description: 'Session not found or expired' }) + @ApiResponse({ status: 401, description: 'Unauthorized' }) async handleMessage( @Param('serverName') _serverName: string, @CurrentUser() _user: JwtPayload,