plan_journey
Plan journeys between two London locations using Transport for London's journey planner to find optimal routes and travel options.
Instructions
Plan journeys between two locations using the TfL Journey Planner.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| app_key | No | Optional application key for the API. | |
| fromLocation | Yes | Origin location (station name, place name, or UK postcode). | |
| toLocation | Yes | Destination location (station name, place name, or UK postcode). |
Implementation Reference
- tools/tfl/journey-planner.js:12-110 (handler)Main handler function `executeFunction` that implements the core logic for the `plan_journey` tool: input validation, TfL API request construction and execution, disambiguation handling for ambiguous locations, journey data summarization using helper functions, and error management.const executeFunction = async (args = {}) => { const { fromLocation, toLocation, app_key, ...queryParams } = args; if (!fromLocation) { throw new Error('fromLocation is required'); } if (!toLocation) { throw new Error('toLocation is required'); } const params = new URLSearchParams(); if (app_key) { params.append('app_key', app_key); } for (const [key, value] of Object.entries(queryParams)) { if (value === undefined || value === null || value === '') continue; params.append(key, value); } const query = params.toString(); const url = `https://api.tfl.gov.uk/journey/journeyresults/${encodeURIComponent( fromLocation )}/to/${encodeURIComponent(toLocation)}${query ? `?${query}` : ''}`; try { const response = await fetch(url, { method: 'GET' }); let data = null; try { data = await response.json(); } catch { data = null; } if (response.status === 300 && data) { const fromOptions = formatDisambiguation(data.fromLocationDisambiguation); const toOptions = formatDisambiguation(data.toLocationDisambiguation); let message = 'Multiple location matches found. Please specify which locations you meant:\n\n'; if (fromOptions.length > 0) { message += `**From "${fromLocation}" - choose one:**\n`; fromOptions.forEach((option, index) => { message += `${index + 1}. ${option.name} (${option.placeType || 'Station'})\n`; }); message += '\n'; } if (toOptions.length > 0) { message += `**To "${toLocation}" - choose one:**\n`; toOptions.forEach((option, index) => { message += `${index + 1}. ${option.name} (${option.placeType || 'Station'})\n`; }); message += '\n'; } message += '**Alternative:** You can also use UK postcodes for more precise locations (e.g., "SW19 7NE" for Wimbledon area).\n\n'; message += 'Please tell me which specific stations you want to use, or provide postcodes for your origin and destination.'; return { message, requiresUserChoice: true, fromOptions, toOptions, }; } if (!response.ok) { throw new Error( data ? JSON.stringify(data) : `Unexpected response with status ${response.status}` ); } // Extract simplified journey data for successful responses if (data?.journeys) { return { journeys: data.journeys.map(extractJourneySummary), searchCriteria: data.searchCriteria }; } return ( data ?? { message: 'Journey planned successfully, but no response body was returned.', } ); } catch (error) { console.error('Error fetching journey plan:', error); return { error: `An error occurred while fetching the journey plan: ${ error instanceof Error ? error.message : JSON.stringify(error) }`, }; } };
- tools/tfl/journey-planner.js:117-139 (schema)Input schema definition for the `plan_journey` tool, specifying required parameters `fromLocation` and `toLocation`, optional `app_key`, and tool metadata like name and description.name: 'plan_journey', description: 'Plan journeys between two locations using the TfL Journey Planner.', parameters: { type: 'object', properties: { fromLocation: { type: 'string', description: 'Origin location (station name, place name, or UK postcode).', }, toLocation: { type: 'string', description: 'Destination location (station name, place name, or UK postcode).', }, app_key: { type: 'string', description: 'Optional application key for the API.', }, }, required: ['fromLocation', 'toLocation'], additionalProperties: true, }, }, },
- tools/paths.js:1-5 (registration)Registration of tool paths; `tfl/journey-planner.js` is listed here, enabling dynamic import in `discoverTools()`.export const toolPaths = [ 'tfl/status.js', 'tfl/status-detail.js', 'tfl/journey-planner.js' ];
- tools/tfl/helper-functions.js:62-126 (helper)Helper function `formatDisambiguation` used by the handler to process and format location disambiguation options from TfL API when multiple matches are found.export const formatDisambiguation = (disambiguation) => { if (!disambiguation) return []; const options = disambiguation.matches || disambiguation.disambiguationOptions || []; const normalized = options.map((option) => { if (option.name) { const { id, name, lat, lon, matchQuality, modes, routeType, placeType, parameterStatus, } = option; return { parameterValue: id, name, matchQuality, modes, routeType, placeType, parameterStatus, lat, lon, }; } const { parameterValue, uri, place = {}, matchQuality, } = option; const { commonName, placeType, lat, lon, modes, naptanId, icsCode, } = place; return { parameterValue, uri, name: commonName, matchQuality, modes, placeType, lat, lon, naptanId, icsCode, }; }); return normalized .sort((a, b) => (b.matchQuality ?? 0) - (a.matchQuality ?? 0)) .slice(0, 3); };
- tools/tfl/helper-functions.js:6-14 (helper)Helper function `extractJourneySummary` used by the handler to simplify and extract key information from TfL journey data.export const extractJourneySummary = (journey) => { return { startDateTime: journey.startDateTime, arrivalDateTime: journey.arrivalDateTime, duration: journey.duration, legs: journey.legs?.map(extractLegSummary) || [], alternativeRoute: journey.alternativeRoute }; };