search_flights
Search for available flights using session authentication, specifying destinations, dates, fare types, and passenger details to find booking options.
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
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| 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. | |
| requestBody | Yes | Flight search parameters for finding available flights. Can be identical to initialization parameters or refined based on customer preferences. |
Implementation Reference
- src/mcp/tools/tools.ts:44-218 (handler)Full implementation of the search_flights tool as an MCP tool definition. Specifies the API endpoint, input schema, execution parameters (session-token header), security requirements, and custom serializer function that executes the core logic to parse and format the flight search response into user-readable text with flight options, prices, and next steps.[ '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 search_flights tool input, requiring session-token header and requestBody with itinerary segments, travelers, fare families, etc. Includes detailed descriptions for each field.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/index.ts:9-51 (registration)Registers the tools map (containing search_flights definition) with the MCP server, handling list_tools requests (exposes schema/description) and call_tool requests (executes via executor using the tool definition).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/types.d.ts:72-82 (helper)TypeScript interface FlightSearchResponse defining the structure of the flight search API response, used in the handler's serializer type annotation.export interface FlightSearchResponse { data: { airBoundGroups: AirBoundGroup[]; }; dictionaries: { fareFamilyWithServices: Record<string, FareFamily>; airline: Record<string, string>; flight: Record<string, FlightDetails>; location: Record<string, LocationDetails>; currency: Record<string, CurrencyDetails>; aircraft: Record<string, string>;