Files
gds-mock-mcp/specs/001-mock-gds-server/quickstart.md
Peter.Morton 35a98c6d4b 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>
2026-04-07 11:27:53 -05:00

9.4 KiB

Quick Start Guide: Mock GDS MCP Server

Branch: 001-mock-gds-server | Date: 2026-04-07

Overview

This guide demonstrates how to use the Mock GDS MCP Server for testing travel applications. All examples use mock data with the TEST- prefix for safety.


Installation & Setup

Prerequisites

  • Node.js 20 LTS or later
  • Docker & Docker Compose (for Valkey)
  • MCP-compatible client

Running with Docker Compose

# Start the mock GDS server and Valkey
docker-compose up -d

# View logs
docker-compose logs -f gds-mock-mcp

# Stop services
docker-compose down

Configuration

Create a .env file (optional, defaults shown):

# MCP Server
MCP_TRANSPORT=stdio
MCP_SESSION_TIMEOUT=3600

# Valkey
VALKEY_HOST=valkey
VALKEY_PORT=6379
VALKEY_PASSWORD=
VALKEY_DB=0

# Logging
LOG_LEVEL=info
LOG_PRETTY=false

# Mock Data
MOCK_DATA_SEED=fixed
MOCK_RESPONSE_DELAY=0

Basic Workflows

1. Search and Book a Flight

Step 1: Search for flights

// MCP tool call
{
  "tool": "searchFlights",
  "arguments": {
    "origin": "JFK",
    "destination": "LAX",
    "departureDate": "2026-06-15",
    "passengers": { "adults": 1 },
    "cabin": "economy"
  }
}

// Response
{
  "searchId": "search_abc123",
  "flights": [
    {
      "id": "flight_1",
      "flightNumber": "AA123",
      "airline": { "code": "AA", "name": "American Airlines" },
      "departure": { "time": "2026-06-15T08:00:00Z", "airport": "JFK" },
      "arrival": { "time": "2026-06-15T11:30:00Z", "airport": "LAX" },
      "price": 29900,  // $299.00
      "seatsAvailable": 45,
      "status": "available"
    },
    // ... more flights
  ],
  "resultCount": 5
}

Step 2: Book the selected flight

// MCP tool call
{
  "tool": "bookFlight",
  "arguments": {
    "flightIds": ["flight_1"],
    "passengers": [
      {
        "type": "adult",
        "firstName": "John",
        "lastName": "Doe",
        "email": "john.doe@example.com"
      }
    ],
    "contactEmail": "john.doe@example.com"
  }
}

// Response
{
  "pnr": "TEST-A1B2C3",  // Note: TEST- prefix for safety
  "status": "confirmed",
  "bookingDate": "2026-04-07T16:30:00Z",
  "passengers": [{ "firstName": "John", "lastName": "Doe" }],
  "flights": [{ /* flight details */ }],
  "totalPrice": 29900,
  "currency": "USD"
}

2. Multi-Service Booking (Flight + Hotel)

Step 1: Book a flight (as above)

Result: pnr = "TEST-A1B2C3"

Step 2: Search for hotels

{
  "tool": "searchHotels",
  "arguments": {
    "cityCode": "LAX",
    "checkInDate": "2026-06-15",
    "checkOutDate": "2026-06-17",
    "guests": 1
  }
}

// Response
{
  "hotels": [
    {
      "id": "hotel_1",
      "name": "Marriott Los Angeles Downtown",
      "starRating": 4,
      "pricePerNight": 18000,  // $180.00
      "totalPrice": 36000,     // 2 nights
      "amenities": ["WiFi", "Pool", "Gym", "Parking"]
    }
  ]
}

Step 3: Add hotel to existing booking

{
  "tool": "bookHotel",
  "arguments": {
    "hotelId": "hotel_1",
    "existingPnr": "TEST-A1B2C3",  // Add to flight booking
    "guests": [
      {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john.doe@example.com"
      }
    ]
  }
}

// Response
{
  "pnr": "TEST-A1B2C3",  // Same PNR, now includes hotel
  "flights": [{ /* flight */ }],
  "hotels": [{ /* hotel */ }],
  "totalPrice": 65900  // $299 flight + $360 hotel
}

3. Complete Travel Package (Flight + Hotel + Car)

Step 1-2: Book flight and hotel (as above)

Result: pnr = "TEST-A1B2C3"

Step 3: Search for rental cars

{
  "tool": "searchCars",
  "arguments": {
    "pickupLocationCode": "LAX",
    "pickupDate": "2026-06-15T12:00:00Z",
    "dropoffDate": "2026-06-17T10:00:00Z"
  }
}

// Response
{
  "cars": [
    {
      "id": "car_1",
      "companyName": "Hertz",
      "vehicleClass": "economy",
      "vehicleModel": "Toyota Corolla or similar",
      "dailyRate": 4500,   // $45/day
      "totalPrice": 9000,  // 2 days
      "mileagePolicy": "unlimited"
    }
  ]
}

Step 4: Add car to booking

{
  "tool": "bookCar",
  "arguments": {
    "carId": "car_1",
    "existingPnr": "TEST-A1B2C3",
    "driver": {
      "firstName": "John",
      "lastName": "Doe",
      "age": 35
    }
  }
}

// Response
{
  "pnr": "TEST-A1B2C3",
  "flights": [{ /* flight */ }],
  "hotels": [{ /* hotel */ }],
  "cars": [{ /* car */ }],
  "totalPrice": 74900  // $299 + $360 + $90
}

