Skip to main content
Glama
DEVELOPER_GUIDE.md38.3 kB
# Developer Guide ## Secure MCP Server - Development Documentation ### Table of Contents 1. [Getting Started](#getting-started) 2. [Development Environment Setup](#development-environment-setup) 3. [Architecture Overview](#architecture-overview) 4. [Code Structure](#code-structure) 5. [Development Workflow](#development-workflow) 6. [API Development](#api-development) 7. [Tool Development](#tool-development) 8. [Testing Guidelines](#testing-guidelines) 9. [Code Quality Standards](#code-quality-standards) 10. [Security Guidelines](#security-guidelines) 11. [Performance Guidelines](#performance-guidelines) 12. [Contributing Guidelines](#contributing-guidelines) 13. [Release Process](#release-process) ## Getting Started ### Prerequisites - Node.js 20+ and npm 10+ - Docker Desktop 4.0+ - Git 2.30+ - Visual Studio Code (recommended) - PostgreSQL client tools - Redis client tools ### Quick Start ```bash # Clone the repository git clone https://github.com/enterprise/secure-mcp-server.git cd secure-mcp-server # Install dependencies npm install # Set up environment cp .env.example .env.development # Edit .env.development with your local settings # Start development dependencies docker-compose -f docker-compose.dev.yml up -d # Initialize database npm run db:migrate:dev npm run db:seed:dev # Start development server with hot reload npm run dev # In another terminal, run tests in watch mode npm run test:watch ``` ## Development Environment Setup ### Required Tools Installation ```bash # macOS brew install node@20 postgresql redis docker kubectl helm brew install --cask visual-studio-code docker postman # Ubuntu/Debian sudo apt update sudo apt install nodejs npm postgresql-client redis-tools docker.io sudo snap install code postman kubectl helm # Windows (using Chocolatey) choco install nodejs postgresql redis docker-desktop vscode postman ``` ### VSCode Extensions ```json // .vscode/extensions.json { "recommendations": [ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "ms-azuretools.vscode-docker", "ms-kubernetes-tools.vscode-kubernetes-tools", "prisma.prisma", "redhat.vscode-yaml", "streetsidesoftware.code-spell-checker", "wayou.vscode-todo-highlight", "gruntfuggly.todo-tree", "eamodio.gitlens", "mhutchie.git-graph", "formulahendry.auto-rename-tag", "christian-kohler.path-intellisense", "visualstudioexptteam.vscodeintellicode", "ms-vscode.vscode-typescript-tslint-plugin" ] } ``` ### VSCode Settings ```json // .vscode/settings.json { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "typescript.updateImportsOnFileMove.enabled": "always", "typescript.preferences.importModuleSpecifier": "relative", "files.exclude": { "**/node_modules": true, "**/dist": true, "**/.git": true }, "search.exclude": { "**/node_modules": true, "**/dist": true, "**/coverage": true }, "todo-tree.general.tags": [ "TODO", "FIXME", "HACK", "NOTE", "SECURITY", "PERFORMANCE" ] } ``` ### Environment Configuration ```bash # .env.development NODE_ENV=development PORT=3000 HOST=localhost # Database DATABASE_URL=postgresql://dev_user:dev_password@localhost:5432/mcp_development DATABASE_LOG=true DATABASE_POOL_MIN=1 DATABASE_POOL_MAX=5 # Redis REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD=dev_password # Authentication JWT_SECRET=development_jwt_secret_change_in_production JWT_EXPIRY=1d REFRESH_TOKEN_EXPIRY=7d # Security (relaxed for development) CORS_ORIGINS=http://localhost:3000,http://localhost:3001 RATE_LIMIT_ENABLED=false HTTPS_ENABLED=false # Logging LOG_LEVEL=debug LOG_FORMAT=pretty # Development tools DEBUG=mcp:* REPL_ENABLED=true SWAGGER_ENABLED=true ``` ## Architecture Overview ### System Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Client Layer │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ MCP Client │ │ Web Client │ │ CLI Tool │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ └─────────────────┼─────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────┐ │ Transport Layer │ │ ┌─────────────────────────────────────────────────┐ │ │ │ WebSocket / HTTP Gateway │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────┐ │ Application Layer │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │Auth Service │ │Tool Registry │ │Context Mgmt │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │Rate Limiter │ │ Validator │ │ Logger │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────┐ │ Data Layer │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ PostgreSQL │ │ Redis │ │ Vault │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### Design Patterns #### 1. Repository Pattern ```typescript // Base repository interface interface Repository<T> { findById(id: string): Promise<T | null>; findAll(filter?: Partial<T>): Promise<T[]>; create(data: Omit<T, 'id'>): Promise<T>; update(id: string, data: Partial<T>): Promise<T>; delete(id: string): Promise<boolean>; } // Implementation class UserRepository implements Repository<User> { constructor(private db: PrismaClient) {} async findById(id: string): Promise<User | null> { return this.db.user.findUnique({ where: { id } }); } async findAll(filter?: Partial<User>): Promise<User[]> { return this.db.user.findMany({ where: filter }); } async create(data: CreateUserDto): Promise<User> { return this.db.user.create({ data }); } async update(id: string, data: UpdateUserDto): Promise<User> { return this.db.user.update({ where: { id }, data }); } async delete(id: string): Promise<boolean> { const result = await this.db.user.delete({ where: { id } }); return !!result; } } ``` #### 2. Service Layer Pattern ```typescript // Service layer for business logic class UserService { constructor( private userRepo: UserRepository, private emailService: EmailService, private auditService: AuditService ) {} async registerUser(dto: RegisterUserDto): Promise<User> { // Validate business rules await this.validateEmailUniqueness(dto.email); await this.validatePasswordStrength(dto.password); // Hash password const hashedPassword = await bcrypt.hash(dto.password, 12); // Create user const user = await this.userRepo.create({ ...dto, password: hashedPassword }); // Send welcome email await this.emailService.sendWelcomeEmail(user); // Audit log await this.auditService.log('user.created', { userId: user.id }); return user; } private async validateEmailUniqueness(email: string): Promise<void> { const existing = await this.userRepo.findByEmail(email); if (existing) { throw new ConflictError('Email already registered'); } } private validatePasswordStrength(password: string): void { const requirements = [ { regex: /.{12,}/, message: 'Password must be at least 12 characters' }, { regex: /[A-Z]/, message: 'Password must contain uppercase letter' }, { regex: /[a-z]/, message: 'Password must contain lowercase letter' }, { regex: /[0-9]/, message: 'Password must contain number' }, { regex: /[^A-Za-z0-9]/, message: 'Password must contain special character' } ]; for (const req of requirements) { if (!req.regex.test(password)) { throw new ValidationError(req.message); } } } } ``` #### 3. Dependency Injection ```typescript // DI Container setup import { Container } from 'inversify'; const container = new Container(); // Bind dependencies container.bind<PrismaClient>(TYPES.Database).toConstantValue(new PrismaClient()); container.bind<Redis>(TYPES.Cache).toConstantValue(new Redis()); container.bind<UserRepository>(TYPES.UserRepository).to(UserRepository); container.bind<UserService>(TYPES.UserService).to(UserService); // Usage in controller class UserController { constructor( @inject(TYPES.UserService) private userService: UserService ) {} async register(req: Request, res: Response) { const user = await this.userService.registerUser(req.body); res.status(201).json(user); } } ``` ## Code Structure ### Directory Structure ``` src/ ├── auth/ # Authentication & authorization │ ├── strategies/ # Passport strategies │ ├── guards/ # Route guards │ ├── decorators/ # Custom decorators │ ├── jwt.service.ts # JWT handling │ └── auth.module.ts # Module definition │ ├── tools/ # MCP tool implementations │ ├── base/ # Base tool classes │ ├── builtin/ # Built-in tools │ ├── custom/ # Custom tools │ ├── registry.ts # Tool registry │ └── validator.ts # Tool validation │ ├── server/ # Server implementations │ ├── http/ # HTTP server │ ├── websocket/ # WebSocket server │ ├── middleware/ # Express middleware │ └── routes/ # API routes │ ├── database/ # Database layer │ ├── entities/ # Database entities │ ├── repositories/ # Repository implementations │ ├── migrations/ # Database migrations │ └── seeds/ # Seed data │ ├── config/ # Configuration │ ├── default.ts # Default config │ ├── development.ts # Dev config │ ├── production.ts # Prod config │ └── index.ts # Config loader │ ├── utils/ # Utility functions │ ├── logger.ts # Logging utility │ ├── validator.ts # Validation helpers │ ├── crypto.ts # Cryptography helpers │ └── errors.ts # Custom error classes │ ├── monitoring/ # Monitoring & metrics │ ├── metrics.ts # Prometheus metrics │ ├── health.ts # Health checks │ └── tracing.ts # Distributed tracing │ └── index.ts # Application entry point ``` ### Module Structure ```typescript // Example module structure (auth.module.ts) import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; @Module({ imports: [ PassportModule.register({ defaultStrategy: 'jwt' }), JwtModule.register({ secret: process.env.JWT_SECRET, signOptions: { expiresIn: '1h' } }) ], providers: [ AuthService, JwtStrategy, LocalStrategy, SamlStrategy ], controllers: [AuthController], exports: [AuthService] }) export class AuthModule {} ``` ## Development Workflow ### Git Workflow ```bash # 1. Create feature branch git checkout -b feature/MCP-123-add-new-tool # 2. Make changes and commit git add . git commit -m "feat(tools): add new filesystem tool - Implement read/write operations - Add comprehensive tests - Update documentation Closes #123" # 3. Keep branch updated git fetch origin git rebase origin/main # 4. Push changes git push origin feature/MCP-123-add-new-tool # 5. Create pull request gh pr create --title "feat(tools): add new filesystem tool" \ --body "## Description\nAdds new filesystem tool with read/write operations\n\n## Testing\n- Unit tests added\n- Integration tests passing\n\n## Checklist\n- [ ] Tests passing\n- [ ] Documentation updated\n- [ ] Security review completed" ``` ### Commit Message Format ``` <type>(<scope>): <subject> <body> <footer> ``` Types: - `feat`: New feature - `fix`: Bug fix - `docs`: Documentation changes - `style`: Code style changes - `refactor`: Code refactoring - `test`: Test changes - `chore`: Build/config changes - `perf`: Performance improvements - `security`: Security improvements ### Branch Naming Convention ``` feature/MCP-<ticket>-<description> # New features bugfix/MCP-<ticket>-<description> # Bug fixes hotfix/MCP-<ticket>-<description> # Production hotfixes release/<version> # Release branches chore/<description> # Maintenance tasks ``` ## API Development ### Creating New Endpoints ```typescript // 1. Define DTO (Data Transfer Object) // dto/create-tool.dto.ts import { IsString, IsObject, IsBoolean, IsOptional } from 'class-validator'; export class CreateToolDto { @IsString() name: string; @IsString() description: string; @IsObject() schema: object; @IsBoolean() @IsOptional() enabled?: boolean; } // 2. Implement controller // controllers/tools.controller.ts import { Controller, Get, Post, Body, Param, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; @ApiTags('tools') @Controller('api/tools') @UseGuards(JwtAuthGuard) @ApiBearerAuth() export class ToolsController { constructor(private toolService: ToolService) {} @Get() @ApiOperation({ summary: 'List all tools' }) @UseInterceptors(CacheInterceptor) async listTools(@Query() query: ListToolsDto) { return this.toolService.findAll(query); } @Post() @ApiOperation({ summary: 'Create new tool' }) @Roles('admin') @UseGuards(RolesGuard) async createTool(@Body() dto: CreateToolDto) { return this.toolService.create(dto); } @Get(':id') @ApiOperation({ summary: 'Get tool by ID' }) async getTool(@Param('id') id: string) { const tool = await this.toolService.findById(id); if (!tool) { throw new NotFoundException(`Tool ${id} not found`); } return tool; } } // 3. Implement service // services/tools.service.ts @Injectable() export class ToolService { constructor( private toolRepo: ToolRepository, private validator: ToolValidator, private eventEmitter: EventEmitter2 ) {} async create(dto: CreateToolDto): Promise<Tool> { // Validate tool schema await this.validator.validateSchema(dto.schema); // Create tool const tool = await this.toolRepo.create(dto); // Emit event this.eventEmitter.emit('tool.created', { tool }); return tool; } async findAll(query: ListToolsDto): Promise<PaginatedResult<Tool>> { const { page = 1, limit = 20, ...filters } = query; const [tools, total] = await this.toolRepo.findAndCount({ where: filters, skip: (page - 1) * limit, take: limit }); return { data: tools, pagination: { page, limit, total, pages: Math.ceil(total / limit) } }; } } ``` ### API Documentation ```typescript // Swagger documentation @ApiTags('authentication') @Controller('api/auth') export class AuthController { @Post('login') @ApiOperation({ summary: 'User login', description: 'Authenticate user and return JWT tokens' }) @ApiBody({ description: 'Login credentials', type: LoginDto, examples: { default: { value: { email: 'user@example.com', password: 'SecurePassword123!' } } } }) @ApiResponse({ status: 200, description: 'Login successful', schema: { example: { accessToken: 'eyJhbGciOiJSUzI1NiIs...', refreshToken: 'eyJhbGciOiJSUzI1NiIs...', expiresIn: 3600 } } }) @ApiResponse({ status: 401, description: 'Invalid credentials' }) async login(@Body() dto: LoginDto) { return this.authService.login(dto); } } ``` ## Tool Development ### Creating Custom Tools ```typescript // 1. Define tool interface // tools/interfaces/tool.interface.ts export interface Tool { name: string; description: string; version: string; execute(params: any, context: ToolContext): Promise<ToolResult>; validate(params: any): ValidationResult; } // 2. Implement base tool class // tools/base/base-tool.ts export abstract class BaseTool implements Tool { abstract name: string; abstract description: string; version = '1.0.0'; protected logger: Logger; protected metrics: MetricsCollector; constructor() { this.logger = new Logger(this.constructor.name); this.metrics = new MetricsCollector(this.name); } abstract execute(params: any, context: ToolContext): Promise<ToolResult>; validate(params: any): ValidationResult { const schema = this.getSchema(); const { error } = schema.validate(params); if (error) { return { valid: false, errors: error.details.map(d => d.message) }; } return { valid: true }; } protected abstract getSchema(): Joi.Schema; protected async trackExecution<T>( fn: () => Promise<T> ): Promise<T> { const start = Date.now(); try { const result = await fn(); this.metrics.recordSuccess(Date.now() - start); return result; } catch (error) { this.metrics.recordError(Date.now() - start); throw error; } } } // 3. Implement custom tool // tools/custom/database-tool.ts export class DatabaseTool extends BaseTool { name = 'database'; description = 'Execute database queries'; constructor(private db: DatabaseConnection) { super(); } async execute(params: DatabaseParams, context: ToolContext): Promise<ToolResult> { return this.trackExecution(async () => { // Validate permissions if (!context.user.permissions.includes('database:query')) { throw new ForbiddenError('Insufficient permissions'); } // Log query for audit await this.auditLog(context.user, params.query); // Execute query with timeout const result = await this.db.query(params.query, { timeout: params.timeout || 30000 }); return { success: true, data: result.rows, metadata: { rowCount: result.rowCount, duration: result.duration } }; }); } protected getSchema(): Joi.Schema { return Joi.object({ query: Joi.string().required().max(10000), timeout: Joi.number().optional().min(1000).max(60000) }); } private async auditLog(user: User, query: string): Promise<void> { await this.logger.audit({ userId: user.id, action: 'database.query', details: { query }, timestamp: new Date() }); } } // 4. Register tool // tools/registry.ts export class ToolRegistry { private tools = new Map<string, Tool>(); register(tool: Tool): void { if (this.tools.has(tool.name)) { throw new Error(`Tool ${tool.name} already registered`); } this.tools.set(tool.name, tool); this.logger.info(`Registered tool: ${tool.name}`); } get(name: string): Tool | undefined { return this.tools.get(name); } list(): Tool[] { return Array.from(this.tools.values()); } } ``` ### Tool Testing ```typescript // tools/__tests__/database-tool.test.ts describe('DatabaseTool', () => { let tool: DatabaseTool; let mockDb: jest.Mocked<DatabaseConnection>; beforeEach(() => { mockDb = createMockDatabase(); tool = new DatabaseTool(mockDb); }); describe('execute', () => { it('should execute valid query', async () => { const params = { query: 'SELECT * FROM users LIMIT 10' }; const context = createMockContext({ user: { permissions: ['database:query'] } }); mockDb.query.mockResolvedValue({ rows: [{ id: 1, name: 'Test' }], rowCount: 1, duration: 50 }); const result = await tool.execute(params, context); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); expect(mockDb.query).toHaveBeenCalledWith( params.query, expect.objectContaining({ timeout: 30000 }) ); }); it('should reject without permissions', async () => { const context = createMockContext({ user: { permissions: [] } }); await expect( tool.execute({ query: 'SELECT 1' }, context) ).rejects.toThrow(ForbiddenError); }); it('should validate query parameters', () => { const result = tool.validate({ query: 'a'.repeat(10001) // Too long }); expect(result.valid).toBe(false); expect(result.errors).toContain('query exceeds maximum length'); }); }); }); ``` ## Testing Guidelines ### Test Structure ```typescript // Standard test structure describe('ComponentName', () => { // Setup and teardown beforeAll(async () => { // Global setup }); beforeEach(() => { // Test-specific setup }); afterEach(() => { // Cleanup }); afterAll(async () => { // Global cleanup }); // Group related tests describe('methodName', () => { it('should handle normal case', () => { // Arrange const input = createTestInput(); // Act const result = component.method(input); // Assert expect(result).toEqual(expectedOutput); }); it('should handle edge case', () => { // Test edge cases }); it('should handle error case', () => { // Test error scenarios }); }); }); ``` ### Testing Utilities ```typescript // test-utils/helpers.ts export function createMockRequest(overrides?: Partial<Request>): Request { return { body: {}, params: {}, query: {}, headers: {}, user: null, ...overrides } as Request; } export function createMockResponse(): Response { const res = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis(), send: jest.fn().mockReturnThis(), set: jest.fn().mockReturnThis() } as unknown as Response; return res; } export async function createTestDatabase(): Promise<PrismaClient> { const db = new PrismaClient({ datasources: { db: { url: 'postgresql://test:test@localhost:5433/test' } } }); await db.$executeRaw`CREATE SCHEMA IF NOT EXISTS test`; await db.$connect(); return db; } export class TestFixtures { static user(overrides?: Partial<User>): User { return { id: 'user_123', email: 'test@example.com', name: 'Test User', roles: ['user'], createdAt: new Date(), updatedAt: new Date(), ...overrides }; } static tool(overrides?: Partial<Tool>): Tool { return { id: 'tool_123', name: 'test-tool', description: 'Test tool', enabled: true, schema: {}, ...overrides }; } } ``` ### Integration Testing ```typescript // integration/api.test.ts describe('API Integration Tests', () => { let app: INestApplication; let db: PrismaClient; beforeAll(async () => { const moduleFixture = await Test.createTestingModule({ imports: [AppModule] }).compile(); app = moduleFixture.createNestApplication(); await app.init(); db = app.get(PrismaClient); }); afterAll(async () => { await db.$disconnect(); await app.close(); }); describe('POST /api/auth/login', () => { it('should authenticate valid user', async () => { // Create test user const user = await db.user.create({ data: { email: 'test@example.com', password: await bcrypt.hash('password123', 10) } }); const response = await request(app.getHttpServer()) .post('/api/auth/login') .send({ email: 'test@example.com', password: 'password123' }) .expect(200); expect(response.body).toHaveProperty('accessToken'); expect(response.body).toHaveProperty('refreshToken'); }); }); }); ``` ## Code Quality Standards ### ESLint Configuration ```javascript // .eslintrc.js module.exports = { parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', sourceType: 'module', }, plugins: ['@typescript-eslint', 'security', 'jest'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:security/recommended', 'plugin:jest/recommended', 'prettier' ], rules: { '@typescript-eslint/explicit-function-return-type': 'warn', '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 'security/detect-object-injection': 'warn', 'security/detect-non-literal-regexp': 'warn', 'no-console': ['error', { allow: ['warn', 'error'] }], 'complexity': ['error', 10], 'max-lines-per-function': ['error', 50], 'max-depth': ['error', 3] } }; ``` ### Prettier Configuration ```json // .prettierrc { "semi": true, "trailingComma": "none", "singleQuote": true, "printWidth": 100, "tabWidth": 2, "useTabs": false, "bracketSpacing": true, "arrowParens": "always", "endOfLine": "lf" } ``` ### Code Review Checklist - [ ] **Functionality**: Does the code do what it's supposed to do? - [ ] **Tests**: Are there adequate tests? Do they cover edge cases? - [ ] **Security**: Are there any security vulnerabilities? - [ ] **Performance**: Are there any performance concerns? - [ ] **Readability**: Is the code easy to understand? - [ ] **Maintainability**: Will this code be easy to modify? - [ ] **Documentation**: Is the code properly documented? - [ ] **Error Handling**: Are errors handled appropriately? - [ ] **Logging**: Is there appropriate logging? - [ ] **Dependencies**: Are new dependencies necessary and secure? ## Security Guidelines ### Security Best Practices ```typescript // 1. Input validation import { sanitize } from 'dompurify'; import { validate } from 'class-validator'; class SecurityMiddleware { async validateInput(req: Request, res: Response, next: NextFunction) { // Sanitize HTML content if (req.body.content) { req.body.content = sanitize(req.body.content); } // Validate against schema const errors = await validate(req.body); if (errors.length > 0) { return res.status(400).json({ errors }); } next(); } // 2. SQL injection prevention async secureQuery(query: string, params: any[]) { // Always use parameterized queries return db.query(query, params); // Never do: db.query(`SELECT * FROM users WHERE id = ${userId}`) } // 3. XSS prevention renderResponse(data: any) { return { ...data, // Escape all user-generated content content: this.escapeHtml(data.content) }; } // 4. CSRF protection verifyCsrfToken(req: Request) { const token = req.headers['x-csrf-token']; const sessionToken = req.session.csrfToken; if (!token || token !== sessionToken) { throw new ForbiddenError('Invalid CSRF token'); } } // 5. Rate limiting @RateLimit({ points: 100, duration: 60 }) async handleRequest(req: Request, res: Response) { // Process request } } ``` ### Secure Coding Checklist - [ ] Never trust user input - [ ] Use parameterized queries - [ ] Implement proper authentication - [ ] Use HTTPS everywhere - [ ] Store passwords using bcrypt/argon2 - [ ] Implement rate limiting - [ ] Use security headers - [ ] Validate all inputs - [ ] Escape all outputs - [ ] Keep dependencies updated - [ ] Use least privilege principle - [ ] Implement proper error handling - [ ] Add security logging - [ ] Regular security audits ## Performance Guidelines ### Performance Optimization ```typescript // 1. Database query optimization class OptimizedRepository { // Use indexing @Index(['email', 'status']) class User { email: string; status: string; } // Use projection to fetch only needed fields async findUsersLight() { return db.user.findMany({ select: { id: true, email: true, name: true } }); } // Use pagination async findPaginated(page: number, limit: number) { return db.user.findMany({ skip: (page - 1) * limit, take: limit }); } // Use database transactions async transferCredits(fromId: string, toId: string, amount: number) { return db.$transaction(async (tx) => { await tx.user.update({ where: { id: fromId }, data: { credits: { decrement: amount } } }); await tx.user.update({ where: { id: toId }, data: { credits: { increment: amount } } }); }); } } // 2. Caching strategy class CacheService { private cache: Redis; async getWithCache<T>( key: string, fetcher: () => Promise<T>, ttl: number = 3600 ): Promise<T> { // Try cache first const cached = await this.cache.get(key); if (cached) { return JSON.parse(cached); } // Fetch and cache const data = await fetcher(); await this.cache.setex(key, ttl, JSON.stringify(data)); return data; } async invalidate(pattern: string) { const keys = await this.cache.keys(pattern); if (keys.length > 0) { await this.cache.del(...keys); } } } // 3. Async processing class QueueService { async processAsync(job: Job) { // Add to queue for background processing await this.queue.add('process-job', job, { attempts: 3, backoff: { type: 'exponential', delay: 2000 } }); } } ``` ### Performance Monitoring ```typescript // Performance monitoring utilities class PerformanceMonitor { private metrics = new Map<string, number[]>(); measure(name: string, fn: () => void) { const start = performance.now(); fn(); const duration = performance.now() - start; if (!this.metrics.has(name)) { this.metrics.set(name, []); } this.metrics.get(name)!.push(duration); // Alert if slow if (duration > 1000) { logger.warn(`Slow operation: ${name} took ${duration}ms`); } } getStats(name: string) { const times = this.metrics.get(name) || []; return { count: times.length, avg: times.reduce((a, b) => a + b, 0) / times.length, min: Math.min(...times), max: Math.max(...times), p95: this.percentile(times, 95) }; } private percentile(arr: number[], p: number) { const sorted = [...arr].sort((a, b) => a - b); const index = Math.ceil((p / 100) * sorted.length) - 1; return sorted[index]; } } ``` ## Contributing Guidelines ### How to Contribute 1. **Fork the repository** ```bash gh repo fork enterprise/secure-mcp-server ``` 2. **Create feature branch** ```bash git checkout -b feature/your-feature ``` 3. **Make your changes** - Write clean, documented code - Add tests for new functionality - Update documentation 4. **Run tests** ```bash npm run test npm run lint npm run security:check ``` 5. **Submit pull request** - Fill out PR template - Link related issues - Request reviews ### Pull Request Template ```markdown ## Description Brief description of changes ## Type of Change - [ ] Bug fix - [ ] New feature - [ ] Breaking change - [ ] Documentation update ## Testing - [ ] Unit tests pass - [ ] Integration tests pass - [ ] Manual testing completed ## Checklist - [ ] Code follows style guidelines - [ ] Self-review completed - [ ] Documentation updated - [ ] No new warnings - [ ] Tests added - [ ] Security review if needed ``` ### Code of Conduct - Be respectful and inclusive - Welcome newcomers - Provide constructive feedback - Focus on what is best for the community - Show empathy towards others ## Release Process ### Version Management ```bash # Semantic versioning: MAJOR.MINOR.PATCH # MAJOR: Breaking changes # MINOR: New features (backward compatible) # PATCH: Bug fixes # Update version npm version patch # 1.0.0 -> 1.0.1 npm version minor # 1.0.1 -> 1.1.0 npm version major # 1.1.0 -> 2.0.0 ``` ### Release Checklist ```bash #!/bin/bash # release.sh VERSION=$1 echo "Releasing version $VERSION" # 1. Run tests npm test npm run test:integration npm run test:security # 2. Build npm run build # 3. Update changelog echo "## Version $VERSION - $(date +%Y-%m-%d)" >> CHANGELOG.md git log --pretty=format:"- %s" $(git describe --tags --abbrev=0)..HEAD >> CHANGELOG.md # 4. Commit changes git add . git commit -m "chore: release v$VERSION" # 5. Tag release git tag -a "v$VERSION" -m "Release v$VERSION" # 6. Push to repository git push origin main --tags # 7. Build Docker image docker build -t secure-mcp-server:$VERSION . docker tag secure-mcp-server:$VERSION secure-mcp-server:latest # 8. Push to registry docker push secure-mcp-server:$VERSION docker push secure-mcp-server:latest # 9. Deploy to staging kubectl set image deployment/mcp-server mcp-server=secure-mcp-server:$VERSION -n staging # 10. Run smoke tests npm run test:smoke echo "Release $VERSION completed!" ``` ### Deployment Pipeline ```yaml # .github/workflows/release.yml name: Release on: push: tags: - 'v*' jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '20' - name: Install dependencies run: npm ci - name: Run tests run: | npm test npm run test:integration - name: Build run: npm run build - name: Build Docker image run: | docker build -t secure-mcp-server:${{ github.ref_name }} . docker tag secure-mcp-server:${{ github.ref_name }} secure-mcp-server:latest - name: Push to registry run: | echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin docker push secure-mcp-server:${{ github.ref_name }} docker push secure-mcp-server:latest - name: Create release uses: softprops/action-gh-release@v1 with: files: | dist/** CHANGELOG.md generate_release_notes: true ``` ## Resources ### Documentation - [MCP Specification](https://modelcontextprotocol.io) - [TypeScript Documentation](https://www.typescriptlang.org/docs/) - [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) - [OWASP Security Guide](https://owasp.org/www-project-secure-coding-practices/) ### Tools - [VS Code](https://code.visualstudio.com/) - [Postman](https://www.postman.com/) - [Docker Desktop](https://www.docker.com/products/docker-desktop) - [pgAdmin](https://www.pgadmin.org/) - [Redis Commander](https://github.com/joeferner/redis-commander) ### Learning Resources - Internal Wiki: https://wiki.enterprise.com/mcp - Video Tutorials: https://learn.enterprise.com/mcp - Slack Channel: #mcp-development - Office Hours: Thursdays 2-3 PM ## Support - **Documentation**: [Full Documentation](./docs/) - **Issues**: [GitHub Issues](https://github.com/enterprise/secure-mcp-server/issues) - **Slack**: #mcp-development - **Email**: mcp-team@enterprise.com

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/perfecxion-ai/secure-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server