airports-search
Search for flight information between airports using Google Flights. Find available flights, prices, and schedules for one-way, round-trip, or multi-city trips.
Instructions
Search for airport and flight information using Google Flights
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| departure_id | Yes | Departure airport code (e.g., 'CDG', 'AUS', 'LAX', 'SFO'). Use 3-letter IATA codes. | |
| arrival_id | Yes | Arrival airport code (e.g., 'NRT', 'LAX', 'JFK', 'LHR'). Use 3-letter IATA codes. | |
| outbound_date | No | Outbound date in YYYY-MM-DD format | |
| return_date | No | Return date in YYYY-MM-DD format for round-trip (REQUIRED when type=1) | |
| multi_city_json | No | JSON string for multi-city trips with multiple legs (required when type=3) | |
| type | No | Trip type: 1=Round-trip (requires return_date), 2=One-way (default), 3=Multi-city (requires multi_city_json). Use type=2 for one-way trips to avoid errors. | |
| max_best_flights | No | Maximum number of best flights to return | |
| max_other_flights | No | Maximum number of other flights to return | |
| include_price_insights | No | Include price insights and history | |
| include_airports | No | Include airport information | |
| summary_only | No | Return only essential flight information | |
| include_links | No | Include booking links for each flight (off by default) |
Implementation Reference
- src/airports.ts:173-221 (handler)Main handler function that queries SerpAPI for Google Flights data using provided airport codes and dates, then formats the response into Markdown using helper function.
export async function searchAirports( params: z.infer<typeof airportsSearchSchema> ): Promise<string> { const apiKey = process.env.SERP_API_KEY; if (!apiKey) { throw new Error("SERP_API_KEY environment variable is required"); } const { max_best_flights, max_other_flights, include_price_insights, include_airports, summary_only, include_links, ...apiParams } = params; const searchParams = new URLSearchParams({ engine: "google_flights", api_key: apiKey, departure_id: apiParams.departure_id, arrival_id: apiParams.arrival_id, hl: "en", currency: "USD", ...(apiParams.outbound_date && { outbound_date: apiParams.outbound_date }), ...(apiParams.return_date && { return_date: apiParams.return_date }), ...(apiParams.multi_city_json && { multi_city_json: apiParams.multi_city_json, }), ...(apiParams.type !== undefined && { type: apiParams.type.toString() }), }); try { const response = await axios.get( `${SERPAPI_BASE_URL}?${searchParams.toString()}` ); return formatFlightToMarkdown(response.data, params); } catch (error) { if (axios.isAxiosError(error)) { throw new Error( `Airports API request failed: ${ error.response?.data?.error || error.message }` ); } throw error; } } - src/airports.ts:6-58 (schema)Zod schema defining input parameters for the airports-search tool, including airport codes, dates, trip types, and formatting options.
const airportsSearchSchema = z.object({ departure_id: z .string() .describe("Departure airport code (e.g., 'CDG', 'AUS', 'LAX', 'SFO'). Use 3-letter IATA codes."), arrival_id: z.string().describe("Arrival airport code (e.g., 'NRT', 'LAX', 'JFK', 'LHR'). Use 3-letter IATA codes."), outbound_date: z .string() .optional() .describe("Outbound date in YYYY-MM-DD format"), return_date: z .string() .optional() .describe("Return date in YYYY-MM-DD format for round-trip (REQUIRED when type=1)"), multi_city_json: z .string() .optional() .describe("JSON string for multi-city trips with multiple legs (required when type=3)"), type: z .number() .optional() .default(2) .describe("Trip type: 1=Round-trip (requires return_date), 2=One-way (default), 3=Multi-city (requires multi_city_json). Use type=2 for one-way trips to avoid errors."), max_best_flights: z .number() .optional() .default(3) .describe("Maximum number of best flights to return"), max_other_flights: z .number() .optional() .default(5) .describe("Maximum number of other flights to return"), include_price_insights: z .boolean() .optional() .default(true) .describe("Include price insights and history"), include_airports: z .boolean() .optional() .default(false) .describe("Include airport information"), summary_only: z .boolean() .optional() .default(true) .describe("Return only essential flight information"), include_links: z .boolean() .optional() .default(false) .describe("Include booking links for each flight (off by default)"), }); - src/index.ts:151-179 (registration)Registers the airports-search tool on the MCP server with description, schema reference, and async handler that calls searchAirports and wraps response in MCP format with error handling.
server.tool( "airports-search", "Search for airport and flight information using Google Flights", airportsSearchSchema.shape, async (params) => { try { const result = await searchAirports(params); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error searching airports data: ${ error instanceof Error ? error.message : String(error) }`, }, ], }; } } ); - src/airports.ts:69-170 (helper)Key helper function that formats raw SerpAPI Google Flights JSON response into structured Markdown output, handling best/other flights, summaries, price insights, layovers, and links.
function formatFlightToMarkdown(data: any, params: z.infer<typeof airportsSearchSchema>): string { if (!data) return "No flight data available."; let markdown = `# ${params.departure_id} → ${params.arrival_id}\n\n`; // Get the Google Flights URL for booking const googleFlightsUrl = data.search_metadata?.google_flights_url; // Add route summary if (params.summary_only) { const bestFlight = data.best_flights?.[0]; const bestPrice = bestFlight?.price || data.price_insights?.lowest_price; const flightCount = (data.best_flights?.length || 0) + (data.other_flights?.length || 0); markdown += `**Best Price**: $${bestPrice}\n`; if (bestFlight?.flights?.[0]?.airline) { markdown += `**Airline**: ${bestFlight.flights[0].airline}\n`; } markdown += `**Flights Found**: ${flightCount}\n\n`; if (params.include_price_insights !== false && data.price_insights) { markdown += `**Price Level**: ${data.price_insights.price_level}\n`; if (data.price_insights.typical_price_range) { markdown += `**Typical Range**: $${data.price_insights.typical_price_range.join('-$')}\n`; } } return markdown; } // Add best flights if (data.best_flights && params.max_best_flights && params.max_best_flights > 0) { markdown += `## Best Options\n\n`; const bestFlights = data.best_flights.slice(0, params.max_best_flights); bestFlights.forEach((flight: any) => { const stops = flight.flights && flight.flights.length > 1 ? `${flight.flights.length - 1} stop${flight.flights.length > 2 ? 's' : ''}` : 'Direct'; const totalDuration = formatDuration(flight.total_duration); markdown += `### $${flight.price} • ${totalDuration} • ${stops}\n`; if (params.include_links && googleFlightsUrl) { markdown += `**URL**: ${googleFlightsUrl}\n`; } if (flight.flights && flight.flights.length > 0) { flight.flights.forEach((segment: any, index: number) => { const depAirport = segment.departure_airport?.code || segment.departure_airport?.name || segment.departure_airport || 'Unknown'; const arrAirport = segment.arrival_airport?.code || segment.arrival_airport?.name || segment.arrival_airport || 'Unknown'; const depTime = segment.departure_airport?.time || 'Unknown'; const arrTime = segment.arrival_airport?.time || 'Unknown'; markdown += `${segment.airline} ${segment.flight_number}: ${depAirport} ${depTime} → ${arrAirport} ${arrTime}\n`; // Add layover information if this is not the last segment if (index < flight.flights.length - 1 && flight.layovers?.[index]) { const layover = flight.layovers[index]; const layoverDuration = formatDuration(layover.duration); markdown += ` Layover: ${layoverDuration}\n`; } }); } markdown += `\n`; }); } // Add other flights if (data.other_flights && params.max_other_flights && params.max_other_flights > 0) { markdown += `## Other Options\n\n`; const otherFlights = data.other_flights.slice(0, params.max_other_flights); otherFlights.forEach((flight: any) => { const stops = flight.flights && flight.flights.length > 1 ? `${flight.flights.length - 1} stop${flight.flights.length > 2 ? 's' : ''}` : 'Direct'; const totalDuration = formatDuration(flight.total_duration); markdown += `### $${flight.price} • ${totalDuration} • ${stops}\n`; if (params.include_links && googleFlightsUrl) { markdown += `**URL**: ${googleFlightsUrl}\n`; } if (flight.flights && flight.flights.length > 0) { flight.flights.forEach((segment: any, index: number) => { const depAirport = segment.departure_airport?.code || segment.departure_airport?.name || segment.departure_airport || 'Unknown'; const arrAirport = segment.arrival_airport?.code || segment.arrival_airport?.name || segment.arrival_airport || 'Unknown'; const depTime = segment.departure_airport?.time || 'Unknown'; const arrTime = segment.arrival_airport?.time || 'Unknown'; markdown += `${segment.airline} ${segment.flight_number}: ${depAirport} ${depTime} → ${arrAirport} ${arrTime}\n`; // Add layover information if this is not the last segment if (index < flight.flights.length - 1 && flight.layovers?.[index]) { const layover = flight.layovers[index]; const layoverDuration = formatDuration(layover.duration); markdown += ` Layover: ${layoverDuration}\n`; } }); } markdown += `\n`; }); } // Add price insights for detailed view if (params.include_price_insights !== false && data.price_insights && !params.summary_only) { markdown += `**Price Level**: ${data.price_insights.price_level} • Lowest: $${data.price_insights.lowest_price}\n`; } return markdown; }