4. Retrieve and Manage Bookings

Retrieve a booking by PNR

{
  "tool": "retrieveBooking",
  "arguments": {
    "pnr": "TEST-A1B2C3"
  }
}

// Response: Full booking details

List all bookings in session

{
  "tool": "listBookings",
  "arguments": {
    "status": "all"
  }
}

// Response
{
  "bookings": [
    {
      "pnr": "TEST-A1B2C3",
      "status": "confirmed",
      "createdAt": "2026-04-07T16:30:00Z",
      "passengerCount": 1,
      "segmentCounts": { "flights": 1, "hotels": 1, "cars": 1 },
      "totalPrice": 74900
    }
  ],
  "totalCount": 1
}

Cancel a booking

{
  "tool": "cancelBooking",
  "arguments": {
    "pnr": "TEST-A1B2C3",
    "reason": "Travel plans changed"
  }
}

// Response
{
  "pnr": "TEST-A1B2C3",
  "status": "cancelled",
  "cancelledAt": "2026-04-07T17:00:00Z"
}

Testing Scenarios

Concurrent Sessions

// Session 1
const session1 = createMCPConnection();
const booking1 = await session1.call("bookFlight", { /* ... */ });
// booking1.pnr = "TEST-A1B2C3"

// Session 2 (different MCP connection)
const session2 = createMCPConnection();
const booking2 = await session2.call("bookFlight", { /* ... */ });
// booking2.pnr = "TEST-X7Y8Z9"  // Different PNR

// Sessions are isolated - cannot retrieve booking1 from session2
await session2.call("retrieveBooking", { pnr: "TEST-A1B2C3" });
// Error: PNR not found in current session

Error Handling

Invalid airport code

{
  "tool": "searchFlights",
  "arguments": {
    "origin": "ZZZ",  // Invalid code
    "destination": "LAX",
    "departureDate": "2026-06-15"
  }
}

// Error response
{
  "code": -32602,
  "message": "Invalid airport code 'ZZZ': must be a valid 3-letter IATA code",
  "data": { "field": "origin", "value": "ZZZ" }
}

Date in the past

{
  "tool": "searchFlights",
  "arguments": {
    "origin": "JFK",
    "destination": "LAX",
    "departureDate": "2020-01-01"  // Past date
  }
}

// Error response
{
  "code": -32002,
  "message": "Departure date cannot be in the past",
  "data": { "field": "departureDate", "value": "2020-01-01" }
}

Cancelled booking modification

// First cancel
await call("cancelBooking", { pnr: "TEST-A1B2C3" });

// Try to add hotel to cancelled booking
await call("bookHotel", {
  hotelId: "hotel_1",
  existingPnr: "TEST-A1B2C3"
});

// Error response
{
  "code": -32002,
  "message": "Cannot modify cancelled booking TEST-A1B2C3",
  "data": { "pnr": "TEST-A1B2C3", "status": "cancelled" }
}

Integration Testing Example

// Example integration test (using Node.js test runner)
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { MCPClient } from './mcp-client.js';

test('complete booking workflow', async (t) => {
  const client = new MCPClient();
  
  // Search flights
  const searchResults = await client.call('searchFlights', {
    origin: 'JFK',
    destination: 'LAX',
    departureDate: '2026-06-15',
    passengers: { adults: 1 }
  });
  
  assert.ok(searchResults.flights.length > 0);
  const flight = searchResults.flights[0];
  
  // Book flight
  const booking = await client.call('bookFlight', {
    flightIds: [flight.id],
    passengers: [{
      type: 'adult',
      firstName: 'Test',
      lastName: 'User',
      email: 'test@example.com'
    }],
    contactEmail: 'test@example.com'
  });
  
  assert.match(booking.pnr, /^TEST-[A-Z0-9]{6}$/);
  assert.equal(booking.status, 'confirmed');
  
  // Retrieve booking
  const retrieved = await client.call('retrieveBooking', {
    pnr: booking.pnr
  });
  
  assert.equal(retrieved.pnr, booking.pnr);
  assert.equal(retrieved.flights.length, 1);
  
  // Cancel booking
  const cancelled = await client.call('cancelBooking', {
    pnr: booking.pnr
  });
  
  assert.equal(cancelled.status, 'cancelled');
});

Mock Data Reference

Supported Airports

Major US: JFK, LAX, ORD, DFW, ATL, MIA, SFO, SEA, BOS, LAS
International: LHR, CDG, FRA, NRT, HKG, SYD, DXB

Supported Airlines

  • American Airlines (AA)
  • Delta Air Lines (DL)
  • United Airlines (UA)
  • Southwest Airlines (WN)
  • British Airways (BA)
  • Virgin Atlantic (VS)
  • Lufthansa (LH)
  • Air France (AF)

Price Ranges

  • Flights: $200-$800 (economy domestic), $800-$2000 (business), $2500+ (first)
  • Hotels: $80-$150 (budget), $150-$300 (mid-range), $300-$800 (luxury)
  • Cars: $35-$50 (economy), $50-$80 (midsize), $100-$150 (luxury)

Troubleshooting

Server won't start

Check Valkey connection:

docker-compose logs valkey

Session expires

Default timeout: 1 hour. Adjust with MCP_SESSION_TIMEOUT env var.

PNR not found

Verify you're retrieving from the same session that created the booking.


Next Steps

  • Explore all available tools in /contracts/mcp-tools.md
  • Review data model in /data-model.md
  • Check technical research in /research.md
  • See implementation tasks in /tasks.md (after running /speckit.tasks)