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,28 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeStopDestinations } from "../normalize/stop-destinations.js";
3
+ import { getStopDestinationsInputShape, stopDestinationsOutputShape, } from "../schemas/stop-destinations.js";
4
+ import { addHoursToTime, hhmmToWire, nowInToronto } from "../time.js";
5
+ export function registerGetStopDestinations(server, client) {
6
+ server.registerTool("get_stop_destinations", {
7
+ title: "Get stop destinations",
8
+ description: "Where a GO Transit stop's services go within a time window (default: now to +4h, Toronto).",
9
+ inputSchema: getStopDestinationsInputShape,
10
+ outputSchema: stopDestinationsOutputShape,
11
+ }, async ({ stop_code, from_time, to_time }) => {
12
+ try {
13
+ const fromTime = from_time ?? nowInToronto().time;
14
+ const toTime = to_time ?? addHoursToTime(fromTime, 4);
15
+ const dto = normalizeStopDestinations(await client.getStopDestinations(stop_code, hhmmToWire(fromTime), hhmmToWire(toTime)));
16
+ return {
17
+ content: [{ type: "text", text: JSON.stringify(dto) }],
18
+ structuredContent: dto,
19
+ };
20
+ }
21
+ catch (error) {
22
+ if (error instanceof MetrolinxError)
23
+ return toToolErrorResult(error);
24
+ throw error;
25
+ }
26
+ });
27
+ }
28
+ //# sourceMappingURL=get-stop-destinations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-stop-destinations.js","sourceRoot":"","sources":["../../src/tools/get-stop-destinations.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,UAAU,2BAA2B,CACzC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,4FAA4F;QAC9F,WAAW,EAAE,6BAA6B;QAC1C,YAAY,EAAE,2BAA2B;KAC1C,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,EAA2B,EAAE;QACnE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,SAAS,IAAI,YAAY,EAAE,CAAC,IAAI,CAAC;YAClD,MAAM,MAAM,GAAG,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACtD,MAAM,GAAG,GAAG,yBAAyB,CACnC,MAAM,MAAM,CAAC,mBAAmB,CAC9B,SAAS,EACT,UAAU,CAAC,QAAQ,CAAC,EACpB,UAAU,CAAC,MAAM,CAAC,CACnB,CACF,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeStopDetails } from "../normalize/stop-details.js";
3
+ import { getStopDetailsInputShape, stopDetailsOutputShape, } from "../schemas/stop-details.js";
4
+ export function registerGetStopDetails(server, client) {
5
+ server.registerTool("get_stop_details", {
6
+ title: "Get stop details",
7
+ description: "Details for one GO Transit stop or station: location, train/bus service, facilities, parking, accessibility.",
8
+ inputSchema: getStopDetailsInputShape,
9
+ outputSchema: stopDetailsOutputShape,
10
+ }, async ({ stop_code, lang }) => {
11
+ try {
12
+ const dto = normalizeStopDetails(await client.getStopDetails(stop_code), lang);
13
+ return {
14
+ content: [{ type: "text", text: JSON.stringify(dto) }],
15
+ structuredContent: dto,
16
+ };
17
+ }
18
+ catch (error) {
19
+ if (error instanceof MetrolinxError)
20
+ return toToolErrorResult(error);
21
+ throw error;
22
+ }
23
+ });
24
+ }
25
+ //# sourceMappingURL=get-stop-details.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-stop-details.js","sourceRoot":"","sources":["../../src/tools/get-stop-details.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,UAAU,sBAAsB,CACpC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,8GAA8G;QAChH,WAAW,EAAE,wBAAwB;QACrC,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAA2B,EAAE;QACrD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,oBAAoB,CAC9B,MAAM,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,EACtC,IAAI,CACL,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeTripStatus } from "../normalize/trip-status.js";
3
+ import { getTripStatusInputShape, tripStatusOutputShape, } from "../schemas/trip-status.js";
4
+ import { dateToWire, nowInToronto } from "../time.js";
5
+ export function registerGetTripStatus(server, client) {
6
+ server.registerTool("get_trip_status", {
7
+ title: "Get trip status",
8
+ description: "Live stop-by-stop status for a single GO Transit trip, including vehicle position when tracked.",
9
+ inputSchema: getTripStatusInputShape,
10
+ outputSchema: tripStatusOutputShape,
11
+ }, async ({ trip_number, date }) => {
12
+ try {
13
+ const effectiveDate = date ?? nowInToronto().date;
14
+ const [tripStatus, stopAll] = await Promise.all([
15
+ client.getTripStatus(dateToWire(effectiveDate), trip_number),
16
+ client.getStopAll(),
17
+ ]);
18
+ const dto = normalizeTripStatus(tripStatus, stopAll, effectiveDate);
19
+ return {
20
+ content: [{ type: "text", text: JSON.stringify(dto) }],
21
+ structuredContent: dto,
22
+ };
23
+ }
24
+ catch (error) {
25
+ if (error instanceof MetrolinxError)
26
+ return toToolErrorResult(error);
27
+ throw error;
28
+ }
29
+ });
30
+ }
31
+ //# sourceMappingURL=get-trip-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-trip-status.js","sourceRoot":"","sources":["../../src/tools/get-trip-status.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,iGAAiG;QACnG,WAAW,EAAE,uBAAuB;QACpC,YAAY,EAAE,qBAAqB;KACpC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAA2B,EAAE;QACvD,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,IAAI,IAAI,YAAY,EAAE,CAAC,IAAI,CAAC;YAClD,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC9C,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,WAAW,CAAC;gBAC5D,MAAM,CAAC,UAAU,EAAE;aACpB,CAAC,CAAC;YACH,MAAM,GAAG,GAAG,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YACpE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeTripUpdates } from "../normalize/trip-updates.js";
3
+ import { getTripUpdatesInputShape, tripUpdatesOutputShape, } from "../schemas/trip-updates.js";
4
+ export function registerGetTripUpdates(server, client) {
5
+ server.registerTool("get_trip_updates", {
6
+ title: "Get trip updates",
7
+ description: "What's off-plan right now. Unfiltered: only trips with a material delay or cancelled/skipped stops. With any filter: everything matching, including on-time confirmations.",
8
+ inputSchema: getTripUpdatesInputShape,
9
+ outputSchema: tripUpdatesOutputShape,
10
+ }, async ({ line_code, trip_number, stop_code, limit, }) => {
11
+ try {
12
+ const raw = await client.getTripUpdates();
13
+ const dto = normalizeTripUpdates(raw, { line_code, trip_number, stop_code }, limit ?? 20);
14
+ return {
15
+ content: [{ type: "text", text: JSON.stringify(dto) }],
16
+ structuredContent: dto,
17
+ };
18
+ }
19
+ catch (error) {
20
+ if (error instanceof MetrolinxError)
21
+ return toToolErrorResult(error);
22
+ throw error;
23
+ }
24
+ });
25
+ }
26
+ //# sourceMappingURL=get-trip-updates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-trip-updates.js","sourceRoot":"","sources":["../../src/tools/get-trip-updates.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,UAAU,sBAAsB,CACpC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,4KAA4K;QAC9K,WAAW,EAAE,wBAAwB;QACrC,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EACL,SAAS,EACT,WAAW,EACX,SAAS,EACT,KAAK,GACN,EAA2B,EAAE;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,oBAAoB,CAC9B,GAAG,EACH,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,EACrC,KAAK,IAAI,EAAE,CACZ,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeUnionDepartures } from "../normalize/union-departures.js";
3
+ import { getUnionDeparturesInputShape, unionDeparturesOutputShape, } from "../schemas/union-departures.js";
4
+ export function registerGetUnionDepartures(server, client) {
5
+ server.registerTool("get_union_departures", {
6
+ title: "Get Union departures",
7
+ description: "Live Union Station departure board for trains and buses.",
8
+ inputSchema: getUnionDeparturesInputShape,
9
+ outputSchema: unionDeparturesOutputShape,
10
+ }, async ({ mode }) => {
11
+ try {
12
+ const [departures, stopAll] = await Promise.all([
13
+ client.getUnionDepartures(),
14
+ client.getStopAll(),
15
+ ]);
16
+ const dto = normalizeUnionDepartures(departures, stopAll, mode ?? "any");
17
+ return {
18
+ content: [{ type: "text", text: JSON.stringify(dto) }],
19
+ structuredContent: dto,
20
+ };
21
+ }
22
+ catch (error) {
23
+ if (error instanceof MetrolinxError)
24
+ return toToolErrorResult(error);
25
+ throw error;
26
+ }
27
+ });
28
+ }
29
+ //# sourceMappingURL=get-union-departures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-union-departures.js","sourceRoot":"","sources":["../../src/tools/get-union-departures.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AAExC,MAAM,UAAU,0BAA0B,CACxC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE,0DAA0D;QACvE,WAAW,EAAE,4BAA4B;QACzC,YAAY,EAAE,0BAA0B;KACzC,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAA2B,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC9C,MAAM,CAAC,kBAAkB,EAAE;gBAC3B,MAAM,CAAC,UAAU,EAAE;aACpB,CAAC,CAAC;YACH,MAAM,GAAG,GAAG,wBAAwB,CAClC,UAAU,EACV,OAAO,EACP,IAAI,IAAI,KAAK,CACd,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeVehiclePositions } from "../normalize/vehicle-positions.js";
3
+ import { getVehiclePositionsInputShape, vehiclePositionsOutputShape, } from "../schemas/vehicle-positions.js";
4
+ export function registerGetVehiclePositions(server, client) {
5
+ server.registerTool("get_vehicle_positions", {
6
+ title: "Get vehicle positions",
7
+ description: "Live positions, delay, and occupancy for GO vehicles of one mode (train, bus, or UPX).",
8
+ inputSchema: getVehiclePositionsInputShape,
9
+ outputSchema: vehiclePositionsOutputShape,
10
+ }, async ({ mode, line_code, trip_number, limit, }) => {
11
+ try {
12
+ const [glance, positions, stopAll] = await Promise.all([
13
+ client.getServiceGlance(mode),
14
+ client.getVehiclePositions(),
15
+ client.getStopAll(),
16
+ ]);
17
+ const dto = normalizeVehiclePositions(glance, positions, stopAll, mode, { line_code, trip_number }, limit ?? 20);
18
+ return {
19
+ content: [{ type: "text", text: JSON.stringify(dto) }],
20
+ structuredContent: dto,
21
+ };
22
+ }
23
+ catch (error) {
24
+ if (error instanceof MetrolinxError)
25
+ return toToolErrorResult(error);
26
+ throw error;
27
+ }
28
+ });
29
+ }
30
+ //# sourceMappingURL=get-vehicle-positions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-vehicle-positions.js","sourceRoot":"","sources":["../../src/tools/get-vehicle-positions.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AAEzC,MAAM,UAAU,2BAA2B,CACzC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,wFAAwF;QAC1F,WAAW,EAAE,6BAA6B;QAC1C,YAAY,EAAE,2BAA2B;KAC1C,EACD,KAAK,EAAE,EACL,IAAI,EACJ,SAAS,EACT,WAAW,EACX,KAAK,GACN,EAA2B,EAAE;QAC5B,IAAI,CAAC;YACH,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACrD,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC;gBAC7B,MAAM,CAAC,mBAAmB,EAAE;gBAC5B,MAAM,CAAC,UAAU,EAAE;aACpB,CAAC,CAAC;YACH,MAAM,GAAG,GAAG,yBAAyB,CACnC,MAAM,EACN,SAAS,EACT,OAAO,EACP,IAAI,EACJ,EAAE,SAAS,EAAE,WAAW,EAAE,EAC1B,KAAK,IAAI,EAAE,CACZ,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeListLines } from "../normalize/list-lines.js";
3
+ import { listLinesInputShape, listLinesOutputShape, } from "../schemas/list-lines.js";
4
+ import { dateToWire, nowInToronto } from "../time.js";
5
+ export function registerListLines(server, client) {
6
+ server.registerTool("list_lines", {
7
+ title: "List lines",
8
+ description: "GO Transit's line roster for a service day, including valid direction codes for get_line_schedule.",
9
+ inputSchema: listLinesInputShape,
10
+ outputSchema: listLinesOutputShape,
11
+ }, async ({ date }) => {
12
+ try {
13
+ const dateWire = dateToWire(date ?? nowInToronto().date);
14
+ const dto = normalizeListLines(await client.getLineAll(dateWire));
15
+ return {
16
+ content: [{ type: "text", text: JSON.stringify(dto) }],
17
+ structuredContent: dto,
18
+ };
19
+ }
20
+ catch (error) {
21
+ if (error instanceof MetrolinxError)
22
+ return toToolErrorResult(error);
23
+ throw error;
24
+ }
25
+ });
26
+ }
27
+ //# sourceMappingURL=list-lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-lines.js","sourceRoot":"","sources":["../../src/tools/list-lines.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,UAAU,iBAAiB,CAC/B,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;QACE,KAAK,EAAE,YAAY;QACnB,WAAW,EACT,oGAAoG;QACtG,WAAW,EAAE,mBAAmB;QAChC,YAAY,EAAE,oBAAoB;KACnC,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAA2B,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,IAAI,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,kBAAkB,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { planItineraries } from "../normalize/journey.js";
3
+ import { buildStopNameIndex } from "../normalize/search-stops.js";
4
+ import { planJourneyInputShape, planJourneyOutputShape, } from "../schemas/journey.js";
5
+ import { nowInToronto } from "../time.js";
6
+ export function registerPlanJourney(server, client) {
7
+ server.registerTool("plan_journey", {
8
+ title: "Plan a journey (raw)",
9
+ description: "Fine-control trip planner for exact GO stop codes — no fuzzy name resolution, depart-after only. Use plan_trip for names or arrive-by search.",
10
+ inputSchema: planJourneyInputShape,
11
+ outputSchema: planJourneyOutputShape,
12
+ }, async ({ from_stop_code, to_stop_code, date, time, max_results, }) => {
13
+ try {
14
+ const stopAll = await client.getStopAll();
15
+ const stopNames = buildStopNameIndex(stopAll);
16
+ if (!stopNames.has(from_stop_code)) {
17
+ throw new MetrolinxError("not_found", `Unknown stop code "${from_stop_code}" for from_stop_code. Verify via search_stops.`, false);
18
+ }
19
+ if (!stopNames.has(to_stop_code)) {
20
+ throw new MetrolinxError("not_found", `Unknown stop code "${to_stop_code}" for to_stop_code. Verify via search_stops.`, false);
21
+ }
22
+ const itineraries = await planItineraries(client, {
23
+ from: from_stop_code,
24
+ to: to_stop_code,
25
+ date: date ?? nowInToronto().date,
26
+ time: time ?? nowInToronto().time,
27
+ timeMode: "depart_after",
28
+ maxResults: max_results ?? 3,
29
+ // Raw mirror of one Schedule/Journey call — no via-Union
30
+ // composition here (ADR 0002); that's plan_trip's value-add.
31
+ viaUnionFallback: false,
32
+ }, stopNames);
33
+ const dto = { itineraries };
34
+ return {
35
+ content: [{ type: "text", text: JSON.stringify(dto) }],
36
+ structuredContent: dto,
37
+ };
38
+ }
39
+ catch (error) {
40
+ if (error instanceof MetrolinxError)
41
+ return toToolErrorResult(error);
42
+ throw error;
43
+ }
44
+ });
45
+ }
46
+ //# sourceMappingURL=plan-journey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-journey.js","sourceRoot":"","sources":["../../src/tools/plan-journey.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,+IAA+I;QACjJ,WAAW,EAAE,qBAAqB;QAClC,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EACL,cAAc,EACd,YAAY,EACZ,IAAI,EACJ,IAAI,EACJ,WAAW,GACZ,EAA2B,EAAE;QAC5B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAE9C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,cAAc,CACtB,WAAW,EACX,sBAAsB,cAAc,gDAAgD,EACpF,KAAK,CACN,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,cAAc,CACtB,WAAW,EACX,sBAAsB,YAAY,8CAA8C,EAChF,KAAK,CACN,CAAC;YACJ,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,eAAe,CACvC,MAAM,EACN;gBACE,IAAI,EAAE,cAAc;gBACpB,EAAE,EAAE,YAAY;gBAChB,IAAI,EAAE,IAAI,IAAI,YAAY,EAAE,CAAC,IAAI;gBACjC,IAAI,EAAE,IAAI,IAAI,YAAY,EAAE,CAAC,IAAI;gBACjC,QAAQ,EAAE,cAAc;gBACxB,UAAU,EAAE,WAAW,IAAI,CAAC;gBAC5B,yDAAyD;gBACzD,6DAA6D;gBAC7D,gBAAgB,EAAE,KAAK;aACxB,EACD,SAAS,CACV,CAAC;YAEF,MAAM,GAAG,GAAsB,EAAE,WAAW,EAAE,CAAC;YAC/C,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,81 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { planItineraries } from "../normalize/journey.js";
3
+ import { buildStopNameIndex, resolveStopByName, } from "../normalize/search-stops.js";
4
+ import { planTripInputShape, planTripOutputShape } from "../schemas/journey.js";
5
+ import { nowInToronto } from "../time.js";
6
+ export function registerPlanTrip(server, client) {
7
+ server.registerTool("plan_trip", {
8
+ title: "Plan a trip",
9
+ description: "Plan a GO Transit trip between two stations/stops by name (fuzzy-resolved) or stop code, returning itineraries with legs, transfers, and accessibility. Cross-line trips are composed automatically via a Union Station transfer. Ambiguous names return candidates instead of an error.",
10
+ inputSchema: planTripInputShape,
11
+ outputSchema: planTripOutputShape,
12
+ }, async ({ from, to, date, time, time_mode, max_results, }) => {
13
+ try {
14
+ const stopAll = await client.getStopAll();
15
+ const fromResolution = resolveStopByName(stopAll, from);
16
+ if (fromResolution.status === "not_found") {
17
+ throw new MetrolinxError("not_found", `No stop matches "${from}". Verify the name via search_stops.`, false);
18
+ }
19
+ const toResolution = resolveStopByName(stopAll, to);
20
+ if (toResolution.status === "not_found") {
21
+ throw new MetrolinxError("not_found", `No stop matches "${to}". Verify the name via search_stops.`, false);
22
+ }
23
+ if (fromResolution.status === "ambiguous" ||
24
+ toResolution.status === "ambiguous") {
25
+ const ambiguities = [];
26
+ if (fromResolution.status === "ambiguous") {
27
+ ambiguities.push({
28
+ field: "from",
29
+ query: from,
30
+ candidates: fromResolution.candidates,
31
+ });
32
+ }
33
+ if (toResolution.status === "ambiguous") {
34
+ ambiguities.push({
35
+ field: "to",
36
+ query: to,
37
+ candidates: toResolution.candidates,
38
+ });
39
+ }
40
+ const dto = { status: "ambiguous", ambiguities };
41
+ return {
42
+ content: [{ type: "text", text: JSON.stringify(dto) }],
43
+ structuredContent: dto,
44
+ };
45
+ }
46
+ // Neither resolution is "not_found" (handled above) nor "ambiguous"
47
+ // (handled above, with a return) — both are "resolved" here.
48
+ const fromMatch = fromResolution.match;
49
+ const toMatch = toResolution.match;
50
+ const stopNames = buildStopNameIndex(stopAll);
51
+ const itineraries = await planItineraries(client, {
52
+ from: fromMatch.stop_code,
53
+ to: toMatch.stop_code,
54
+ date: date ?? nowInToronto().date,
55
+ time: time ?? nowInToronto().time,
56
+ timeMode: time_mode ?? "depart_after",
57
+ maxResults: max_results ?? 3,
58
+ viaUnionFallback: true,
59
+ }, stopNames);
60
+ const dto = {
61
+ status: "ok",
62
+ from: {
63
+ stop_code: fromMatch.stop_code,
64
+ stop_name: fromMatch.stop_name,
65
+ },
66
+ to: { stop_code: toMatch.stop_code, stop_name: toMatch.stop_name },
67
+ itineraries,
68
+ };
69
+ return {
70
+ content: [{ type: "text", text: JSON.stringify(dto) }],
71
+ structuredContent: dto,
72
+ };
73
+ }
74
+ catch (error) {
75
+ if (error instanceof MetrolinxError)
76
+ return toToolErrorResult(error);
77
+ throw error;
78
+ }
79
+ });
80
+ }
81
+ //# sourceMappingURL=plan-trip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-trip.js","sourceRoot":"","sources":["../../src/tools/plan-trip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { MetrolinxError, toToolErrorResult } from "../errors.js";
2
+ import { normalizeSearchStops } from "../normalize/search-stops.js";
3
+ import { searchStopsInputShape, searchStopsOutputShape, } from "../schemas/search-stops.js";
4
+ export function registerSearchStops(server, client) {
5
+ server.registerTool("search_stops", {
6
+ title: "Search stops",
7
+ description: "Fuzzy search for GO Transit stops and stations by name fragment (e.g. 'union', 'oakville').",
8
+ inputSchema: searchStopsInputShape,
9
+ outputSchema: searchStopsOutputShape,
10
+ }, async ({ query, stop_type, limit }) => {
11
+ try {
12
+ const dto = normalizeSearchStops(await client.getStopAll(), query, stop_type ?? "any", limit ?? 10);
13
+ return {
14
+ content: [{ type: "text", text: JSON.stringify(dto) }],
15
+ structuredContent: dto,
16
+ };
17
+ }
18
+ catch (error) {
19
+ if (error instanceof MetrolinxError)
20
+ return toToolErrorResult(error);
21
+ throw error;
22
+ }
23
+ });
24
+ }
25
+ //# sourceMappingURL=search-stops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-stops.js","sourceRoot":"","sources":["../../src/tools/search-stops.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,MAAuB;IAEvB,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,6FAA6F;QAC/F,WAAW,EAAE,qBAAqB;QAClC,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAA2B,EAAE;QAC7D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,oBAAoB,CAC9B,MAAM,MAAM,CAAC,UAAU,EAAE,EACzB,KAAK,EACL,SAAS,IAAI,KAAK,EAClB,KAAK,IAAI,EAAE,CACZ,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,cAAc;gBAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
+ import { buildServer } from "../server.js";
4
+ /** Shared MCP round-trip harness for tool tests (test-architecture spec §1). */
5
+ export async function callTool(fake, toolName, args) {
6
+ const server = buildServer(fake);
7
+ const client = new Client({ name: "test-client", version: "0.0.0" });
8
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
9
+ await Promise.all([
10
+ server.connect(serverTransport),
11
+ client.connect(clientTransport),
12
+ ]);
13
+ try {
14
+ const result = await client.callTool({ name: toolName, arguments: args });
15
+ const content = result.content;
16
+ const isError = result.isError === true;
17
+ return {
18
+ isError,
19
+ structuredContent: result.structuredContent,
20
+ errorPayload: isError
21
+ ? JSON.parse(content[0].text)
22
+ : undefined,
23
+ };
24
+ }
25
+ finally {
26
+ await client.close();
27
+ await server.close();
28
+ }
29
+ }
30
+ /** Reads an MCP resource over a real in-memory client/server pair. */
31
+ export async function readResource(fake, uri) {
32
+ const server = buildServer(fake);
33
+ const client = new Client({ name: "test-client", version: "0.0.0" });
34
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
35
+ await Promise.all([
36
+ server.connect(serverTransport),
37
+ client.connect(clientTransport),
38
+ ]);
39
+ try {
40
+ const result = await client.readResource({ uri });
41
+ return result.contents;
42
+ }
43
+ finally {
44
+ await client.close();
45
+ await server.close();
46
+ }
47
+ }
48
+ /** Renders an MCP prompt over a real in-memory client/server pair. */
49
+ export async function getPrompt(fake, name, args) {
50
+ const server = buildServer(fake);
51
+ const client = new Client({ name: "test-client", version: "0.0.0" });
52
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
53
+ await Promise.all([
54
+ server.connect(serverTransport),
55
+ client.connect(clientTransport),
56
+ ]);
57
+ try {
58
+ const result = await client.getPrompt({ name, arguments: args });
59
+ return result.messages;
60
+ }
61
+ finally {
62
+ await client.close();
63
+ await server.close();
64
+ }
65
+ }
66
+ /** A MetrolinxClient fake with every method stubbed to reject — override what a test needs. */
67
+ export function fakeClient(overrides) {
68
+ const unimplemented = () => Promise.reject(new Error("not stubbed for this test"));
69
+ return {
70
+ getStopDetails: unimplemented,
71
+ getStopAll: unimplemented,
72
+ getNextService: unimplemented,
73
+ getStopDestinations: unimplemented,
74
+ getFares: unimplemented,
75
+ getFleetConsistAll: unimplemented,
76
+ getFleetConsistByEngine: unimplemented,
77
+ getServiceAlerts: unimplemented,
78
+ getInformationAlerts: unimplemented,
79
+ getMarketingAlerts: unimplemented,
80
+ getUnionDepartures: unimplemented,
81
+ getServiceExceptions: unimplemented,
82
+ getServiceGuarantee: unimplemented,
83
+ getLineAll: unimplemented,
84
+ getLineSchedule: unimplemented,
85
+ getTripStatus: unimplemented,
86
+ getServiceGlance: unimplemented,
87
+ getVehiclePositions: unimplemented,
88
+ getTripUpdates: unimplemented,
89
+ getJourney: unimplemented,
90
+ ...overrides,
91
+ };
92
+ }
93
+ //# sourceMappingURL=test-support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-support.js","sourceRoot":"","sources":["../../src/tools/test-support.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "go-transit-mcp",
3
+ "version": "0.0.0",
4
+ "description": "MCP server for GO Transit (Metrolinx) trip planning: schedules, live departures, fares, and service alerts.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Emil Soleymani",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/EmilSoleymani/go-planning-mcp.git"
11
+ },
12
+ "engines": {
13
+ "node": ">=20"
14
+ },
15
+ "bin": {
16
+ "go-transit-mcp": "dist/entry/stdio.js"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "dev": "tsx watch src/entry/http.ts",
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "start:stdio": "node dist/entry/stdio.js",
25
+ "start:http": "node dist/entry/http.js",
26
+ "mcp-inspector": "npx @modelcontextprotocol/inspector",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "test:smoke": "vitest run test/smoke",
31
+ "lint": "eslint .",
32
+ "format": "prettier --write .",
33
+ "format:check": "prettier --check ."
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.29.0",
37
+ "mcp-handler": "^1.1.0",
38
+ "zod": "^4.4.3"
39
+ },
40
+ "devDependencies": {
41
+ "@eslint/js": "^10.0.1",
42
+ "@vitest/coverage-v8": "^4.1.10",
43
+ "eslint": "^10.7.0",
44
+ "eslint-config-prettier": "^10.1.8",
45
+ "msw": "^2.15.0",
46
+ "prettier": "^3.9.5",
47
+ "tsx": "^4.23.1",
48
+ "typescript": "^6.0.3",
49
+ "typescript-eslint": "^8.64.0",
50
+ "vitest": "^4.1.10"
51
+ },
52
+ "overrides": {
53
+ "mcp-handler": {
54
+ "@modelcontextprotocol/sdk": "$@modelcontextprotocol/sdk"
55
+ }
56
+ }
57
+ }