From 0b427687ccfca65db0a49dc5e0d26ba20ee96b2d Mon Sep 17 00:00:00 2001 From: CabLate Date: Sun, 19 Apr 2026 20:17:22 +0800 Subject: [PATCH 1/5] fix: improve transit error messages for unsupported regions (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Google Routes API does not support transit directions in some regions (notably Japan and India). Previously, users received a generic "No route found" error. Now the error message clearly explains the regional limitation and suggests alternatives. - computeRoutes: detect transit mode + empty routes → descriptive error - computeRouteMatrix: track ROUTE_NOT_FOUND count, throw on all-fail, attach warning on partial-fail for transit mode Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 4 ++++ src/services/RoutesService.ts | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df808e7..2e14d7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.0.51 + +- fix: improve transit error messages for unsupported regions (Japan, India) (#74) + ## 0.0.50 - docs: add CODE_OF_CONDUCT.md diff --git a/src/services/RoutesService.ts b/src/services/RoutesService.ts index d3a3027..ee090c0 100644 --- a/src/services/RoutesService.ts +++ b/src/services/RoutesService.ts @@ -176,8 +176,16 @@ export class RoutesService { const data = await response.json(); if (!data.routes || data.routes.length === 0) { + const mode = params.mode || "driving"; + if (mode === "transit") { + throw new Error( + `No transit route found from "${params.origin}" to "${params.destination}". ` + + `The Google Routes API does not support transit directions in some regions (notably Japan and India). ` + + `Try using mode "driving" or "walking" instead, or use a regional transit service for public transportation details.` + ); + } throw new Error( - `No route found from "${params.origin}" to "${params.destination}" with mode: ${params.mode || "driving"}` + `No route found from "${params.origin}" to "${params.destination}" with mode: ${mode}` ); } @@ -218,6 +226,7 @@ export class RoutesService { durations: any[][]; origin_addresses: string[]; destination_addresses: string[]; + warning?: string; }> { const travelMode = TRAVEL_MODE_MAP[params.mode || "driving"] || "DRIVE"; @@ -260,11 +269,15 @@ export class RoutesService { const distances: any[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null)); const durations: any[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null)); + let routeNotFoundCount = 0; for (const element of elements) { const i = element.originIndex; const j = element.destinationIndex; if (i === undefined || j === undefined) continue; - if (element.condition === "ROUTE_NOT_FOUND") continue; + if (element.condition === "ROUTE_NOT_FOUND") { + routeNotFoundCount++; + continue; + } const distMeters = element.distanceMeters || 0; const durSeconds = parseDuration(element.duration); @@ -279,12 +292,28 @@ export class RoutesService { }; } + const totalPairs = rowCount * colCount; + + // All pairs failed — likely a regional transit limitation + if (routeNotFoundCount === totalPairs && travelMode === "TRANSIT") { + throw new Error( + `No transit routes found for any origin/destination pair. ` + + `The Google Routes API does not support transit directions in some regions (notably Japan and India). ` + + `Try using mode "driving" or "walking" instead, or use a regional transit service for public transportation details.` + ); + } + // Routes API doesn't return resolved addresses; use input strings return { distances, durations, origin_addresses: params.origins, destination_addresses: params.destinations, + ...(routeNotFoundCount > 0 && travelMode === "TRANSIT" + ? { + warning: `${routeNotFoundCount} of ${totalPairs} origin/destination pairs returned no transit route. The Google Routes API has limited transit coverage in some regions.`, + } + : {}), }; } } From ada83d7e25469fac767043693dd97bc6613c5db2 Mon Sep 17 00:00:00 2001 From: CabLate Date: Sun, 19 Apr 2026 20:25:20 +0800 Subject: [PATCH 2/5] test: add transit error message tests for unsupported regions Adds Test 8 to smoke tests verifying: - Transit directions in Japan returns isError with descriptive message - Transit distance matrix in Japan returns descriptive error - Driving directions baseline still works (graceful on transient API issues) Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/smoke.test.ts | 96 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tests/smoke.test.ts b/tests/smoke.test.ts index a01e3b4..e88b798 100644 --- a/tests/smoke.test.ts +++ b/tests/smoke.test.ts @@ -971,6 +971,101 @@ async function testExecMode(): Promise { } } +// --------------- Test 8: Transit Error Messages --------------- + +async function testTransitErrorMessages(session: McpSession): Promise { + console.log("\n🧪 Test 8: Transit error messages for unsupported regions"); + + if (!API_KEY) { + console.log(" ā­ļø Skipped (no GOOGLE_MAPS_API_KEY)"); + return; + } + + // Verify driving mode works fine first (baseline) + const driveResult = await sendRequest(session, "tools/call", { + name: "maps_directions", + arguments: { origin: "Tokyo Station", destination: "Nagoya Station", mode: "driving" }, + }); + const driveContent = driveResult?.result?.content ?? []; + assert(driveContent.length > 0, "Driving directions returns content"); + if (driveContent.length > 0) { + const text = driveContent[0]?.text ?? ""; + const isError = driveResult?.result?.isError === true; + if (isError) { + console.log(` āš ļø Driving directions failed (may be rate-limited): ${text.slice(0, 150)}`); + // Don't fail the test — driving works in general, this is likely a transient API issue + assert(true, "Driving directions callable (transient API issue)"); + assert(true, "Driving directions skipped JSON check"); + } else { + assert(true, "Driving directions in Japan works (no error)"); + try { + const parsed = JSON.parse(text); + assert(parsed?.total_distance !== undefined, "Driving returns total_distance"); + assert(parsed?.total_duration !== undefined, "Driving returns total_duration"); + } catch { + assert(false, "Driving directions returns valid JSON"); + } + } + } + + // Test directions with transit in Japan — should return improved error message + const dirResult = await sendRequest(session, "tools/call", { + name: "maps_directions", + arguments: { origin: "Tokyo Station", destination: "Nagoya Station", mode: "transit" }, + }); + const dirContent = dirResult?.result?.content ?? []; + assert(dirContent.length > 0, "Transit directions returns content"); + if (dirContent.length > 0) { + const text = dirContent[0]?.text ?? ""; + const isError = dirResult?.result?.isError === true; + assert(isError, "Transit directions in Japan returns isError=true"); + assert( + text.includes("does not support transit") || text.includes("transit route"), + "Error message mentions transit limitation", + `got: ${text.slice(0, 200)}` + ); + assert( + text.includes("Japan") || text.includes("region"), + "Error message mentions affected region", + `got: ${text.slice(0, 200)}` + ); + } + + // Test distance matrix with transit in Japan + const dmResult = await sendRequest(session, "tools/call", { + name: "maps_distance_matrix", + arguments: { origins: ["Tokyo Station"], destinations: ["Nagoya Station"], mode: "transit" }, + }); + const dmContent = dmResult?.result?.content ?? []; + assert(dmContent.length > 0, "Transit distance matrix returns content"); + if (dmContent.length > 0) { + const text = dmContent[0]?.text ?? ""; + const isError = dmResult?.result?.isError === true; + // May return error (all-fail) or warning (partial-fail) + if (isError) { + assert( + text.includes("does not support transit") || text.includes("transit route"), + "Distance matrix error mentions transit limitation", + `got: ${text.slice(0, 200)}` + ); + } else { + // Partial success — check for warning in response + try { + const parsed = JSON.parse(text); + const hasWarning = parsed?.warning !== undefined; + const hasNulls = parsed?.distances?.[0]?.[0] === null; + assert( + hasWarning || hasNulls, + "Distance matrix returns warning or null entries for unsupported transit", + `warning=${hasWarning}, nulls=${hasNulls}` + ); + } catch { + assert(false, "Distance matrix returns valid JSON", text.slice(0, 200)); + } + } + } +} + // --------------- Main --------------- async function main() { @@ -994,6 +1089,7 @@ async function main() { await testGeocode(session); await testToolCalls(session); await testPlaceDetailsPhotos(session); + await testTransitErrorMessages(session); await testMultiSession(); } catch (err) { console.error("\nšŸ’„ Fatal error:", err); From 7d37cb2f3639abe9274258ef75f49e97130dc19d Mon Sep 17 00:00:00 2001 From: CabLate Date: Sun, 19 Apr 2026 20:28:40 +0800 Subject: [PATCH 3/5] fix: avoid passing default departureTime to Routes API PlacesSearcher.getDirections defaulted to `new Date()` when no departure_time was provided, but Routes API rejects past timestamps causing sporadic "Timestamp must be set to a future time" errors. Now omits departureTime entirely when not explicitly provided, letting the Routes API use its own default behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/services/PlacesSearcher.ts | 6 +++--- tests/smoke.test.ts | 9 ++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/services/PlacesSearcher.ts b/src/services/PlacesSearcher.ts index e91b4de..fb2e5d3 100644 --- a/src/services/PlacesSearcher.ts +++ b/src/services/PlacesSearcher.ts @@ -328,14 +328,14 @@ export class PlacesSearcher { arrival_time?: string ): Promise { try { - const departureTime = departure_time ? new Date(departure_time) : new Date(); + const departureTime = departure_time ? new Date(departure_time) : undefined; const arrivalTime = arrival_time ? new Date(arrival_time) : undefined; const result = await this.routesService.computeRoutes({ origin, destination, mode, - departureTime, - arrivalTime, + ...(departureTime ? { departureTime } : {}), + ...(arrivalTime ? { arrivalTime } : {}), }); return { diff --git a/tests/smoke.test.ts b/tests/smoke.test.ts index e88b798..0158462 100644 --- a/tests/smoke.test.ts +++ b/tests/smoke.test.ts @@ -991,13 +991,8 @@ async function testTransitErrorMessages(session: McpSession): Promise { if (driveContent.length > 0) { const text = driveContent[0]?.text ?? ""; const isError = driveResult?.result?.isError === true; - if (isError) { - console.log(` āš ļø Driving directions failed (may be rate-limited): ${text.slice(0, 150)}`); - // Don't fail the test — driving works in general, this is likely a transient API issue - assert(true, "Driving directions callable (transient API issue)"); - assert(true, "Driving directions skipped JSON check"); - } else { - assert(true, "Driving directions in Japan works (no error)"); + assert(!isError, "Driving directions in Japan works (no error)", isError ? text.slice(0, 150) : undefined); + if (!isError) { try { const parsed = JSON.parse(text); assert(parsed?.total_distance !== undefined, "Driving returns total_distance"); From d73d3321e7163275f9fce87925c197c9c31ba164 Mon Sep 17 00:00:00 2001 From: CabLate Date: Sun, 19 Apr 2026 20:28:51 +0800 Subject: [PATCH 4/5] docs: add departureTime fix to CHANGELOG Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e14d7f..d56c92e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 0.0.51 - fix: improve transit error messages for unsupported regions (Japan, India) (#74) +- fix: avoid passing default departureTime to Routes API (sporadic "Timestamp must be set to a future time" errors) ## 0.0.50 From 3b248bf9787335444b87ea84526f0c217a4d6351 Mon Sep 17 00:00:00 2001 From: CabLate Date: Sun, 19 Apr 2026 20:48:39 +0800 Subject: [PATCH 5/5] style: format RoutesService and PlacesSearcher with Prettier Co-Authored-By: Claude Opus 4.6 (1M context) --- src/services/RoutesService.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/services/RoutesService.ts b/src/services/RoutesService.ts index ee090c0..6b9d8b5 100644 --- a/src/services/RoutesService.ts +++ b/src/services/RoutesService.ts @@ -184,9 +184,7 @@ export class RoutesService { `Try using mode "driving" or "walking" instead, or use a regional transit service for public transportation details.` ); } - throw new Error( - `No route found from "${params.origin}" to "${params.destination}" with mode: ${mode}` - ); + throw new Error(`No route found from "${params.origin}" to "${params.destination}" with mode: ${mode}`); } const route = data.routes[0];