# Khám Phá Module MCP - Nền Tảng GoodGo ## 1. CẤU TRÚC MODULE VÀ TỆP NGUỒN ### Cấu Trúc Thư Mục ``` apps/api/src/modules/mcp/ ├── index.ts ├── mcp.module.ts └── presentation/ ├── mcp-transport.controller.ts └── __tests__/ └── mcp-transport.controller.spec.ts ``` ### Tất Cả Tệp Nguồn (4 tệp) #### 1. **apps/api/src/modules/mcp/index.ts** - **Loại**: Điểm vào module (xuất khẩu) - **Số dòng**: 1 - **Mục đích**: Xuất khẩu McpIntegrationModule - **Xuất khẩu**: `{ McpIntegrationModule }` #### 2. **apps/api/src/modules/mcp/mcp.module.ts** - **Loại**: Cấu hình Module NestJS (22 dòng) - **Lớp chính**: `McpIntegrationModule implements OnModuleInit` - **Trách nhiệm**: - Thiết lập module MCP cốt lõi với cấu hình - Khởi tạo TypesenseClient cho registry MCP - Ghi nhật ký tên máy chủ đã khởi tạo khi module khởi động - **Các phụ thuộc được tiêm**: - `TypesenseClientService` (từ SearchModule) - `McpRegistryService` (từ @goodgo/mcp-servers) - `LoggerService` (từ SharedModule) - **Nhập khẩu**: SearchModule, AuthModule, McpCoreModule.forRoot() - **Controllers**: McpTransportController - **Vòng đời**: Triển khai `onModuleInit()` #### 3. **apps/api/src/modules/mcp/presentation/mcp-transport.controller.ts** - **Loại**: Controller NestJS (102 dòng) - **Lớp chính**: `McpTransportController` - **Trách nhiệm**: Lớp truyền tải HTTP cho các kết nối SSE của MCP - **Decorator**: @ApiTags, @ApiBearerAuth, @Controller, @UseGuards(JwtAuthGuard) - **Thuộc tính**: `transports: Map` (quản lý phiên) - **Được tiêm**: `registry: McpRegistryService` **Các endpoint:** 1. **GET /mcp/servers** - Liệt kê các máy chủ MCP khả dụng (Throttle: 30/60 giây) 2. **GET /mcp/:serverName/sse** - Mở kết nối SSE (Throttle: 5/60 giây, giới hạn chặt hơn) 3. **POST /mcp/:serverName/messages** - Gửi tin nhắn tới phiên (Throttle: 30/60 giây) #### 4. **apps/api/src/modules/mcp/presentation/__tests__/mcp-transport.controller.spec.ts** - **Loại**: Bộ kiểm thử đơn vị (174 dòng) - **Framework**: Vitest - **Kiểm thử**: 11 tổng cộng trong 4 khối describe - **Phạm vi bao phủ**: Decorator bảo mật, listServers, handleSse, handleMessage ## 2. TÓM TẮT CÁC TỆP KIỂM THỬ ### Tổng cộng: 1 tệp kiểm thử (174 dòng) **Tệp**: `mcp-transport.controller.spec.ts` - **Bộ kiểm thử**: 4 khối describe - **Tổng số kiểm thử**: 11 - **Trạng thái**: ✅ Được kiểm thử đầy đủ - **Phạm vi bao phủ**: Các endpoint controller, bảo mật, xử lý lỗi ## 3. CẤU TRÚC LỚP DDD **Trạng thái hiện tại**: ĐƠN GIẢN HÓA (chỉ có lớp Presentation) ### ✅ Những gì TỒN TẠI: - **Lớp Presentation** - `presentation/mcp-transport.controller.ts` (102 dòng) - `presentation/__tests__/mcp-transport.controller.spec.ts` (174 dòng) ### ❌ Những gì KHÔNG TỒN TẠI: - **Lớp Domain** - Không có entity, value object, event - **Lớp Application** - Không có handler, command, query - **Lớp Infrastructure** - Không có repository, adapter ### Ghi chú kiến trúc: - Hoạt động như lớp bọc tích hợp cho @goodgo/mcp-servers - Cầu nối HTTP-to-SSE đơn giản - Theo dõi phiên trong bộ nhớ thông qua Map - Không có logic nghiệp vụ hay lưu trữ dữ liệu ## 4. CÁC LỚP/HANDLER CHÍNH ### McpIntegrationModule - **Trạng thái**: ⚠️ Được kiểm thử một phần - **Phương thức**: constructor, onModuleInit() - **Kiểm thử cần thêm**: Khởi tạo module, tích hợp service ### McpTransportController - **Trạng thái**: ✅ Được kiểm thử đầy đủ - **Phương thức**: listServers(), handleSse(), handleMessage() - **Kiểm thử**: 11 bài kiểm thử toàn diện bao phủ tất cả phương thức ## 5. MẪU KIỂM THỬ TỪ CÁC MODULE KHÁC ### Mẫu 1: Module Auth (Handler đơn giản) ```typescript 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); }); }); ``` ### Mẫu 2: Module Payments (Handler phức tạp) ```typescript describe('CreatePaymentHandler', () => { let handler: CreatePaymentHandler; let mockPaymentRepo: { [K in keyof IPaymentRepository]: ReturnType }; let mockGatewayFactory: { getGateway: ReturnType }; beforeEach(() => { mockPaymentRepo = { findById: vi.fn(), save: vi.fn().mockResolvedValue(undefined), }; handler = new CreatePaymentHandler(mockPaymentRepo as any, mockGatewayFactory as any); }); it('creates payment successfully', async () => { const result = await handler.execute(command); expect(result.paymentId).toBeDefined(); expect(mockPaymentRepo.save).toHaveBeenCalledTimes(1); }); }); ``` ### Mẫu 3: Kiểm thử Domain Entity ```typescript 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); }); }); ``` ### Mẫu 4: Kiểm thử Service Infrastructure ```typescript 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); }); }); ``` ## 6. TÓM TẮT QUY ƯỚC KIỂM THỬ ### Framework và Thiết lập - **Framework**: Vitest - **Môi trường**: Node.js - **Globals**: Được bật - **Mẫu tệp**: `*.spec.ts` - **Tổ chức kiểm thử**: Thư mục con `__tests__/` ### Mẫu Mock 1. **Service Mock**: `mockService = { method: vi.fn().mockResolvedValue(value) }` 2. **Module Mock**: `vi.mock('@goodgo/mcp-servers', () => ({ ... }))` 3. **Configuration Mock**: `get: vi.fn((key) => env[key])` ### Cấu Trúc Kiểm Thử ```typescript describe('ClassName', () => { let instance: ClassName; let mockDep1: Mock; beforeEach(() => { mockDep1 = { method: vi.fn() }; instance = new ClassName(mockDep1 as any); }); describe('methodName', () => { it('happy path', async () => { mockDep1.method.mockResolvedValue(expected); const result = await instance.method(); expect(result).toEqual(expected); }); }); }); ``` ### Lệnh Kiểm Thử ```bash # Chạy kiểm thử MCP pnpm test -- src/modules/mcp # Chế độ theo dõi pnpm test -- --watch src/modules/mcp # Với độ bao phủ pnpm test -- --coverage src/modules/mcp ``` ## 7. KHUYẾN NGHỊ ### Tức thì - ✅ Duy trì độ bao phủ kiểm thử controller - 📝 Thêm kiểm thử cho McpIntegrationModule.onModuleInit() - 📝 Kiểm thử tiêm phụ thuộc module ### Tương lai - 🏗️ Thêm kiểm thử tích hợp cho vòng đời SSE đầy đủ - 🏗️ Nếu thêm logic domain, tuân theo mẫu module payments - 🏗️ Nếu thêm handler, sử dụng mẫu CQRS từ các module auth/payments