feat: complete implementation plan for Mock GDS MCP server
Phase 0 (Research):
- Selected MCP SDK (@modelcontextprotocol/sdk) for protocol compliance
- Chose ioredis for Valkey client (Redis-compatible)
- Minimal dependencies: Pino logging, native test runner
- Docker buildx bake for multi-platform builds
- Embedded mock data with deterministic generation
- PNR format: TEST-{BASE32} for safety
Phase 1 (Design):
- Data model: 8 core entities (Session, PNR, FlightSegment, etc.)
- MCP contracts: 8 tools with JSON schemas
- Quickstart guide with complete workflow examples
- Constitution compliance verified
Technical stack:
- Node.js 20 LTS
- Valkey 8.0+ for persistence
- Docker containers (amd64/arm64)
- Performance: <2s search, 50+ concurrent sessions
Ready for task generation with /speckit.tasks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
299
specs/001-mock-gds-server/research.md
Normal file
299
specs/001-mock-gds-server/research.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user