search_flights
Find available flights using origin/destination codes, dates, and passenger details. Requires booking session initialization first to obtain authentication token for flight search functionality.
Instructions
Perform a flight search based on search criteria. This operation required 'initialize_booking_session' to be exexcuted first to start the session.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| requestBody | Yes | Flight search parameters for finding available flights. Can be identical to initialization parameters or refined based on customer preferences. | |
| session-token | Yes | Required authentication token obtained from the initialization step. This token is generated during booking initialization and returned in the response headers (look for "Session-Token" header). The token maintains booking context and enables flight search functionality. WITHOUT this token, flight searches will fail. |
Input Schema (JSON Schema)
{
"properties": {
"requestBody": {
"additionalProperties": false,
"description": "Flight search parameters for finding available flights. Can be identical to initialization parameters or refined based on customer preferences.",
"properties": {
"commercialFareFamilies": {
"description": "Array of fare family codes to filter search results. Common codes: CFFECO (Economy), CFFBUS (Business), CFFFIR (First). Use airline-specific codes for targeted searches.",
"items": {
"type": "string"
},
"minItems": 1,
"type": "array"
},
"flowCode": {
"default": "Revenue",
"description": "Booking flow context that determines available options and pricing logic",
"enum": [
"Revenue",
"Award",
"Upgrade"
],
"type": "string"
},
"itineraries": {
"description": "Flight segments defining the journey. Single segment = one-way, two segments = round-trip. Each segment represents one flight leg of the complete journey.",
"items": {
"additionalProperties": false,
"properties": {
"departureDate": {
"description": "Departure date in ISO format YYYY-MM-DD (e.g., 2025-09-30). Must be a future date.",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"type": "string"
},
"destinationLocationCode": {
"description": "IATA airport code for arrival city (3 uppercase letters, e.g., SIN for Singapore)",
"maxLength": 3,
"minLength": 3,
"pattern": "^[A-Z]{3}$",
"type": "string"
},
"isRequestedBound": {
"description": "Whether this segment is the primary requested journey. Set true for outbound/main flight, false for return flights in round-trip searches.",
"type": "boolean"
},
"originLocationCode": {
"description": "IATA airport code for departure city (3 uppercase letters, e.g., KUL for Kuala Lumpur)",
"maxLength": 3,
"minLength": 3,
"pattern": "^[A-Z]{3}$",
"type": "string"
}
},
"required": [
"originLocationCode",
"destinationLocationCode",
"departureDate",
"isRequestedBound"
],
"type": "object"
},
"maxItems": 10,
"minItems": 1,
"type": "array"
},
"selectedBoundId": {
"description": "Optional identifier for pre-selected outbound flight in round-trip bookings. Used when customer has already chosen their outbound flight and is now selecting return options.",
"type": "string"
},
"travelers": {
"description": "List of all passengers for the booking. Each traveler object represents one person. Total count affects pricing and availability. Maximum 9 passengers per booking.",
"items": {
"additionalProperties": false,
"properties": {
"passengerTypeCode": {
"description": "Type of passenger for pricing and service determination",
"enum": [
"ADT",
"CHD",
"INF"
],
"type": "string"
}
},
"required": [
"passengerTypeCode"
],
"type": "object"
},
"maxItems": 9,
"minItems": 1,
"type": "array"
}
},
"required": [
"commercialFareFamilies",
"itineraries",
"travelers"
],
"type": "object"
},
"session-token": {
"description": "Required authentication token obtained from the initialization step. This token is generated during booking initialization and returned in the response headers (look for \"Session-Token\" header). The token maintains booking context and enables flight search functionality. WITHOUT this token, flight searches will fail.",
"minLength": 1,
"type": "string"
}
},
"required": [
"session-token",
"requestBody"
],
"type": "object"
}
Implementation Reference
- src/mcp/tools/tools.ts:45-218 (registration)Defines and registers the 'search_flights' tool including its metadata, input schema reference, API endpoint, parameters, security, and custom serializer that implements the core response processing logic for displaying flight search results.'search_flights', { name: 'search_flights', description: `Perform a flight search based on search criteria. This operation required 'initialize_booking_session' to be exexcuted first to start the session.`, inputSchema: FlightSearchSchema, method: 'post', pathTemplate: '/flight-search/flights', executionParameters: [ { name: 'session-token', in: 'header', }, ], requestBodyContentType: 'application/json', securityRequirements: [ { HeaderApiToken: [], HeaderApimSubscriptionKey: [], HeaderApiVersion: [], }, ], serializer: (response: AxiosResponse<FlightSearchResponse>): string => { const { data: responseData } = response; const { data, dictionaries } = responseData; const { airBoundGroups } = data; let output = 'FLIGHT SEARCH RESULTS\n'; output += '='.repeat(80) + '\n\n'; if (!airBoundGroups || airBoundGroups.length === 0) { return output + 'No flights found matching your criteria.\n'; } airBoundGroups.forEach((group, groupIdx) => { const { boundDetails, airBounds } = group; const { origin, destination, duration, segments, isFastestBound } = boundDetails; const originLoc = dictionaries.location[origin.airportCode]; const destLoc = dictionaries.location[destination.airportCode]; if (!originLoc || !destLoc) { output += `Warning: Location information not available for group ${groupIdx + 1}\n\n`; return; } if (!segments || segments.length === 0) { output += `Warning: No segment information for group ${groupIdx + 1}\n\n`; return; } const firstSegment = segments[0]!; const flightDetails = dictionaries.flight[firstSegment.flightId]; if (!flightDetails) { output += `Warning: Flight details not available for group ${groupIdx + 1}\n\n`; return; } const allPrices = airBounds.map((ab) => ab.prices.totalPrice.total); const minPrice = Math.min(...allPrices); const maxPrice = Math.max(...allPrices); const currency = airBounds[0]?.prices.totalPrice.original.currencyCode || 'MYR'; // Extract flight number for the header const mainFlightNumber = flightDetails.flightDesignator.marketing.flightNumber; const mainAirlineCode = flightDetails.flightDesignator.marketing.airlineCode; output += `OPTION ${groupIdx + 1}${isFastestBound ? ' (FASTEST)' : ''}\n`; output += `Flight: ${mainAirlineCode}${mainFlightNumber}\n`; output += `Route: ${originLoc.cityName} (${origin.airportCode}) -> ${destLoc.cityName} (${destination.airportCode})\n`; output += `Departure: ${formatDate(flightDetails.departure.dateTime)} at ${formatTime( flightDetails.departure.dateTime, )}\n`; output += `Duration: ${formatDuration(duration)}\n`; output += `Price Range: ${formatPrice(minPrice, currency)} - ${formatPrice(maxPrice, currency)}\n`; output += '\nFlight Details:\n'; segments.forEach((seg, segIdx) => { const flight = dictionaries.flight[seg.flightId]; if (flight) { const airline = dictionaries.airline[flight.flightDesignator.marketing.airlineCode]; const flightNum = flight.flightDesignator.marketing.flightNumber; const operatingAirline = dictionaries.airline[flight.flightDesignator.operating.airlineCode]; output += ` ${segIdx + 1}. ${airline} ${flightNum}`; // Show if operated by different airline (code share) if ( flight.flightDesignator.marketing.airlineCode !== flight.flightDesignator.operating.airlineCode ) { output += ` (operated by ${operatingAirline})`; } output += `\n Aircraft: ${flight.aircraftName}\n`; output += ` ${flight.departure.locationCode}`; if (flight.departure.terminal) { output += ` T${flight.departure.terminal}`; } output += ` ${formatTime(flight.departure.dateTime)}`; output += ` -> ${flight.arrival.locationCode}`; if (flight.arrival.terminal) { output += ` T${flight.arrival.terminal}`; } output += ` ${formatTime(flight.arrival.dateTime)}`; if (seg.arrivalDaysDifference) { output += ` +${seg.arrivalDaysDifference}d`; } output += `\n`; } }); const econFares = airBounds.filter((ab) => { const ff = dictionaries.fareFamilyWithServices[ab.fareFamilyCode]; return ff?.cabin === 'eco'; }); const busFares = airBounds.filter((ab) => { const ff = dictionaries.fareFamilyWithServices[ab.fareFamilyCode]; return ff?.cabin === 'business'; }); if (econFares.length > 0) { output += '\nEconomy Class Options:\n'; econFares.forEach((fare) => { const isRecommended = fare.extraProperties?.isRecommended; const isCheapest = fare.isCheapestOffer; const price = formatPrice(fare.prices.totalPrice.total, currency); const fareFamily = fare.fareFamilyCode; const availability = fare.availabilityDetails?.[0]; if (!availability) { return; } output += ` ${isRecommended ? '[RECOMMENDED] ' : isCheapest ? '[CHEAPEST] ' : ''}${fareFamily}: ${price}`; output += ` | Class: ${availability.bookingClass}`; if (availability.seatLeft) { output += ` | ${availability.seatLeft} seats left`; } output += `\n AirBoundID: ${fare.airBoundId}\n`; }); } if (busFares.length > 0) { output += '\nBusiness Class Options:\n'; busFares.forEach((fare) => { const isRecommended = fare.extraProperties?.isRecommended; const price = formatPrice(fare.prices.totalPrice.total, currency); const fareFamily = fare.fareFamilyCode; const availability = fare.availabilityDetails?.[0]; if (!availability) { return; } output += ` ${isRecommended ? '[RECOMMENDED] ' : ''}${fareFamily}: ${price}`; output += ` | Class: ${availability.bookingClass}`; if (availability.seatLeft) { output += ` | ${availability.seatLeft} seats left`; } output += `\n AirBoundID: ${fare.airBoundId}\n`; }); } output += '\n' + '-'.repeat(80) + '\n\n'; }); output += 'To select a flight, use the create_cart tool with the desired AirBoundID(s)\n'; return output; }, }, ],
- src/mcp/tools/input-schema.ts:59-66 (schema)Zod schema for validating inputs to search_flights tool: requires session-token header and requestBody with itineraries, travelers, fare families, etc.export const FlightSearchSchema = z.object({ 'session-token': z.string() .min(1) .describe('Required authentication token obtained from the initialization step. This token is generated during booking initialization and returned in the response headers (look for "Session-Token" header). The token maintains booking context and enables flight search functionality. WITHOUT this token, flight searches will fail.'), requestBody: FlightSearchBodySchema .describe('Flight search parameters for finding available flights. Can be identical to initialization parameters or refined based on customer preferences.'), }).describe('STEP 2: Perform actual flight search using session token from initialization. This endpoint searches for available flights matching the criteria and returns flight options with pricing and schedules.');
- src/mcp/tools/input-schema.ts:32-52 (schema)Core FlightSearchBodySchema defining the request body structure for flight searches, including itineraries (origin/dest/date), passengers, fare families, and flow type. Referenced by FlightSearchSchema.const FlightSearchBodySchema = z .object({ commercialFareFamilies: z.array(z.string()) .min(1) .describe('Array of fare family codes to filter search results. Common codes: CFFECO (Economy), CFFBUS (Business), CFFFIR (First). Use airline-specific codes for targeted searches.'), itineraries: z.array(ItinerarySegment) .min(1) .max(10) .describe('Flight segments defining the journey. Single segment = one-way, two segments = round-trip. Each segment represents one flight leg of the complete journey.'), selectedBoundId: z.string() .optional() .describe('Optional identifier for pre-selected outbound flight in round-trip bookings. Used when customer has already chosen their outbound flight and is now selecting return options.'), flowCode: FlowCode .default('Revenue') .describe('Booking flow context that determines available options and pricing logic'), travelers: z.array(Traveler) .min(1) .max(9) .describe('List of all passengers for the booking. Each traveler object represents one person. Total count affects pricing and availability. Maximum 9 passengers per booking.'), }) .describe('Flight search request schema for airline booking systems. Supports one-way and round-trip searches with multiple passenger types and fare family filtering.');
- src/mcp/tools/index.ts:9-51 (registration)Registers all tools from the tools map (including search_flights) with the MCP server for listTools and callTool requests, delegating execution to executeApiTool.export function registerTools(server: Server): void { server.setRequestHandler(ListToolsRequestSchema, async () => { const toolsForClient: Tool[] = []; for (const def of Array.from(tools.values())) { toolsForClient.push({ name: def.name, description: def.description, inputSchema: zodToMcpJsonSchema(def.inputSchema), }); } return { tools: toolsForClient, }; }); server.setRequestHandler( CallToolRequestSchema, async (request: CallToolRequest): Promise<CallToolResult> => { const { name: toolName, arguments: toolArgs } = request.params; console.info(`Attempt to use custom tool: ${toolName}`); const toolDefinition = tools.get(toolName); if (!toolDefinition) { return { content: [ { type: 'text', text: `Error: Unknown tool requested: ${toolName}`, }, ], }; } return await executeApiTool( toolName, toolDefinition, toolArgs ?? {}, securitySchemes, ); }, ); }
- src/mcp/tools/executor.ts:320-377 (helper)Generic tool executor used by all tools including search_flights: validates args with schema, builds HTTP request to the API endpoint specified in tool def, applies security headers/tokens, executes axios request, and formats response using the tool-specific serializer.export async function executeApiTool( toolName: string, definition: McpToolDefinition, toolArgs: Record<string, unknown>, securityRequirementDictionaries: Record<string, SecurityScheme>, ): Promise<CallToolResult> { // 1. Validate arguments const validation = validateToolArguments(toolName, definition, toolArgs); if (!validation.success) { return validation.result; } try { // 2. Build request configuration const baseUrl = DSP_BOOKING_BASE_URL; const requestConfig = buildRequestConfig(definition, validation.data); // 3. Validate and apply security const securityContext = validateSecurity(definition, securityRequirementDictionaries); if (!securityContext.isValid) { console.warn(`Tool '${toolName}' ${securityContext.errorMessage}`); return { content: [ { type: 'text', text: `Internal error during tool setup: '${toolName}'`, }, ], }; } if (securityContext.appliedSecurity) { await applySecurity( requestConfig.headers, securityContext.appliedSecurity, securityRequirementDictionaries, ); } // 4. Create axios configuration const axiosConfig: AxiosRequestConfig = { method: definition.method.toUpperCase(), url: baseUrl + requestConfig.urlPath, params: requestConfig.queryParams, headers: requestConfig.headers, ...(requestConfig.requestBodyData !== undefined && { data: requestConfig.requestBodyData }), }; // 5. Execute request console.info(`Executing tool "${toolName}": ${axiosConfig.method} ${axiosConfig.url}`); const response = await axios(axiosConfig); // 6. Format and return response - pass definition instead of toolName return formatResponse(response, definition); } catch (error) { return formatError(toolName, error); } }