airports-search
Search for flight information and pricing between airports using Google Flights data. Find available flights, compare prices, and get travel options for one-way, round-trip, or multi-city journeys.
Instructions
Search for airport and flight information using Google Flights
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| arrival_id | Yes | Arrival airport code (e.g., 'NRT', 'LAX', 'JFK', 'LHR'). Use 3-letter IATA codes. | |
| departure_id | Yes | Departure airport code (e.g., 'CDG', 'AUS', 'LAX', 'SFO'). Use 3-letter IATA codes. | |
| include_airports | No | Include airport information | |
| include_links | No | Include booking links for each flight (off by default) | |
| include_price_insights | No | Include price insights and history | |
| max_best_flights | No | Maximum number of best flights to return | |
| max_other_flights | No | Maximum number of other flights to return | |
| multi_city_json | No | JSON string for multi-city trips with multiple legs (required when type=3) | |
| 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) | |
| summary_only | No | Return only essential flight information | |
| 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. |
Input Schema (JSON Schema)
{
"properties": {
"arrival_id": {
"description": "Arrival airport code (e.g., 'NRT', 'LAX', 'JFK', 'LHR'). Use 3-letter IATA codes.",
"type": "string"
},
"departure_id": {
"description": "Departure airport code (e.g., 'CDG', 'AUS', 'LAX', 'SFO'). Use 3-letter IATA codes.",
"type": "string"
},
"include_airports": {
"default": false,
"description": "Include airport information",
"type": "boolean"
},
"include_links": {
"default": false,
"description": "Include booking links for each flight (off by default)",
"type": "boolean"
},
"include_price_insights": {
"default": true,
"description": "Include price insights and history",
"type": "boolean"
},
"max_best_flights": {
"default": 3,
"description": "Maximum number of best flights to return",
"type": "number"
},
"max_other_flights": {
"default": 5,
"description": "Maximum number of other flights to return",
"type": "number"
},
"multi_city_json": {
"description": "JSON string for multi-city trips with multiple legs (required when type=3)",
"type": "string"
},
"outbound_date": {
"description": "Outbound date in YYYY-MM-DD format",
"type": "string"
},
"return_date": {
"description": "Return date in YYYY-MM-DD format for round-trip (REQUIRED when type=1)",
"type": "string"
},
"summary_only": {
"default": true,
"description": "Return only essential flight information",
"type": "boolean"
},
"type": {
"default": 2,
"description": "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.",
"type": "number"
}
},
"required": [
"departure_id",
"arrival_id"
],
"type": "object"
}
Implementation Reference
- src/index.ts:151-179 (registration)Registers the 'airports-search' tool using McpServer.tool(), providing name, description, schema from airportsSearchSchema.shape, and a wrapper async handler that calls searchAirports and formats response.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:6-58 (schema)Zod schema defining input parameters for the airports-search tool, including airports, dates, trip type, 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/airports.ts:173-221 (handler)The core handler function searchAirports that constructs SerpAPI query for Google Flights, fetches data using axios, and formats the response using formatFlightToMarkdown.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:69-170 (helper)Helper function to format raw API flight data into a structured Markdown response, handling summary and detailed views.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; }