Skip to content

Commit 9d4e4e2

Browse files
authored
fix: include transitDetails in Routes API field mask (#81)
Add routes.legs.steps.transitDetails to COMPUTE_ROUTES_FIELD_MASK so transit steps return structured metadata (bus/route number, stop names, headsign, agency, stop count) instead of just a free-text navigationInstruction. Closes #78. Also resolves the symptom reported in #74 (comment) ("take a bus but doesn't say which one"), which shares the same root cause. Adds a smoke test using Dublin Airport -> Trinity College Dublin. Includes a small prettier pass on three route-tool schemas (avoid_tolls one-liner from #80) and the new test to satisfy the format:check CI step running for the first time.
1 parent 44b5e88 commit 9d4e4e2

5 files changed

Lines changed: 59 additions & 3 deletions

File tree

src/services/RoutesService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const COMPUTE_ROUTES_FIELD_MASK = [
2525
"routes.legs.steps.staticDuration",
2626
"routes.legs.steps.startLocation",
2727
"routes.legs.steps.endLocation",
28+
"routes.legs.steps.transitDetails",
2829
"routes.legs.polyline",
2930
"routes.optimizedIntermediateWaypointIndex",
3031
].join(",");

src/tools/maps/directions.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ const SCHEMA = {
1515
.describe("Travel mode for directions"),
1616
departure_time: z.string().optional().describe("Departure time (ISO string format)"),
1717
arrival_time: z.string().optional().describe("Arrival time (ISO string format)"),
18-
avoid_tolls: z.boolean().optional().describe('Avoid toll roads where reasonable. Only supported with mode "driving".'),
18+
avoid_tolls: z
19+
.boolean()
20+
.optional()
21+
.describe('Avoid toll roads where reasonable. Only supported with mode "driving".'),
1922
avoid_highways: z
2023
.boolean()
2124
.optional()

src/tools/maps/distanceMatrix.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ const SCHEMA = {
1919
.describe(
2020
"Departure time in ISO 8601 format (e.g. 2026-03-21T09:00:00Z). Enables traffic-aware duration estimates."
2121
),
22-
avoid_tolls: z.boolean().optional().describe('Avoid toll roads where reasonable. Only supported with mode "driving".'),
22+
avoid_tolls: z
23+
.boolean()
24+
.optional()
25+
.describe('Avoid toll roads where reasonable. Only supported with mode "driving".'),
2326
avoid_highways: z
2427
.boolean()
2528
.optional()

src/tools/maps/planRoute.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ const SCHEMA = {
1919
.string()
2020
.optional()
2121
.describe("Departure time in ISO 8601 format (e.g. 2026-03-21T09:00:00Z). Enables traffic-aware routing."),
22-
avoid_tolls: z.boolean().optional().describe('Avoid toll roads where reasonable. Only supported with mode "driving".'),
22+
avoid_tolls: z
23+
.boolean()
24+
.optional()
25+
.describe('Avoid toll roads where reasonable. Only supported with mode "driving".'),
2326
avoid_highways: z
2427
.boolean()
2528
.optional()

tests/smoke.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,51 @@ async function testTransitErrorMessages(session: McpSession): Promise<void> {
11031103
}
11041104
}
11051105

1106+
async function testTransitDetailsField(session: McpSession): Promise<void> {
1107+
if (!API_KEY) {
1108+
console.log(" ⏭️ Skipped transitDetails test (no GOOGLE_MAPS_API_KEY)");
1109+
return;
1110+
}
1111+
1112+
console.log("\n🔍 Testing transit step includes transitDetails (field mask)...");
1113+
1114+
// Dublin Airport → city centre: virtually always returns a bus step (issue #78 scope).
1115+
const result = await sendRequest(session, "tools/call", {
1116+
name: "maps_directions",
1117+
arguments: {
1118+
origin: "Dublin Airport, Ireland",
1119+
destination: "Trinity College Dublin, Ireland",
1120+
mode: "transit",
1121+
},
1122+
});
1123+
const content = result?.result?.content ?? [];
1124+
assert(content.length > 0, "Transit directions in Dublin returns content");
1125+
if (content.length === 0) return;
1126+
1127+
const text = content[0]?.text ?? "";
1128+
const isError = result?.result?.isError === true;
1129+
assert(!isError, `Transit directions in Dublin should succeed, got error: ${text.slice(0, 200)}`);
1130+
if (isError) return;
1131+
1132+
const parsed = JSON.parse(text);
1133+
const steps: any[] = parsed?.routes?.[0]?.legs?.flatMap((l: any) => l.steps ?? []) ?? [];
1134+
assert(steps.length > 0, "Transit response has at least one step");
1135+
1136+
const transitStep = steps.find((s: any) => s.transitDetails);
1137+
if (!transitStep) {
1138+
// Routes API may rarely return a walking-only itinerary; warn but don't flaky-fail.
1139+
console.log(" ⚠️ No transit step in this itinerary — field mask correctness cannot be asserted this run");
1140+
return;
1141+
}
1142+
1143+
const td = transitStep.transitDetails;
1144+
assert(
1145+
td?.transitLine || td?.stopDetails,
1146+
"transitDetails contains structured transit metadata (transitLine or stopDetails)",
1147+
`got: ${JSON.stringify(td).slice(0, 200)}`
1148+
);
1149+
}
1150+
11061151
// --------------- Main ---------------
11071152

11081153
async function main() {
@@ -1127,6 +1172,7 @@ async function main() {
11271172
await testToolCalls(session);
11281173
await testPlaceDetailsPhotos(session);
11291174
await testTransitErrorMessages(session);
1175+
await testTransitDetailsField(session);
11301176
await testMultiSession();
11311177
} catch (err) {
11321178
console.error("\n💥 Fatal error:", err);

0 commit comments

Comments
 (0)