# MCP Module - Quick Reference & Testing Guide ## ๐Ÿ“‹ Module at a Glance | Aspect | Details | |--------|---------| | **Location** | `apps/api/src/modules/mcp/` | | **Total Files** | 4 source files (2 TypeScript + 1 module config + 1 export) | | **Test Files** | 1 test file (174 lines) | | **Architecture** | Presentation layer only (simplified) | | **Testing Framework** | Vitest with Globals enabled | | **Test Coverage** | Controller well tested, Module/Infrastructure untested | --- ## ๐Ÿ“ File Listing (Complete) ``` โœ… TESTED โœ… SOURCE โŒ NOT TESTED โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ MCP module/ mcp/ โ”œโ”€โ”€ mcp.module.ts [Module config - 22 lines] โ”œโ”€โ”€ index.ts [Re-export - 1 line] โ””โ”€โ”€ presentation/ โ”œโ”€โ”€ mcp-transport.controller.ts [Controller - 102 lines] โ””โ”€โ”€ __tests__/ โ””โ”€โ”€ mcp-transport.controller.spec.ts โœ… [174 lines] ``` ### File Details | File | Type | Lines | Status | Purpose | |------|------|-------|--------|---------| | `index.ts` | Export | 1 | โœ… | Module entry point | | `mcp.module.ts` | Config | 22 | โš ๏ธ | NestJS module setup | | `mcp-transport.controller.ts` | Controller | 102 | โœ… | HTTP/SSE transport | | `mcp-transport.controller.spec.ts` | Test | 174 | โœ… | Controller tests | --- ## ๐Ÿ—๏ธ Architecture Diagram ### Current DDD Structure (Simplified) ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Presentation Layer (Only) โœ… โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ€ข McpTransportController โ”‚ โ”‚ - GET /mcp/servers (list) โ”‚ โ”‚ - GET /mcp/:serverName/sse (connect) โ”‚ โ”‚ - POST /mcp/:serverName/messages (send) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Dependencies: โ”‚ โ”‚ โ€ข McpRegistryService (external) โ”‚ โ”‚ โ€ข JwtAuthGuard (auth layer) โ”‚ โ”‚ โ€ข SSEServerTransport (external) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### Missing Layers (Not Implemented) ``` โŒ Domain Layer - No entities, value objects, events - No business logic abstractions โŒ Application Layer - No CQRS handlers - No command/query objects - No use case orchestration โŒ Infrastructure Layer - No repositories - No external service adapters - No persistence logic ``` --- ## ๐Ÿงช Testing Overview ### Test File Stats ``` File: mcp-transport.controller.spec.ts โ”œโ”€โ”€ Total Tests: 11 โ”œโ”€โ”€ Test Suites: 4 describe blocks โ”œโ”€โ”€ Total Lines: 174 โ””โ”€โ”€ Coverage Areas: โ”œโ”€โ”€ Security Decorators: 4 tests โ”œโ”€โ”€ listServers(): 2 tests โ”œโ”€โ”€ handleSse(): 3 tests โ””โ”€โ”€ handleMessage(): 2 tests ``` ### Test Breakdown by Suite ``` 1. Security Decorators (4 tests) โ”œโ”€โ”€ JwtAuthGuard applied โ”œโ”€โ”€ listServers throttle (30 req/60s) โ”œโ”€โ”€ handleSse throttle (5 req/60s) โšก stricter โ””โ”€โ”€ handleMessage throttle (30 req/60s) 2. listServers (2 tests) โ”œโ”€โ”€ Returns server list โ””โ”€โ”€ Handles empty list 3. handleSse (3 tests) โ”œโ”€โ”€ Throws NOT_FOUND for missing server โ”œโ”€โ”€ Creates transport & connects โ””โ”€โ”€ Cleans up on connection close 4. handleMessage (2 tests) โ”œโ”€โ”€ Throws BAD_REQUEST for missing sessionId โ””โ”€โ”€ Throws NOT_FOUND for expired session ``` --- ## ๐ŸŽฏ Key Classes & Methods ### McpIntegrationModule ```typescript class McpIntegrationModule implements OnModuleInit { constructor( typesenseClient: TypesenseClientService, mcpRegistry: McpRegistryService, logger: LoggerService, ) {} async onModuleInit(): Promise { // 1. Set typesense client on registry // 2. Re-initialize servers // 3. Log initialized servers } } ``` ### McpTransportController ```typescript @Controller('mcp') @UseGuards(JwtAuthGuard) class McpTransportController { private transports: Map constructor(registry: McpRegistryService) {} @Get('servers') @Throttle(30/60s) listServers(): { servers: string[] } @Get(':serverName/sse') @Throttle(5/60s) // Stricter limit for SSE async handleSse(serverName, user, req, res): Promise @Post(':serverName/messages') @Throttle(30/60s) async handleMessage(serverName, user, req, res): Promise } ``` --- ## ๐Ÿ”ง Testing Patterns Used ### 1. Mock Pattern (Vitest) ```typescript // Mocking external module classes vi.mock('@goodgo/mcp-servers', () => ({ SSEServerTransport: class MockSSEServerTransport { sessionId = 'mock-session-id'; handlePostMessage = vi.fn().mockResolvedValue(undefined); constructor(path: string, res: unknown) {} }, })); ``` ### 2. Service Mock Pattern ```typescript const mockRegistry = { getServerNames: vi.fn(), getServer: vi.fn(), }; ``` ### 3. Decorator Verification Pattern ```typescript // Using Reflect API for NestJS decorators const guards = Reflect.getMetadata('__guards__', McpTransportController); const throttleLimit = Reflect.getMetadata( 'THROTTLER:LIMITdefault', McpTransportController.prototype.listServers, ); ``` ### 4. Error Testing Pattern ```typescript // Testing HTTP errors await expect( controller.handleSse('nonexistent', user, req, res) ).rejects.toThrow(HttpException); // Checking status code try { await controller.handleSse(...); } catch (error) { expect((error as HttpException).getStatus()).toBe(HttpStatus.NOT_FOUND); } ``` --- ## ๐Ÿ“š Testing Patterns from Other Modules ### Auth Module - Simple Handler Pattern ```typescript // Minimal dependencies, focused testing describe('LoginUserHandler', () => { let handler: LoginUserHandler; let mockTokenService = { generateTokenPair: vi.fn() }; beforeEach(() => { handler = new LoginUserHandler(mockTokenService as any); }); it('generates token pair', async () => { mockTokenService.generateTokenPair.mockResolvedValue(tokenPair); const result = await handler.execute(command); expect(result).toEqual(tokenPair); }); }); ``` ### Payments Module - Complex Handler Pattern ```typescript // Multiple dependencies, rich testing scenarios describe('CreatePaymentHandler', () => { let handler: CreatePaymentHandler; let mockPaymentRepo: { [K in keyof IPaymentRepository]: ReturnType }; let mockGatewayFactory: { getGateway: ReturnType }; let mockEventBus: { publish: ReturnType }; beforeEach(() => { mockPaymentRepo = { findById: vi.fn(), save: vi.fn().mockResolvedValue(undefined), // ... more methods }; handler = new CreatePaymentHandler(mockPaymentRepo as any, ...); }); it('creates payment successfully', async () => { // Rich test scenario with multiple assertions expect(result.paymentId).toBeDefined(); expect(mockPaymentRepo.save).toHaveBeenCalledTimes(1); expect(mockEventBus.publish).toHaveBeenCalled(); }); }); ``` ### Payments Domain - DDD Pattern ```typescript // Explicit imports, entity behavior testing import { describe, it, expect } from 'vitest'; describe('PaymentEntity', () => { it('should create payment with events', () => { const payment = PaymentEntity.createNew(...); expect(payment.status).toBe('PENDING'); expect(payment.domainEvents).toHaveLength(1); expect(payment.domainEvents[0]).toBeInstanceOf(PaymentCreatedEvent); }); it('should not refund non-completed payment', () => { const payment = createPayment(); // helper const result = payment.markRefunded(); expect(result.isErr).toBe(true); }); }); ``` ### Infrastructure Service - Crypto Pattern ```typescript // Complex external service testing describe('ZalopayService', () => { let service: ZalopayService; beforeEach(() => { const mockConfig = { get: vi.fn((key) => env[key]), getOrThrow: vi.fn((key) => env[key] || throw), }; service = new ZalopayService(mockConfig as any); }); it('should verify valid callback', () => { const mac = crypto.createHmac('sha256', key2).update(dataStr).digest('hex'); const result = service.verifyCallback({ data: dataStr, mac }); expect(result.isValid).toBe(true); expect(result.orderId).toBe('expected-order-id'); }); }); ``` --- ## ๐Ÿš€ Running Tests ### Test Commands ```bash # Run all MCP tests pnpm test -- src/modules/mcp # Run with watch mode pnpm test -- --watch src/modules/mcp # Run with coverage pnpm test -- --coverage src/modules/mcp # Run specific test file pnpm test -- src/modules/mcp/presentation/__tests__/mcp-transport.controller.spec.ts ``` ### Vitest Configuration ```typescript // apps/api/vitest.config.ts { test: { globals: true, // vi, describe, it, expect available globally environment: 'node', include: ['src/**/*.spec.ts'], // Matches *.spec.ts pattern exclude: ['**/*.integration.spec.ts', 'node_modules'], } } ``` --- ## ๐Ÿ’ก Recommendations ### Immediate (High Priority) - โœ… Controller is well tested - maintain this - ๐Ÿ“ Add tests for `McpIntegrationModule.onModuleInit()` - ๐Ÿ“ Test module dependency injection ### Future (Lower Priority) - ๐Ÿ—๏ธ If domain logic is added, create domain tests - ๐Ÿ—๏ธ If application handlers are added, follow payments module pattern - ๐Ÿ—๏ธ Add integration tests for full SSE lifecycle ### Best Practices to Follow 1. **Use globals pattern** for simple tests (like existing controller tests) 2. **Use explicit imports** for complex domain tests 3. **Use helper factories** for complex entity setup 4. **Use Reflect API** for decorator verification 5. **Test both happy path AND error cases** 6. **Use regex matching** for error message assertions 7. **Verify service calls** with `toHaveBeenCalledWith()` --- ## ๐Ÿ“– File Location Reference ``` GoodGo Platform Root โ”œโ”€โ”€ apps/api/src/modules/mcp/ โ† MCP Module โ”‚ โ”œโ”€โ”€ index.ts โ”‚ โ”œโ”€โ”€ mcp.module.ts โ”‚ โ””โ”€โ”€ presentation/ โ”‚ โ”œโ”€โ”€ mcp-transport.controller.ts โ”‚ โ””โ”€โ”€ __tests__/ โ”‚ โ””โ”€โ”€ mcp-transport.controller.spec.ts โ”‚ โ”œโ”€โ”€ apps/api/vitest.config.ts โ† Test config โ”œโ”€โ”€ apps/api/package.json โ† Test scripts โ”‚ โ””โ”€โ”€ MCP_MODULE_EXPLORATION.md โ† Full documentation ``` --- Generated: April 11, 2026