Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 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

- docs: add CODE_OF_CONDUCT.md
Expand Down
6 changes: 3 additions & 3 deletions src/services/PlacesSearcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,14 @@ export class PlacesSearcher {
arrival_time?: string
): Promise<DirectionsResponse> {
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 {
Expand Down
35 changes: 31 additions & 4 deletions src/services/RoutesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,15 @@ export class RoutesService {
const data = await response.json();

if (!data.routes || data.routes.length === 0) {
throw new Error(
`No route found from "${params.origin}" to "${params.destination}" with mode: ${params.mode || "driving"}`
);
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: ${mode}`);
}

const route = data.routes[0];
Expand Down Expand Up @@ -218,6 +224,7 @@ export class RoutesService {
durations: any[][];
origin_addresses: string[];
destination_addresses: string[];
warning?: string;
}> {
const travelMode = TRAVEL_MODE_MAP[params.mode || "driving"] || "DRIVE";

Expand Down Expand Up @@ -260,11 +267,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);
Expand All @@ -279,12 +290,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.`,
}
: {}),
};
}
}
91 changes: 91 additions & 0 deletions tests/smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,96 @@ async function testExecMode(): Promise<void> {
}
}

// --------------- Test 8: Transit Error Messages ---------------

async function testTransitErrorMessages(session: McpSession): Promise<void> {
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;
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");
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() {
Expand All @@ -994,6 +1084,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);
Expand Down
Loading