go-transit-mcp 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +68 -0
  3. package/dist/entry/http.js +78 -0
  4. package/dist/entry/http.js.map +1 -0
  5. package/dist/entry/stdio.js +16 -0
  6. package/dist/entry/stdio.js.map +1 -0
  7. package/dist/errors.js +30 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/metrolinx/cache.js +29 -0
  10. package/dist/metrolinx/cache.js.map +1 -0
  11. package/dist/metrolinx/client.js +180 -0
  12. package/dist/metrolinx/client.js.map +1 -0
  13. package/dist/metrolinx/types.js +5 -0
  14. package/dist/metrolinx/types.js.map +1 -0
  15. package/dist/normalize/fares.js +44 -0
  16. package/dist/normalize/fares.js.map +1 -0
  17. package/dist/normalize/fleet-consist.js +31 -0
  18. package/dist/normalize/fleet-consist.js.map +1 -0
  19. package/dist/normalize/journey.js +184 -0
  20. package/dist/normalize/journey.js.map +1 -0
  21. package/dist/normalize/line-schedule.js +56 -0
  22. package/dist/normalize/line-schedule.js.map +1 -0
  23. package/dist/normalize/list-lines.js +29 -0
  24. package/dist/normalize/list-lines.js.map +1 -0
  25. package/dist/normalize/next-service.js +59 -0
  26. package/dist/normalize/next-service.js.map +1 -0
  27. package/dist/normalize/search-stops.js +113 -0
  28. package/dist/normalize/search-stops.js.map +1 -0
  29. package/dist/normalize/service-alerts.js +61 -0
  30. package/dist/normalize/service-alerts.js.map +1 -0
  31. package/dist/normalize/service-exceptions.js +51 -0
  32. package/dist/normalize/service-exceptions.js.map +1 -0
  33. package/dist/normalize/service-guarantee.js +23 -0
  34. package/dist/normalize/service-guarantee.js.map +1 -0
  35. package/dist/normalize/stop-destinations.js +54 -0
  36. package/dist/normalize/stop-destinations.js.map +1 -0
  37. package/dist/normalize/stop-details.js +44 -0
  38. package/dist/normalize/stop-details.js.map +1 -0
  39. package/dist/normalize/trip-status.js +88 -0
  40. package/dist/normalize/trip-status.js.map +1 -0
  41. package/dist/normalize/trip-updates.js +113 -0
  42. package/dist/normalize/trip-updates.js.map +1 -0
  43. package/dist/normalize/union-departures.js +52 -0
  44. package/dist/normalize/union-departures.js.map +1 -0
  45. package/dist/normalize/vehicle-positions.js +67 -0
  46. package/dist/normalize/vehicle-positions.js.map +1 -0
  47. package/dist/prompts.js +96 -0
  48. package/dist/prompts.js.map +1 -0
  49. package/dist/resources.js +61 -0
  50. package/dist/resources.js.map +1 -0
  51. package/dist/schemas/fares.js +30 -0
  52. package/dist/schemas/fares.js.map +1 -0
  53. package/dist/schemas/fleet-consist.js +32 -0
  54. package/dist/schemas/fleet-consist.js.map +1 -0
  55. package/dist/schemas/journey.js +127 -0
  56. package/dist/schemas/journey.js.map +1 -0
  57. package/dist/schemas/line-schedule.js +43 -0
  58. package/dist/schemas/line-schedule.js.map +1 -0
  59. package/dist/schemas/list-lines.js +29 -0
  60. package/dist/schemas/list-lines.js.map +1 -0
  61. package/dist/schemas/next-service.js +29 -0
  62. package/dist/schemas/next-service.js.map +1 -0
  63. package/dist/schemas/search-stops.js +35 -0
  64. package/dist/schemas/search-stops.js.map +1 -0
  65. package/dist/schemas/service-alerts.js +55 -0
  66. package/dist/schemas/service-alerts.js.map +1 -0
  67. package/dist/schemas/service-exceptions.js +27 -0
  68. package/dist/schemas/service-exceptions.js.map +1 -0
  69. package/dist/schemas/service-guarantee.js +32 -0
  70. package/dist/schemas/service-guarantee.js.map +1 -0
  71. package/dist/schemas/stop-destinations.js +32 -0
  72. package/dist/schemas/stop-destinations.js.map +1 -0
  73. package/dist/schemas/stop-details.js +27 -0
  74. package/dist/schemas/stop-details.js.map +1 -0
  75. package/dist/schemas/trip-status.js +39 -0
  76. package/dist/schemas/trip-status.js.map +1 -0
  77. package/dist/schemas/trip-updates.js +51 -0
  78. package/dist/schemas/trip-updates.js.map +1 -0
  79. package/dist/schemas/union-departures.js +26 -0
  80. package/dist/schemas/union-departures.js.map +1 -0
  81. package/dist/schemas/vehicle-positions.js +48 -0
  82. package/dist/schemas/vehicle-positions.js.map +1 -0
  83. package/dist/server.js +58 -0
  84. package/dist/server.js.map +1 -0
  85. package/dist/time.js +159 -0
  86. package/dist/time.js.map +1 -0
  87. package/dist/tools/get-fares.js +26 -0
  88. package/dist/tools/get-fares.js.map +1 -0
  89. package/dist/tools/get-fleet-consist.js +34 -0
  90. package/dist/tools/get-fleet-consist.js.map +1 -0
  91. package/dist/tools/get-line-schedule.js +27 -0
  92. package/dist/tools/get-line-schedule.js.map +1 -0
  93. package/dist/tools/get-next-service.js +25 -0
  94. package/dist/tools/get-next-service.js.map +1 -0
  95. package/dist/tools/get-service-alerts.js +45 -0
  96. package/dist/tools/get-service-alerts.js.map +1 -0
  97. package/dist/tools/get-service-exceptions.js +25 -0
  98. package/dist/tools/get-service-exceptions.js.map +1 -0
  99. package/dist/tools/get-service-guarantee.js +26 -0
  100. package/dist/tools/get-service-guarantee.js.map +1 -0
  101. package/dist/tools/get-stop-destinations.js +28 -0
  102. package/dist/tools/get-stop-destinations.js.map +1 -0
  103. package/dist/tools/get-stop-details.js +25 -0
  104. package/dist/tools/get-stop-details.js.map +1 -0
  105. package/dist/tools/get-trip-status.js +31 -0
  106. package/dist/tools/get-trip-status.js.map +1 -0
  107. package/dist/tools/get-trip-updates.js +26 -0
  108. package/dist/tools/get-trip-updates.js.map +1 -0
  109. package/dist/tools/get-union-departures.js +29 -0
  110. package/dist/tools/get-union-departures.js.map +1 -0
  111. package/dist/tools/get-vehicle-positions.js +30 -0
  112. package/dist/tools/get-vehicle-positions.js.map +1 -0
  113. package/dist/tools/list-lines.js +27 -0
  114. package/dist/tools/list-lines.js.map +1 -0
  115. package/dist/tools/plan-journey.js +46 -0
  116. package/dist/tools/plan-journey.js.map +1 -0
  117. package/dist/tools/plan-trip.js +81 -0
  118. package/dist/tools/plan-trip.js.map +1 -0
  119. package/dist/tools/search-stops.js +25 -0
  120. package/dist/tools/search-stops.js.map +1 -0
  121. package/dist/tools/test-support.js +93 -0
  122. package/dist/tools/test-support.js.map +1 -0
  123. package/package.json +57 -0
