Files
mcpctl/.taskmaster/tasks/task_003.md

206 lines
8.5 KiB
Markdown
Raw Normal View History

2026-02-21 03:10:39 +00:00
# Task ID: 3
**Title:** Implement mcpd Core Server Framework
**Status:** pending
**Dependencies:** 1, 2
**Priority:** high
**Description:** Build the mcpd daemon server with Express/Fastify, including middleware for authentication, logging, and error handling. Design for horizontal scalability.
**Details:**
Create mcpd server in `src/mcpd/src/`:
```typescript
// server.ts
import Fastify from 'fastify';
import { PrismaClient } from '@prisma/client';
const app = Fastify({ logger: true });
const prisma = new PrismaClient();
// Middleware
app.register(require('@fastify/cors'));
app.register(require('@fastify/helmet'));
app.register(require('@fastify/rate-limit'), { max: 100, timeWindow: '1 minute' });
// Health check for load balancers
app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
// Auth middleware
app.addHook('preHandler', async (request, reply) => {
if (request.url === '/health') return;
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) return reply.status(401).send({ error: 'Unauthorized' });
// Validate token against session table
});
// Audit logging middleware
app.addHook('onResponse', async (request, reply) => {
await prisma.auditLog.create({
data: {
action: request.method,
resource: request.url,
details: { statusCode: reply.statusCode },
userId: request.user?.id
}
});
});
```
Design principles:
- Stateless: All state in PostgreSQL, no in-memory session storage
- Scalable: Can run multiple instances behind load balancer
- Configurable via environment variables
- Graceful shutdown handling
**Test Strategy:**
Unit test middleware functions. Integration test health endpoint. Load test with multiple concurrent requests. Verify statelessness by running two instances and alternating requests.
## Subtasks
### 3.1. Set up mcpd package structure with clean architecture layers and TDD infrastructure
**Status:** pending
**Dependencies:** None
Create the src/mcpd directory structure following clean architecture principles with separate layers for routes, controllers, services, and repositories, along with Vitest test configuration.
**Details:**
Create src/mcpd/src/ directory structure with the following layers:
- routes/ - HTTP route definitions (thin layer, delegates to controllers)
- controllers/ - Request/response handling, input validation
- services/ - Business logic, orchestrates repositories
- repositories/ - Data access layer, Prisma abstraction
- middleware/ - Auth, logging, error handling, rate limiting
- config/ - Environment configuration with Zod validation
- types/ - TypeScript interfaces for dependency injection
- utils/ - Utility functions (graceful shutdown, health checks)
Create src/mcpd/tests/ with matching structure:
- unit/ (routes, controllers, services, repositories, middleware)
- integration/ (API endpoint tests)
- fixtures/ (mock data, Prisma mock setup)
Set up vitest.config.ts extending root config with mcpd-specific settings. Create test-utils.ts with Prisma mock factory and Fastify test helpers. Install dependencies: fastify, @fastify/cors, @fastify/helmet, @fastify/rate-limit, zod, pino. DevDependencies: vitest, @vitest/coverage-v8, supertest.
### 3.2. Implement Fastify server core with health endpoint and database connectivity verification
**Status:** pending
**Dependencies:** 3.1
Create the core Fastify server with health check endpoint that verifies PostgreSQL database connectivity, environment configuration validation, and server lifecycle management.
**Details:**
Create src/mcpd/src/server.ts with Fastify instance factory function createServer(config: ServerConfig) for testability via dependency injection. Implement:
- config/env.ts: Zod schema for environment variables (DATABASE_URL, PORT, NODE_ENV, LOG_LEVEL)
- config/index.ts: loadConfig() function that validates env with Zod
- utils/health.ts: checkDatabaseConnectivity(prisma) function
- routes/health.ts: GET /health endpoint returning { status: 'ok' | 'degraded', timestamp: ISO8601, db: 'connected' | 'disconnected' }
Server requirements:
- Fastify with pino logger enabled (configurable log level)
- Health endpoint bypasses auth middleware
- Health endpoint checks actual DB connectivity via prisma.$queryRaw
- Server does NOT start if DATABASE_URL is missing (fail fast)
- Export createServer() and startServer() separately for testing
Write TDD tests FIRST in tests/unit/routes/health.test.ts and tests/unit/config/env.test.ts before implementing.
### 3.3. Implement authentication middleware with JWT validation and session management
**Status:** pending
**Dependencies:** 3.2
Create authentication preHandler hook that validates Bearer tokens against the Session table in PostgreSQL, with proper error responses and request decoration for downstream handlers.
**Details:**
Create src/mcpd/src/middleware/auth.ts with:
- authMiddleware(prisma: PrismaClient) factory function (dependency injection)
- Fastify preHandler hook implementation
- Extract Bearer token from Authorization header
- Validate token exists and format is correct
- Query Session table: find by token, check expiresAt > now()
- Query User by session.userId for request decoration
- Decorate request with user: { id, email, name } via fastify.decorateRequest
- Return 401 Unauthorized with { error: 'Unauthorized', code: 'TOKEN_REQUIRED' } for missing token
- Return 401 with { error: 'Unauthorized', code: 'TOKEN_EXPIRED' } for expired session
- Return 401 with { error: 'Unauthorized', code: 'TOKEN_INVALID' } for invalid token
Create types/fastify.d.ts with FastifyRequest augmentation for user property.
Write unit tests in tests/unit/middleware/auth.test.ts with mocked Prisma client before implementation.
### 3.4. Implement security middleware stack with CORS, Helmet, rate limiting, and input sanitization
**Status:** pending
**Dependencies:** 3.2
Configure and register security middleware including CORS policy, Helmet security headers, rate limiting, and create input sanitization utilities to prevent injection attacks.
**Details:**
Create src/mcpd/src/middleware/security.ts with:
- registerSecurityPlugins(app: FastifyInstance, config: SecurityConfig) function
- CORS configuration: configurable origins (default: same-origin for production, * for development), credentials support, allowed methods/headers
- Helmet configuration: contentSecurityPolicy, hsts (enabled in production), noSniff, frameguard
- Rate limiting: 100 requests per minute default, configurable via env, different limits for auth endpoints (stricter)
Create src/mcpd/src/utils/sanitize.ts:
- sanitizeInput(input: unknown): sanitized value
- stripHtmlTags(), escapeHtml() for XSS prevention
- Validate JSON input doesn't exceed size limits
Create src/mcpd/src/middleware/validate.ts:
- createValidationMiddleware(schema: ZodSchema) factory
- Validates request.body against Zod schema
- Returns 400 Bad Request with Zod errors formatted
Document security decisions in src/mcpd/SECURITY.md with rationale for each configuration choice.
### 3.5. Implement error handling, audit logging middleware, and graceful shutdown with comprehensive tests
**Status:** pending
**Dependencies:** 3.2, 3.3, 3.4
Create global error handler, audit logging onResponse hook that records all operations to database, and graceful shutdown handling with connection draining and proper signal handling.
**Details:**
Create src/mcpd/src/middleware/error-handler.ts:
- Global Fastify error handler via setErrorHandler
- Handle Zod validation errors -> 400 Bad Request
- Handle Prisma errors (P2002 unique, P2025 not found) -> appropriate HTTP codes
- Handle custom application errors with error codes
- Log errors with pino, include stack trace in development only
- Never expose internal errors to clients in production
Create src/mcpd/src/middleware/audit.ts:
- auditMiddleware(prisma: PrismaClient, auditLogger: AuditLogger) factory
- Fastify onResponse hook
- Create AuditLog record with: userId (from request.user), action (HTTP method), resource (URL), details ({ statusCode, responseTime, ip })
- Skip audit logging for /health endpoint
- Async write - don't block response
- Handle audit write failures gracefully (log warning, don't fail request)
Create src/mcpd/src/utils/shutdown.ts:
- setupGracefulShutdown(app: FastifyInstance, prisma: PrismaClient) function
- Handle SIGTERM, SIGINT signals
- Stop accepting new connections
- Wait for in-flight requests (configurable timeout, default 30s)
- Disconnect Prisma client
- Exit with appropriate code
Create services/audit-logger.ts interface that Task 14 will implement.