# Technical Research: Mock GDS MCP Server **Branch**: `001-mock-gds-server` | **Date**: 2026-04-07 ## Phase 0: Technology Decisions ### Decision 1: MCP SDK and Protocol Implementation **Decision**: Use `@modelcontextprotocol/sdk` official Node.js SDK **Rationale**: - Official SDK ensures MCP protocol compliance (Constitution Principle I) - Handles JSON-RPC 2.0 message format automatically - Provides TypeScript types for type safety - Active maintenance by Anthropic - Simplified tool registration and schema management **Alternatives Considered**: - **Custom MCP implementation**: Rejected - high risk of protocol non-compliance, significant development effort - **Python MCP SDK**: Rejected - requirement specifies Node.js with minimal dependencies **Implementation Notes**: - SDK provides `Server` class for initialization - Tool handlers registered via `server.setRequestHandler` - Automatic capability negotiation during handshake - Built-in error handling with standard MCP error codes ### Decision 2: Valkey Client Library **Decision**: Use `ioredis` v5.x as Valkey client **Rationale**: - Valkey is Redis protocol-compatible, ioredis is the most mature Node.js Redis client - Full support for Redis/Valkey commands (GET, SET, HSET, EXPIRE, etc.) - Connection pooling and automatic reconnection - Cluster and sentinel support (for future scaling) - Pipeline and transaction support - Active maintenance and TypeScript support **Alternatives Considered**: - **node-redis**: Rejected - ioredis has better TypeScript support and more features - **Custom Valkey protocol**: Rejected - unnecessarily complex - **No persistence (memory-only)**: Rejected - requirement specifies Valkey for persistence **Implementation Notes**: - Use Redis-compatible commands only (avoid Redis-specific extensions) - Session data stored with TTL (e.g., 1 hour default) - Key naming: `gds:session:{sessionId}:bookings:{pnr}` - Use hash structures for complex objects (bookings, searches) - Enable Valkey RDB/AOF persistence via docker-compose configuration ### Decision 3: Minimal Dependencies Strategy **Decision**: Limit external dependencies to essentials only **Core Dependencies** (production): 1. `@modelcontextprotocol/sdk` - MCP protocol (required) 2. `ioredis` - Valkey client (required for persistence) 3. `pino` - Structured logging (minimal, fast, constitution requires observability) **Development Dependencies**: - Node.js native test runner (`node:test`) - no jest/mocha overhead - `c8` - code coverage (lightweight) **Rationale**: - Aligns with "minimal libraries" requirement - Reduces attack surface and dependency maintenance burden - Faster container builds and smaller images - Native Node.js features (test runner, assert) are mature and sufficient **Explicitly Avoided**: - ❌ Express/Fastify/Koa - MCP uses stdio/SSE, no HTTP server needed - ❌ TypeORM/Prisma - direct Valkey commands sufficient for key-value storage - ❌ Jest/Mocha - native test runner adequate - ❌ Lodash/Ramda - native JS methods sufficient - ❌ Moment.js/date-fns - native Date and Temporal API (when available) ### Decision 4: Docker Build Strategy **Decision**: Use Docker Buildx with `docker-bake.hcl` for multi-platform builds **Rationale**: - `docker buildx bake` supports complex build configurations - Multi-platform builds (linux/amd64, linux/arm64) in single command - Build matrix for multiple tags (latest, version, dev) - Better caching and parallelization than traditional Dockerfile - Aligns with requirement specification **Build Configuration**: ```hcl # docker-bake.hcl target "default" { dockerfile = "docker/Dockerfile" tags = ["gds-mock-mcp:latest"] platforms = ["linux/amd64", "linux/arm64"] cache-from = ["type=registry,ref=gds-mock-mcp:buildcache"] cache-to = ["type=registry,ref=gds-mock-mcp:buildcache,mode=max"] } ``` **Dockerfile Strategy**: - Multi-stage build: builder → production - Builder stage: install dependencies, run tests - Production stage: copy only production deps and source - Use Node.js 20 Alpine for minimal image size - Non-root user for security - Health check via MCP ping or valkey connection test **Alternatives Considered**: - **Traditional Dockerfile only**: Rejected - buildx bake provides better DX and multi-platform support - **Docker Compose build**: Rejected - less flexible than buildx, no multi-platform - **Podman**: Rejected - Docker specified in requirements ### Decision 5: Mock Data Architecture **Decision**: Embed realistic GDS data in JavaScript modules with deterministic generation **Data Structure**: - **Airports**: ~100 major airports with IATA codes (JFK, LAX, ORD, etc.) - **Airlines**: Major carriers with IATA codes (AA, DL, UA, BA, etc.) - **Hotels**: 50+ chains/properties across major cities - **Car Rentals**: Major companies (Hertz, Avis, Enterprise) with vehicle types - **Flight Routes**: Pre-defined routes with realistic times and prices - **Pricing Tiers**: Economy ($200-$600 domestic), Business ($800-$2000), First Class ($2500+) **Generation Strategy**: - Deterministic: Same search inputs produce same results (for testing reproducibility) - Controlled Randomness: Optional seed parameter for demo variety - Rule-Based Pricing: Distance-based pricing with time-of-day adjustments - Availability Simulation: Random sold-out scenarios (10% of flights) **Rationale**: - Embedded data = no external dependencies (fast startup) - Deterministic = reliable integration tests - Realistic codes = constitution compliance (Principle II) - Pre-computed routes = sub-2s response times **Alternatives Considered**: - **External API (Skyscanner, etc.)**: Rejected - violates "no external connections" (Constitution Principle III) - **Database seeding**: Rejected - overhead, embedded data sufficient for mock scope - **Fully random data**: Rejected - testing requires deterministic outputs ### Decision 6: Testing Strategy **Decision**: Use Node.js native test runner with three-tier test structure **Test Tiers**: 1. **Unit Tests**: Individual tool handlers, data generators, validators - Fast (<100ms total), isolated, no external dependencies - Mock Valkey client for session tests 2. **Integration Tests**: Full MCP workflows with real Valkey (test container) - End-to-end booking flows (search → book → retrieve → cancel) - Multi-service workflows (flight + hotel + car) - Concurrent session isolation tests - Use docker-compose test profile for Valkey 3. **Contract Tests**: MCP protocol compliance validation - Verify JSON-RPC 2.0 format - Tool schema validation - Error response structure **Test Execution**: ```bash npm test # All tests npm run test:unit # Fast unit tests only npm run test:integration # Requires Valkey npm run test:coverage # Coverage report with c8 ``` **Rationale**: - Native test runner = minimal dependencies - Three tiers = appropriate test coverage - Docker test containers = realistic integration tests - Fast unit tests = quick feedback loop ### Decision 7: Configuration Management **Decision**: Environment variables with secure defaults **Configuration Variables**: ```bash # MCP Server MCP_TRANSPORT=stdio # stdio or sse MCP_SESSION_TIMEOUT=3600 # 1 hour session TTL # Valkey VALKEY_HOST=localhost VALKEY_PORT=6379 VALKEY_PASSWORD= # Empty for dev, required for prod VALKEY_DB=0 VALKEY_KEY_PREFIX=gds: # Logging LOG_LEVEL=info # silent, error, warn, info, debug, trace LOG_PRETTY=false # Pretty print for dev # Mock Data MOCK_DATA_SEED=fixed # fixed or random MOCK_RESPONSE_DELAY=0 # Artificial delay (ms) for demo purposes ``` **Security Defaults**: - No production credentials in code or .env.example - Configuration validation on startup - Reject production-like patterns (Constitution Principle III) **Rationale**: - Standard practice for containerized apps - Easy to override in docker-compose - Secure defaults prevent accidents ### Decision 8: PNR Generation Strategy **Decision**: Deterministic PNR generation with TEST prefix **Format**: `TEST-{BASE32}` where BASE32 is 6 characters **Example**: `TEST-A1B2C3` **Generation Algorithm**: 1. Generate session-scoped sequence number 2. Combine with booking timestamp 3. Hash with SHA-256 4. Take first 6 characters of base32 encoding 5. Prefix with "TEST-" **Rationale**: - "TEST-" prefix = clear mock indicator (Constitution Principle III) - Base32 = human-readable, unambiguous (no 0/O, 1/I confusion) - 6 characters = 1 billion unique combinations (sufficient for testing) - Deterministic = reproducible test scenarios - Session-scoped = prevents conflicts **Alternatives Considered**: - **Random UUID**: Rejected - too long, not human-friendly - **Sequential numbers**: Rejected - predictable, not realistic - **No prefix**: Rejected - violates safety requirement ## Technology Stack Summary | Component | Technology | Version | Rationale | |-----------|-----------|---------|-----------| | Runtime | Node.js | 20 LTS | Current stable, long-term support | | MCP SDK | @modelcontextprotocol/sdk | Latest | Official SDK, protocol compliance | | Persistence | Valkey | 8.0+ | Redis-compatible, requirement specified | | Valkey Client | ioredis | 5.x | Mature, feature-rich, TypeScript support | | Logging | Pino | Latest | Fast, structured, minimal overhead | | Testing | node:test | Built-in | Native, zero dependencies | | Coverage | c8 | Latest | V8 coverage, lightweight | | Container | Docker | 24+ | Buildx support, multi-platform | | Orchestration | docker-compose | 2.x | Development environment | ## Performance Considerations ### Expected Performance Profile - **Search Operations**: <500ms (data generation + Valkey lookup) - **Booking Operations**: <200ms (validation + Valkey write) - **Retrieval Operations**: <100ms (Valkey read) - **Concurrent Sessions**: 50+ (limited by Valkey and Node.js event loop) - **Memory Footprint**: <100MB per server instance - **Container Image Size**: <50MB (Alpine-based) ### Optimization Strategies 1. **Caching**: Pre-compute common search results in Valkey 2. **Connection Pooling**: ioredis maintains persistent Valkey connections 3. **Lazy Loading**: Load mock data modules on-demand 4. **Batch Operations**: Use Valkey pipelines for multi-key operations ## Security Considerations ### Mock Data Safety - ✅ No real API keys or credentials stored - ✅ Configuration validation rejects production patterns - ✅ All PNRs prefixed with "TEST-" - ✅ No external network calls (except to local Valkey) - ✅ Non-root container user ### Docker Security - Use official Node.js Alpine base images - Run as non-root user (node:node) - Minimal attack surface (no shell, no dev tools in prod image) - Regular security updates via base image updates ## Open Questions (Resolved) All technical unknowns from initial planning have been resolved through research above. No blocking issues identified. ## Next Steps Proceed to Phase 1: Design & Contracts - Create data-model.md (data structures) - Define MCP tool contracts in contracts/ - Generate quickstart.md with usage examples - Update agent context with technology decisions