@@ -0,0 +1,54 @@
1
+ import { MetrolinxError } from "../errors.js";
2
+ // GO Transit's known line roster. Stop/Destinations carries no line-name
3
+ // field (only Code/Display/Direction/DestinationStop) — best-effort static
4
+ // map, same pattern as the enum-expansion tables elsewhere; falls back to
5
+ // the raw code for anything not yet seen.
6
+ const LINE_NAMES = {
7
+ LW: "Lakeshore West",
8
+ LE: "Lakeshore East",
9
+ MI: "Milton",
10
+ GT: "Kitchener",
11
+ RH: "Richmond Hill",
12
+ BR: "Barrie",
13
+ ST: "Stouffville",
14
+ UP: "UP Express",
15
+ };
16
+ // Display is "{Code} - {destination name}" (confirmed live, issue #7); the
17
+ // destination name has no separate field, so it's recovered by stripping
18
+ // the line-code prefix.
19
+ function destinationStopName(line) {
20
+ const prefix = `${line.Code} - `;
21
+ return line.Display.startsWith(prefix)
22
+ ? line.Display.slice(prefix.length)
23
+ : line.Display;
24
+ }
25
+ export function normalizeStopDestinations(raw) {
26
+ const stop = raw.Stop;
27
+ if (!stop) {
28
+ throw new MetrolinxError("not_found", "No stop matches that code. Verify the code via search_stops.", false);
29
+ }
30
+ // The upstream feed repeats one entry per departure in the window rather
31
+ // than once per distinct destination (confirmed live) — dedupe down to
32
+ // unique line/direction/destination combinations.
33
+ const seen = new Set();
34
+ const destinations = [];
35
+ for (const line of stop.Line ?? []) {
36
+ const key = `${line.Code}|${line.Direction}|${line.DestinationStop}`;
37
+ if (seen.has(key))
38
+ continue;
39
+ seen.add(key);
40
+ destinations.push({
41
+ line_code: line.Code,
42
+ line_name: LINE_NAMES[line.Code] ?? line.Code,
43
+ direction: line.Direction,
44
+ destination_stop_code: line.DestinationStop,
45
+ destination_stop_name: destinationStopName(line),
46
+ });
47
+ }
48
+ return {
49
+ destinations,
50
+ truncated: false,
51
+ total_matched: destinations.length,
52
+ };
53
+ }
54
+ //# sourceMappingURL=stop-destinations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-destinations.js","sourceRoot":"","sources":["../../src/normalize/stop-destinations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAU9C,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,0CAA0C;AAC1C,MAAM,UAAU,GAA2B;IACzC,EAAE,EAAE,gBAAgB;IACpB,EAAE,EAAE,gBAAgB;IACpB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,WAAW;IACf,EAAE,EAAE,eAAe;IACnB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,aAAa;IACjB,EAAE,EAAE,YAAY;CACjB,CAAC;AAEF,2EAA2E;AAC3E,yEAAyE;AACzE,wBAAwB;AACxB,SAAS,mBAAmB,CAAC,IAAwB;IACnD,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;QACpC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;QACnC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,GAAgC;IAEhC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,cAAc,CACtB,WAAW,EACX,8DAA8D,EAC9D,KAAK,CACN,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,uEAAuE;IACvE,kDAAkD;IAClD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,YAAY,GAAkB,EAAE,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACrE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,YAAY,CAAC,IAAI,CAAC;YAChB,SAAS,EAAE,IAAI,CAAC,IAAI;YACpB,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;YAC7C,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,qBAAqB,EAAE,IAAI,CAAC,eAAe;YAC3C,qBAAqB,EAAE,mBAAmB,CAAC,IAAI,CAAC;SACjD,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,YAAY;QACZ,SAAS,EAAE,KAAK;QAChB,aAAa,EAAE,YAAY,CAAC,MAAM;KACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { MetrolinxError } from "../errors.js";
2
+ // Facility codes that signal wheelchair-accessible train service (Data
3
+ // Catalogue). Metrolinx has no dedicated AccessibilityInfo field — verified
4
+ // against a live capture (issue #3) — so accessibility is derived here.
5
+ const ACCESSIBILITY_FACILITY_CODES = new Set(["WAT", "UPWAT"]);
6
+ function pickLang(en, fr, lang) {
7
+ // Bilingual field pairs collapse to one field per requested language
8
+ // (tool-schemas spec §1.1); live data sometimes ships an empty French
9
+ // string, so fall back to English rather than returning "".
10
+ if (lang === "fr" && fr)
11
+ return fr;
12
+ return en;
13
+ }
14
+ export function normalizeStopDetails(raw, lang = "en") {
15
+ const stop = raw.Stop;
16
+ if (!stop) {
17
+ throw new MetrolinxError("not_found", "No stop matches that code. Verify the code via search_stops.", false);
18
+ }
19
+ const facilities = stop.Facilities ?? [];
20
+ const facilityDescriptions = facilities.map((f) => pickLang(f.Description, f.DescriptionFr, lang));
21
+ const accessibilityText = facilities
22
+ .filter((f) => ACCESSIBILITY_FACILITY_CODES.has(f.Code))
23
+ .map((f) => pickLang(f.Description, f.DescriptionFr, lang))
24
+ .join("; ");
25
+ const boardingInfo = pickLang(stop.BoardingInfo, stop.BoardingInfoFr, lang);
26
+ const drivingDirections = pickLang(stop.DrivingDirections, stop.DrivingDirectionsFr, lang);
27
+ return {
28
+ stop_code: stop.Code,
29
+ stop_name: pickLang(stop.StopName, stop.StopNameFr, lang),
30
+ city: stop.City ?? "",
31
+ coordinates: { lat: Number(stop.Latitude), lon: Number(stop.Longitude) },
32
+ served_by: { train: stop.IsTrain, bus: stop.IsBus },
33
+ facilities: facilityDescriptions,
34
+ parking: (stop.Parkings ?? []).map((lot) => ({
35
+ name: pickLang(lot.Name, lot.NameFr, lang),
36
+ spaces: Number(lot.ParkSpots) || 0,
37
+ type: lot.Type || "unknown",
38
+ })),
39
+ ...(accessibilityText ? { accessibility_info: accessibilityText } : {}),
40
+ ...(boardingInfo ? { boarding_info: boardingInfo } : {}),
41
+ ...(drivingDirections ? { driving_directions: drivingDirections } : {}),
42
+ };
43
+ }
44
+ //# sourceMappingURL=stop-details.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-details.js","sourceRoot":"","sources":["../../src/normalize/stop-details.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { MetrolinxError } from "../errors.js";
2
+ import { combineDateAndHhmm } from "../time.js";
3
+ // Same S/M vocabulary already confirmed for Stop/NextService's Status field
4
+ // (tool-schemas spec §5): S = scheduled, M = modified. Confirmed live
5
+ // (issue #8 follow-up) on both Schedule/Trip's top-level trip Status and
6
+ // each stop's own Status field. An empty string (observed on a trip's
7
+ // not-yet-scheduled terminal stop) passes through unchanged.
8
+ function expandScheduleStatus(status) {
9
+ if (status === "S")
10
+ return "scheduled";
11
+ if (status === "M")
12
+ return "modified";
13
+ return status;
14
+ }
15
+ function isoOrUndefined(date, hhmm) {
16
+ return hhmm ? combineDateAndHhmm(date, hhmm) : undefined;
17
+ }
18
+ function buildStopNames(stopAll) {
19
+ const names = new Map();
20
+ for (const entry of stopAll.Stations?.Station ?? []) {
21
+ names.set(entry.LocationCode, entry.LocationName);
22
+ if (entry.PublicStopId)
23
+ names.set(entry.PublicStopId, entry.LocationName);
24
+ }
25
+ return names;
26
+ }
27
+ // Longitude/Latitude are 0/0 when the trip isn't currently tracked
28
+ // (confirmed live, issue #8 follow-up) — a different placeholder than
29
+ // Stop/NextService's -1/-1. Both are treated as "not tracked" defensively.
30
+ function tripPosition(trip) {
31
+ const lat = Number(trip.Latitude);
32
+ const lon = Number(trip.Longitude);
33
+ if (!Number.isFinite(lat) || !Number.isFinite(lon))
34
+ return undefined;
35
+ if (lat === 0 && lon === 0)
36
+ return undefined;
37
+ if (lat === -1 && lon === -1)
38
+ return undefined;
39
+ return { lat, lon };
40
+ }
41
+ function normalizeTripStop(stop, stopNames, date) {
42
+ const track = {};
43
+ if (stop.Track?.Scheduled)
44
+ track.scheduled = stop.Track.Scheduled;
45
+ if (stop.Track?.Actual)
46
+ track.actual = stop.Track.Actual;
47
+ const result = {
48
+ stop_code: stop.Code,
49
+ stop_name: stopNames.get(stop.Code) ?? stop.Code,
50
+ track,
51
+ status: expandScheduleStatus(stop.Status),
52
+ };
53
+ const scheduledArrival = isoOrUndefined(date, stop.ArrivalTime?.Scheduled);
54
+ if (scheduledArrival)
55
+ result.scheduled_arrival = scheduledArrival;
56
+ const expectedArrival = isoOrUndefined(date, stop.ArrivalTime?.Computed);
57
+ if (expectedArrival)
58
+ result.expected_arrival = expectedArrival;
59
+ const scheduledDeparture = isoOrUndefined(date, stop.DepartureTime?.Scheduled);
60
+ if (scheduledDeparture)
61
+ result.scheduled_departure = scheduledDeparture;
62
+ const expectedDeparture = isoOrUndefined(date, stop.DepartureTime?.Computed);
63
+ if (expectedDeparture)
64
+ result.expected_departure = expectedDeparture;
65
+ return result;
66
+ }
67
+ /**
68
+ * @param date The operational day requested (YYYY-MM-DD), used to anchor
69
+ * Schedule/Trip's bare "HH:MM" stop times — the raw payload carries no date
70
+ * component of its own (confirmed live, issue #8 follow-up).
71
+ */
72
+ export function normalizeTripStatus(raw, stopAll, date) {
73
+ const trip = raw.Trips?.[0];
74
+ if (!trip) {
75
+ throw new MetrolinxError("not_found", "No trip matches that number/date. Verify the trip number via " +
76
+ "get_next_service or get_union_departures.", false);
77
+ }
78
+ const stopNames = buildStopNames(stopAll);
79
+ const position = tripPosition(trip);
80
+ return {
81
+ trip_number: trip.Number,
82
+ destination: stopNames.get(trip.Destination) ?? trip.Destination,
83
+ status: expandScheduleStatus(trip.Status),
84
+ ...(position ? { position } : {}),
85
+ stops: (trip.Stops ?? []).map((stop) => normalizeTripStop(stop, stopNames, date)),
86
+ };
87
+ }
88
+ //# sourceMappingURL=trip-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trip-status.js","sourceRoot":"","sources":["../../src/normalize/trip-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import { toIsoFromEpochSeconds } from "../time.js";
2
+ // GTFS-RT trip_id / route_id encode "<date>-<lineOrRouteCode>-<tripNumber>"
3
+ // / "<feedVersion>-<lineOrRouteCode>" (confirmed live, issue #3/#11) — the
4
+ // trip number / line code is always the last dash-delimited segment.
5
+ function lastSegment(id) {
6
+ const parts = id.split("-");
7
+ return parts[parts.length - 1] ?? id;
8
+ }
9
+ function expandScheduleRelationship(relationship) {
10
+ if (relationship === "SCHEDULED")
11
+ return "scheduled";
12
+ if (relationship === "SKIPPED")
13
+ return "skipped";
14
+ if (relationship === "CANCELED")
15
+ return "cancelled";
16
+ return relationship;
17
+ }
18
+ function stopEventIso(event) {
19
+ if (!event || event.time == null)
20
+ return {};
21
+ const expected = toIsoFromEpochSeconds(event.time);
22
+ if (event.delay == null)
23
+ return { expected };
24
+ return {
25
+ scheduled: toIsoFromEpochSeconds(event.time - event.delay),
26
+ expected,
27
+ };
28
+ }
29
+ function normalizeStopUpdate(raw) {
30
+ const arrival = stopEventIso(raw.arrival);
31
+ const departure = stopEventIso(raw.departure);
32
+ const stopUpdate = {
33
+ stop_code: raw.stop_id,
34
+ status: expandScheduleRelationship(raw.schedule_relationship),
35
+ };
36
+ if (arrival.scheduled)
37
+ stopUpdate.scheduled_arrival = arrival.scheduled;
38
+ if (arrival.expected)
39
+ stopUpdate.expected_arrival = arrival.expected;
40
+ if (departure.scheduled)
41
+ stopUpdate.scheduled_departure = departure.scheduled;
42
+ if (departure.expected)
43
+ stopUpdate.expected_departure = departure.expected;
44
+ return stopUpdate;
45
+ }
46
+ // Confirmed live (issue #3/#11): a trip's delay is constant across every
47
+ // stop_time_update entry — take the first one seen rather than averaging.
48
+ function tripDelayMinutes(stopTimeUpdates) {
49
+ for (const stu of stopTimeUpdates) {
50
+ const event = stu.departure ?? stu.arrival;
51
+ if (event?.delay != null)
52
+ return Math.round(event.delay / 60);
53
+ }
54
+ return 0;
55
+ }
56
+ // "Material update" per the tool-schemas spec §2.16: cancelled trumps
57
+ // modified (partial stop cancellation/skip) trumps a >=3min delay; anything
58
+ // else is on_time. The same threshold drives both this per-trip status and
59
+ // the unfiltered-call disruptions-only filter below, by design.
60
+ function tripStatus(tripUpdate, delayMinutes) {
61
+ if (tripUpdate.trip.schedule_relationship === "CANCELED")
62
+ return "cancelled";
63
+ if (tripUpdate.stop_time_update.some((stu) => stu.schedule_relationship === "SKIPPED")) {
64
+ return "modified";
65
+ }
66
+ if (delayMinutes >= 3)
67
+ return "delayed";
68
+ return "on_time";
69
+ }
70
+ function normalizeOne(tripUpdate) {
71
+ const delayMinutes = tripDelayMinutes(tripUpdate.stop_time_update);
72
+ return {
73
+ trip_number: lastSegment(tripUpdate.trip.trip_id),
74
+ line_code: lastSegment(tripUpdate.trip.route_id),
75
+ // direction_id (0/1) has no confirmed mapping to Metrolinx's line-scoped
76
+ // direction codes without list_lines (not yet implemented) — left unset
77
+ // rather than guessed, per the tool-schemas spec's "populated when
78
+ // upstream provides them" allowance.
79
+ status: tripStatus(tripUpdate, delayMinutes),
80
+ delay_minutes: delayMinutes,
81
+ stop_updates: tripUpdate.stop_time_update.map(normalizeStopUpdate),
82
+ updated_at: toIsoFromEpochSeconds(tripUpdate.timestamp),
83
+ };
84
+ }
85
+ export function normalizeTripUpdates(raw, filters, limit) {
86
+ const hasFilter = Boolean(filters.line_code ?? filters.trip_number ?? filters.stop_code);
87
+ const updates = raw.entity
88
+ .map((entity) => entity.trip_update)
89
+ .filter((tripUpdate) => tripUpdate != null)
90
+ .map(normalizeOne);
91
+ const matched = updates.filter((update) => {
92
+ if (filters.line_code && update.line_code !== filters.line_code) {
93
+ return false;
94
+ }
95
+ if (filters.trip_number && update.trip_number !== filters.trip_number) {
96
+ return false;
97
+ }
98
+ if (filters.stop_code &&
99
+ !update.stop_updates.some((stu) => stu.stop_code === filters.stop_code)) {
100
+ return false;
101
+ }
102
+ if (!hasFilter && update.status === "on_time")
103
+ return false;
104
+ return true;
105
+ });
106
+ const totalMatched = matched.length;
107
+ return {
108
+ updates: matched.slice(0, limit),
109
+ truncated: totalMatched > limit,
110
+ total_matched: totalMatched,
111
+ };
112
+ }
113
+ //# sourceMappingURL=trip-updates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trip-updates.js","sourceRoot":"","sources":["../../src/normalize/trip-updates.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAQnD,4EAA4E;AAC5E,2EAA2E;AAC3E,qEAAqE;AACrE,SAAS,WAAW,CAAC,EAAU;IAC7B,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,0BAA0B,CAAC,YAAoB;IACtD,IAAI,YAAY,KAAK,WAAW;QAAE,OAAO,WAAW,CAAC;IACrD,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACjD,IAAI,YAAY,KAAK,UAAU;QAAE,OAAO,WAAW,CAAC;IACpD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,YAAY,CAAC,KAA8C;IAIlE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC7C,OAAO;QACL,SAAS,EAAE,qBAAqB,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1D,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,GAA0B;IACrD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAE9C,MAAM,UAAU,GAAe;QAC7B,SAAS,EAAE,GAAG,CAAC,OAAO;QACtB,MAAM,EAAE,0BAA0B,CAAC,GAAG,CAAC,qBAAqB,CAAC;KAC9D,CAAC;IACF,IAAI,OAAO,CAAC,SAAS;QAAE,UAAU,CAAC,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC;IACxE,IAAI,OAAO,CAAC,QAAQ;QAAE,UAAU,CAAC,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IACrE,IAAI,SAAS,CAAC,SAAS;QAAE,UAAU,CAAC,mBAAmB,GAAG,SAAS,CAAC,SAAS,CAAC;IAC9E,IAAI,SAAS,CAAC,QAAQ;QAAE,UAAU,CAAC,kBAAkB,GAAG,SAAS,CAAC,QAAQ,CAAC;IAC3E,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,yEAAyE;AACzE,0EAA0E;AAC1E,SAAS,gBAAgB,CAAC,eAAwC;IAChE,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO,CAAC;QAC3C,IAAI,KAAK,EAAE,KAAK,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,sEAAsE;AACtE,4EAA4E;AAC5E,2EAA2E;AAC3E,gEAAgE;AAChE,SAAS,UAAU,CACjB,UAA6B,EAC7B,YAAoB;IAEpB,IAAI,UAAU,CAAC,IAAI,CAAC,qBAAqB,KAAK,UAAU;QAAE,OAAO,WAAW,CAAC;IAC7E,IACE,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAC9B,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,qBAAqB,KAAK,SAAS,CACjD,EACD,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,YAAY,CAAC,UAA6B;IACjD,MAAM,YAAY,GAAG,gBAAgB,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACnE,OAAO;QACL,WAAW,EAAE,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QACjD,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;QAChD,yEAAyE;QACzE,wEAAwE;QACxE,mEAAmE;QACnE,qCAAqC;QACrC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,YAAY,CAAC;QAC5C,aAAa,EAAE,YAAY;QAC3B,YAAY,EAAE,UAAU,CAAC,gBAAgB,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAClE,UAAU,EAAE,qBAAqB,CAAC,UAAU,CAAC,SAAS,CAAC;KACxD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,GAA+B,EAC/B,OAA0B,EAC1B,KAAa;IAEb,MAAM,SAAS,GAAG,OAAO,CACvB,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,SAAS,CAC9D,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM;SACvB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;SACnC,MAAM,CAAC,CAAC,UAAU,EAAmC,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC;SAC3E,GAAG,CAAC,YAAY,CAAC,CAAC;IAErB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QACxC,IAAI,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC;YAChE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;YACtE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IACE,OAAO,CAAC,SAAS;YACjB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC,EACvE,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC5D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IACpC,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QAChC,SAAS,EAAE,YAAY,GAAG,KAAK;QAC/B,aAAa,EAAE,YAAY;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { toIsoWithTorontoOffset } from "../time.js";
2
+ import { normalizeSearchStops } from "./search-stops.js";
3
+ function normalizeMode(serviceType) {
4
+ if (serviceType === "T")
5
+ return "train";
6
+ if (serviceType === "B")
7
+ return "bus";
8
+ return "bus";
9
+ }
10
+ // UnionDepartures' Stops carry a name but no stop code — confirmed live
11
+ // (issue #9): every Code observed across a full capture was null. Resolved
12
+ // via the same fuzzy matcher search_stops uses, since these names are short
13
+ // forms of the full Stop/All LocationName ("Exhibition" vs "Exhibition GO").
14
+ //
15
+ // Filtered to the departure's own mode, not "any": several stations (e.g.
16
+ // Oakville, Port Credit) have *two* Stop/All entries — a standalone Bus Stop
17
+ // and a Train & Bus Station — that tie at the same fuzzy-match tier. The Bus
18
+ // Stop entry happens to sort first, so an unfiltered match silently returned
19
+ // the 6-digit bus stop_code for a train departure's stops. Filtering by mode
20
+ // excludes the bus-only entry (the Train & Bus Station entry still matches
21
+ // under "train" since its stop_type is "both").
22
+ function resolveStopCode(stopAll, name, mode) {
23
+ const match = normalizeSearchStops(stopAll, name, mode, 1).matches[0];
24
+ return match?.stop_code ?? "";
25
+ }
26
+ function normalizeTrip(trip, stopAll) {
27
+ const mode = normalizeMode(trip.ServiceType);
28
+ const dto = {
29
+ trip_number: trip.TripNumber,
30
+ mode,
31
+ service: trip.Service,
32
+ time: toIsoWithTorontoOffset(trip.Time),
33
+ stops_served: (trip.Stops ?? []).map((s) => ({
34
+ stop_code: s.Code ?? resolveStopCode(stopAll, s.Name, mode),
35
+ stop_name: s.Name,
36
+ })),
37
+ };
38
+ if (trip.Platform && trip.Platform !== "-")
39
+ dto.platform = trip.Platform;
40
+ return dto;
41
+ }
42
+ export function normalizeUnionDepartures(raw, stopAll, mode = "any") {
43
+ const trips = raw.AllDepartures?.Trip ?? [];
44
+ const filtered = trips.filter((t) => mode === "any" || normalizeMode(t.ServiceType) === mode);
45
+ const departures = filtered.map((t) => normalizeTrip(t, stopAll));
46
+ return {
47
+ departures,
48
+ truncated: false,
49
+ total_matched: departures.length,
50
+ };
51
+ }
52
+ //# sourceMappingURL=union-departures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"union-departures.js","sourceRoot":"","sources":["../../src/normalize/union-departures.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,SAAS,aAAa,CAAC,WAAmB;IACxC,IAAI,WAAW,KAAK,GAAG;QAAE,OAAO,OAAO,CAAC;IACxC,IAAI,WAAW,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wEAAwE;AACxE,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;AAC7E,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,2EAA2E;AAC3E,gDAAgD;AAChD,SAAS,eAAe,CACtB,OAA2B,EAC3B,IAAY,EACZ,IAAqB;IAErB,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACtE,OAAO,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,aAAa,CACpB,IAAkB,EAClB,OAA2B;IAE3B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAmB;QAC1B,WAAW,EAAE,IAAI,CAAC,UAAU;QAC5B,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;QACvC,YAAY,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,SAAS,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;YAC3D,SAAS,EAAE,CAAC,CAAC,IAAI;SAClB,CAAC,CAAC;KACJ,CAAC;IACF,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG;QAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IACzE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,GAA+B,EAC/B,OAA2B,EAC3B,OAAgC,KAAK;IAErC,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,KAAK,IAAI,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,IAAI,CAC/D,CAAC;IACF,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,UAAU;QACV,SAAS,EAAE,KAAK;QAChB,aAAa,EAAE,UAAU,CAAC,MAAM;KACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { toIsoWithTorontoOffset } from "../time.js";
2
+ import { resolveStopCode } from "./search-stops.js";
3
+ function buildStopNameMap(stopAll) {
4
+ const map = new Map();
5
+ for (const entry of stopAll.Stations?.Station ?? []) {
6
+ map.set(resolveStopCode(entry), entry.LocationName);
7
+ }
8
+ return map;
9
+ }
10
+ // GTFS-RT trip_id / route_id encode "<date>-<lineOrRouteCode>-<tripNumber>"
11
+ // / "<feedVersion>-<lineOrRouteCode>" (confirmed live against the real
12
+ // TripUpdates capture, issue #3/#11) — the trip number is always the last
13
+ // dash-delimited segment, uniformly for train line codes (e.g. "LW") and
14
+ // numeric bus route codes.
15
+ function lastSegment(id) {
16
+ const parts = id.split("-");
17
+ return parts[parts.length - 1] ?? id;
18
+ }
19
+ function buildOccupancyMap(positions) {
20
+ const map = new Map();
21
+ for (const entity of positions.entity) {
22
+ const vehicle = entity.vehicle;
23
+ if (!vehicle || vehicle.occupancy_percentage == null)
24
+ continue;
25
+ map.set(lastSegment(vehicle.trip.trip_id), vehicle.occupancy_percentage);
26
+ }
27
+ return map;
28
+ }
29
+ function normalizeVehicle(trip, mode, stopNames, occupancy) {
30
+ const vehicle = {
31
+ trip_number: trip.TripNumber,
32
+ line_code: trip.LineCode,
33
+ mode,
34
+ position: { lat: trip.Latitude, lon: trip.Longitude },
35
+ delay_minutes: Math.round(trip.DelaySeconds / 60),
36
+ in_motion: trip.IsInMotion,
37
+ updated_at: toIsoWithTorontoOffset(trip.ModifiedDate),
38
+ };
39
+ const occupancyPercent = occupancy.get(trip.TripNumber);
40
+ if (occupancyPercent !== undefined) {
41
+ vehicle.occupancy_percent = occupancyPercent;
42
+ }
43
+ if (trip.NextStopCode) {
44
+ vehicle.next_stop = {
45
+ stop_code: trip.NextStopCode,
46
+ stop_name: stopNames.get(trip.NextStopCode) ?? trip.NextStopCode,
47
+ };
48
+ }
49
+ return vehicle;
50
+ }
51
+ export function normalizeVehiclePositions(glance, positions, stopAll, mode, filters, limit) {
52
+ const stopNames = buildStopNameMap(stopAll);
53
+ const occupancy = buildOccupancyMap(positions);
54
+ const trips = glance.Trips?.Trip ?? [];
55
+ const matched = trips.filter((trip) => (!filters.line_code || trip.LineCode === filters.line_code) &&
56
+ (!filters.trip_number || trip.TripNumber === filters.trip_number));
57
+ const totalMatched = matched.length;
58
+ const vehicles = matched
59
+ .slice(0, limit)
60
+ .map((trip) => normalizeVehicle(trip, mode, stopNames, occupancy));
61
+ return {
62
+ vehicles,
63
+ truncated: totalMatched > limit,
64
+ total_matched: totalMatched,
65
+ };
66
+ }
67
+ //# sourceMappingURL=vehicle-positions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vehicle-positions.js","sourceRoot":"","sources":["../../src/normalize/vehicle-positions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { z } from "zod";
2
+ // Prompts compose tool calls into common flows (ticket 004 §6); they only
3
+ // render instructions for the model — the model is the one that calls the
4
+ // tools, so these handlers never touch MetrolinxClient (tool-schemas §4).
5
+ export function registerPrompts(server) {
6
+ server.registerPrompt("plan_a_trip", {
7
+ title: "Plan a trip",
8
+ description: "Resolve a trip by station name, plan it, and summarize it with fares and alerts.",
9
+ argsSchema: {
10
+ from: z
11
+ .string()
12
+ .describe("Starting station or stop name, e.g. 'Union'."),
13
+ to: z
14
+ .string()
15
+ .describe("Destination station or stop name, e.g. 'Oakville'."),
16
+ when: z
17
+ .string()
18
+ .optional()
19
+ .describe("Freeform departure or arrival time, e.g. 'tomorrow at 8am' or 'now'. Defaults to now."),
20
+ },
21
+ }, ({ from, to, when }) => ({
22
+ messages: [
23
+ {
24
+ role: "user",
25
+ content: {
26
+ type: "text",
27
+ text: [
28
+ `Plan a GO Transit trip from "${from}" to "${to}"${when ? `, ${when}` : ", leaving now"}.`,
29
+ `First, normalize "${when ?? "now"}" yourself into a date (YYYY-MM-DD) and time (HH:MM, 24h, America/Toronto) — plan_trip's inputs are not freeform text. If the phrasing implies an arrival deadline (e.g. "by 9am"), pass the normalized time as arrive_by instead of a departure time.`,
30
+ 'Call plan_trip with from, to, and the normalized date/time. If the result has status: "ambiguous", show the user the candidates for each ambiguous field and ask them to pick before proceeding.',
31
+ "Once you have an itinerary, call get_fares for the fare between the resolved stops and get_service_alerts for the line(s) used, so the summary covers cost and disruptions alongside the schedule.",
32
+ "Summarize conversationally: departure/arrival times, transfers, fare, and any active alerts — and say plainly which stop names you assumed if resolution wasn't exact.",
33
+ ].join("\n"),
34
+ },
35
+ },
36
+ ],
37
+ }));
38
+ server.registerPrompt("check_my_commute", {
39
+ title: "Check my commute",
40
+ description: "Next service, exceptions, and alerts for a home/work stop pair, direction inferred from time of day.",
41
+ argsSchema: {
42
+ home_stop: z.string().describe("Home station or stop name/code."),
43
+ work_stop: z.string().describe("Work station or stop name/code."),
44
+ direction: z
45
+ .string()
46
+ .optional()
47
+ .describe("'to work' or 'to home'. Omit to infer from the current time of day."),
48
+ },
49
+ }, ({ home_stop, work_stop, direction }) => ({
50
+ messages: [
51
+ {
52
+ role: "user",
53
+ content: {
54
+ type: "text",
55
+ text: [
56
+ `Check my GO Transit commute between "${home_stop}" (home) and "${work_stop}" (work).`,
57
+ direction
58
+ ? `Direction: ${direction}.`
59
+ : 'No direction given — infer it from the current time of day: before roughly noon, assume "to work" (depart from home_stop toward work_stop); otherwise assume "to home" (depart from work_stop toward home_stop).',
60
+ "Resolve both stop names to stop codes (search_stops if they aren't already codes), then call get_next_service for the departure stop in the inferred direction, plus get_service_exceptions and get_service_alerts for whatever line(s)/stop(s) are relevant.",
61
+ "Summarize: the next departure(s) and any delay, plus any active exception or alert — call out clearly if the commute looks disrupted right now, not just what's scheduled.",
62
+ ].join("\n"),
63
+ },
64
+ },
65
+ ],
66
+ }));
67
+ server.registerPrompt("service_status", {
68
+ title: "Service status",
69
+ description: "Network-wide (or single-line) GO Transit service status digest.",
70
+ argsSchema: {
71
+ line: z
72
+ .string()
73
+ .optional()
74
+ .describe("Line code or name to scope the digest to, e.g. 'LW'. Defaults to the whole network."),
75
+ },
76
+ }, ({ line }) => ({
77
+ messages: [
78
+ {
79
+ role: "user",
80
+ content: {
81
+ type: "text",
82
+ text: [
83
+ line
84
+ ? `Give me the current GO Transit service status for the ${line} line.`
85
+ : "Give me the current GO Transit service status across the whole network.",
86
+ line
87
+ ? `Call get_service_alerts and get_service_exceptions filtered to the ${line} line (resolve it via list_lines first if it's a name rather than a code).`
88
+ : "Call get_service_alerts and get_service_exceptions with no line filter to cover the whole network.",
89
+ 'Summarize active service alerts and exceptions in plain language. If nothing is active, say so explicitly — a quick glance should answer "is anything wrong right now?".',
90
+ ].join("\n"),
91
+ },
92
+ },
93
+ ],
94
+ }));
95
+ }
96
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { normalizeListLines } from "./normalize/list-lines.js";
3
+ import { normalizeAllStops } from "./normalize/search-stops.js";
4
+ import { normalizeStopDetails } from "./normalize/stop-details.js";
5
+ import { dateToWire, nowInToronto } from "./time.js";
6
+ // Content is exactly the mirror tool's DTO (tool-schemas spec §3) — every
7
+ // handler below calls the same client method and normalizer its mirror tool
8
+ // does, so the two paths cannot drift.
9
+ function jsonResourceResult(uri, dto) {
10
+ return {
11
+ contents: [
12
+ {
13
+ uri: uri.href,
14
+ mimeType: "application/json",
15
+ text: JSON.stringify(dto),
16
+ },
17
+ ],
18
+ };
19
+ }
20
+ function firstValue(value) {
21
+ const resolved = Array.isArray(value) ? value[0] : value;
22
+ if (resolved === undefined) {
23
+ throw new Error("Missing resource template variable");
24
+ }
25
+ return resolved;
26
+ }
27
+ export function registerResources(server, client) {
28
+ server.registerResource("stops", "gotransit://stops", {
29
+ title: "All GO Transit stops",
30
+ description: "Full stop dataset in the search_stops match shape (stop_code, stop_name, stop_type per stop).",
31
+ mimeType: "application/json",
32
+ }, async (uri) => jsonResourceResult(uri, normalizeAllStops(await client.getStopAll())));
33
+ server.registerResource("stop-details", new ResourceTemplate("gotransit://stops/{code}", { list: undefined }), {
34
+ title: "GO Transit stop details",
35
+ description: "Details for one GO stop or station — the get_stop_details tool's DTO.",
36
+ mimeType: "application/json",
37
+ }, async (uri, { code }) => {
38
+ const stopCode = firstValue(code);
39
+ const dto = normalizeStopDetails(await client.getStopDetails(stopCode));
40
+ return jsonResourceResult(uri, dto);
41
+ });
42
+ server.registerResource("lines-for-date", new ResourceTemplate("gotransit://lines/{date}", { list: undefined }), {
43
+ title: "GO Transit line roster",
44
+ description: "GO Transit's line roster for a service day (YYYY-MM-DD) — the list_lines tool's DTO.",
45
+ mimeType: "application/json",
46
+ }, async (uri, { date }) => {
47
+ const dateWire = dateToWire(firstValue(date));
48
+ const dto = normalizeListLines(await client.getLineAll(dateWire));
49
+ return jsonResourceResult(uri, dto);
50
+ });
51
+ server.registerResource("lines-today", "gotransit://lines", {
52
+ title: "Today's GO Transit line roster",
53
+ description: "Static alias for gotransit://lines/{today} — browsable without constructing a dated URI.",
54
+ mimeType: "application/json",
55
+ }, async (uri) => {
56
+ const dateWire = dateToWire(nowInToronto().date);
57
+ const dto = normalizeListLines(await client.getLineAll(dateWire));
58
+ return jsonResourceResult(uri, dto);
59
+ });
60
+ }
61
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}