sbb-mcp 0.4.3 → 0.5.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 (83) hide show
  1. package/LICENSE +50 -57
  2. package/README.md +25 -214
  3. package/dist/index.js +47 -19
  4. package/package.json +10 -33
  5. package/dist/auth.d.ts +0 -2
  6. package/dist/auth.js +0 -44
  7. package/dist/auth.js.map +0 -1
  8. package/dist/cache.d.ts +0 -14
  9. package/dist/cache.js +0 -62
  10. package/dist/cache.js.map +0 -1
  11. package/dist/client.d.ts +0 -17
  12. package/dist/client.js +0 -70
  13. package/dist/client.js.map +0 -1
  14. package/dist/formatters.d.ts +0 -35
  15. package/dist/formatters.js +0 -285
  16. package/dist/formatters.js.map +0 -1
  17. package/dist/http.d.ts +0 -2
  18. package/dist/http.js +0 -117
  19. package/dist/http.js.map +0 -1
  20. package/dist/i18n.d.ts +0 -22
  21. package/dist/i18n.js +0 -36
  22. package/dist/i18n.js.map +0 -1
  23. package/dist/index.d.ts +0 -2
  24. package/dist/index.js.map +0 -1
  25. package/dist/journey.d.ts +0 -5
  26. package/dist/journey.js +0 -67
  27. package/dist/journey.js.map +0 -1
  28. package/dist/look2book.d.ts +0 -98
  29. package/dist/look2book.js +0 -212
  30. package/dist/look2book.js.map +0 -1
  31. package/dist/prices.d.ts +0 -3
  32. package/dist/prices.js +0 -51
  33. package/dist/prices.js.map +0 -1
  34. package/dist/profile.d.ts +0 -16
  35. package/dist/profile.js +0 -84
  36. package/dist/profile.js.map +0 -1
  37. package/dist/rate-limit.d.ts +0 -5
  38. package/dist/rate-limit.js +0 -44
  39. package/dist/rate-limit.js.map +0 -1
  40. package/dist/shortlink.d.ts +0 -60
  41. package/dist/shortlink.js +0 -122
  42. package/dist/shortlink.js.map +0 -1
  43. package/dist/structured.d.ts +0 -125
  44. package/dist/structured.js +0 -134
  45. package/dist/structured.js.map +0 -1
  46. package/dist/swisstrip.d.ts +0 -41
  47. package/dist/swisstrip.js +0 -135
  48. package/dist/swisstrip.js.map +0 -1
  49. package/dist/tools.d.ts +0 -40
  50. package/dist/tools.js +0 -509
  51. package/dist/tools.js.map +0 -1
  52. package/dist/transport/index.d.ts +0 -10
  53. package/dist/transport/index.js +0 -13
  54. package/dist/transport/index.js.map +0 -1
  55. package/dist/transport/setup.d.ts +0 -1
  56. package/dist/transport/setup.js +0 -59
  57. package/dist/transport/setup.js.map +0 -1
  58. package/dist/transport/smapi-auth.d.ts +0 -14
  59. package/dist/transport/smapi-auth.js +0 -89
  60. package/dist/transport/smapi-auth.js.map +0 -1
  61. package/dist/transport/smapi-client.d.ts +0 -46
  62. package/dist/transport/smapi-client.js +0 -186
  63. package/dist/transport/smapi-client.js.map +0 -1
  64. package/dist/transport/smapi-journey.d.ts +0 -29
  65. package/dist/transport/smapi-journey.js +0 -91
  66. package/dist/transport/smapi-journey.js.map +0 -1
  67. package/dist/transport/smapi-mock.d.ts +0 -9
  68. package/dist/transport/smapi-mock.js +0 -151
  69. package/dist/transport/smapi-mock.js.map +0 -1
  70. package/dist/transport/smapi-prices.d.ts +0 -48
  71. package/dist/transport/smapi-prices.js +0 -144
  72. package/dist/transport/smapi-prices.js.map +0 -1
  73. package/dist/transport/smapi-types.d.ts +0 -181
  74. package/dist/transport/smapi-types.js +0 -2
  75. package/dist/transport/smapi-types.js.map +0 -1
  76. package/dist/types.d.ts +0 -139
  77. package/dist/types.js +0 -3
  78. package/dist/types.js.map +0 -1
  79. package/dist/widgets.d.ts +0 -60
  80. package/dist/widgets.js +0 -184
  81. package/dist/widgets.js.map +0 -1
  82. package/web/dist/widgets.css +0 -1
  83. package/web/dist/widgets.js +0 -1
@@ -1,48 +0,0 @@
1
- import type { SmapiPriceResult, SmapiOfferResult, SmapiTraveler } from './smapi-types.js';
2
- /**
3
- * ## Endpoint inventory (observed against b2p-int.api.sbb.ch on 2026-04-23)
4
- *
5
- * GET /api/networks — known working ✓
6
- * GET /api/trip-offers?tripId=…&… — 400 "At least one of passengers
7
- * or dogs or bikes must be set"
8
- * (contract + auth ok; schema below)
9
- * POST /api/v2/prices?tripId=… — 400 "Failed to read request"
10
- * (contract + auth ok; need right
11
- * body shape)
12
- * /api/prices (v1) — 403 NOT FOUND at this gateway
13
- *
14
- * The legacy fallback to `/api/prices` for traveler-less lookups has been
15
- * removed — that endpoint doesn't exist in the current Ticketing API. Both
16
- * exported functions now always hit their v2 / correct paths.
17
- *
18
- * The exact query-param / request-body shapes for `/api/trip-offers` and
19
- * `/api/v2/prices` still need live-tripId verification once Journey API is
20
- * approved and we can fetch a real tripId to probe against. The payload
21
- * schemas in this file are inferred from SBB's RFC 9457 error messages and
22
- * may need refinement — tracked in docs/sbb-train-tickets-implementation.md
23
- * under "Known limits".
24
- */
25
- /**
26
- * Fetch price points for a single trip via `POST /api/v2/prices?tripId=…`.
27
- *
28
- * SBB's /api/prices (v1) endpoint was removed — the current Ticketing API
29
- * only has v2. Always requires passenger details (id, age, reduction).
30
- *
31
- * Returns [] on any error so one bad trip doesn't blow up a batch lookup.
32
- */
33
- export declare function getTripPrices(tripIds: string[], travelers?: SmapiTraveler[], acceptLanguage?: string): Promise<SmapiPriceResult[]>;
34
- /**
35
- * Fetch trip offers + affiliate deep link via `GET /api/trip-offers?tripId=…`.
36
- *
37
- * The legacy `POST /api/trip-offers` with JSON body is obsolete — the current
38
- * contract is GET with query parameters. The `passengers` param is required
39
- * (at least one of passengers / dogs / bikes).
40
- *
41
- * We send passengers as a URL-encoded JSON array because simple form encoding
42
- * drops structured fields (id/age/reduction). This mirrors how most b2p
43
- * partner integrations pass structured collections to their GET endpoints.
44
- *
45
- * Note: this call burns the Look2Book offers ratio — only invoke when the
46
- * user actually intends to see prices / buy.
47
- */
48
- export declare function getTripOffers(tripId: string, travelers: SmapiTraveler[], acceptLanguage?: string): Promise<SmapiOfferResult>;
@@ -1,144 +0,0 @@
1
- // AUTO-GENERATED from packages/sbb-transport/src/smapi-prices.ts
2
- // Run `npm run sync:transport` in packages/sbb-mcp to refresh.
3
- // Do not edit this file directly — edit the sbb-transport source instead.
4
- import { smapiRequest, getTicketingBaseUrl } from './smapi-client.js';
5
- /**
6
- * ## Endpoint inventory (observed against b2p-int.api.sbb.ch on 2026-04-23)
7
- *
8
- * GET /api/networks — known working ✓
9
- * GET /api/trip-offers?tripId=…&… — 400 "At least one of passengers
10
- * or dogs or bikes must be set"
11
- * (contract + auth ok; schema below)
12
- * POST /api/v2/prices?tripId=… — 400 "Failed to read request"
13
- * (contract + auth ok; need right
14
- * body shape)
15
- * /api/prices (v1) — 403 NOT FOUND at this gateway
16
- *
17
- * The legacy fallback to `/api/prices` for traveler-less lookups has been
18
- * removed — that endpoint doesn't exist in the current Ticketing API. Both
19
- * exported functions now always hit their v2 / correct paths.
20
- *
21
- * The exact query-param / request-body shapes for `/api/trip-offers` and
22
- * `/api/v2/prices` still need live-tripId verification once Journey API is
23
- * approved and we can fetch a real tripId to probe against. The payload
24
- * schemas in this file are inferred from SBB's RFC 9457 error messages and
25
- * may need refinement — tracked in docs/sbb-train-tickets-implementation.md
26
- * under "Known limits".
27
- */
28
- /**
29
- * Fetch price points for a single trip via `POST /api/v2/prices?tripId=…`.
30
- *
31
- * SBB's /api/prices (v1) endpoint was removed — the current Ticketing API
32
- * only has v2. Always requires passenger details (id, age, reduction).
33
- *
34
- * Returns [] on any error so one bad trip doesn't blow up a batch lookup.
35
- */
36
- export async function getTripPrices(tripIds, travelers, acceptLanguage) {
37
- const effectiveTravelers = travelers && travelers.length > 0
38
- ? travelers
39
- : [{ id: 'p0', type: 'ADULT' }];
40
- return Promise.all(tripIds.map(async (tripId) => {
41
- const params = new URLSearchParams({ tripId });
42
- try {
43
- const data = await smapiRequest(getTicketingBaseUrl(), `/api/v2/prices?${params}`, {
44
- method: 'POST',
45
- body: { passengers: effectiveTravelers.map(toPassengerPayload) },
46
- acceptLanguage,
47
- });
48
- const prices = data.prices ?? [];
49
- return {
50
- tripId,
51
- prices: prices.map((p) => ({
52
- amount: p.amount,
53
- currency: p.currency || 'CHF',
54
- class: (p.class || '2'),
55
- reductionCard: p.reductionCard,
56
- })),
57
- };
58
- }
59
- catch (err) {
60
- console.warn(`[smapi-prices] Failed to get price for trip ${tripId}:`, err);
61
- return { tripId, prices: [] };
62
- }
63
- }));
64
- }
65
- /**
66
- * Fetch trip offers + affiliate deep link via `GET /api/trip-offers?tripId=…`.
67
- *
68
- * The legacy `POST /api/trip-offers` with JSON body is obsolete — the current
69
- * contract is GET with query parameters. The `passengers` param is required
70
- * (at least one of passengers / dogs / bikes).
71
- *
72
- * We send passengers as a URL-encoded JSON array because simple form encoding
73
- * drops structured fields (id/age/reduction). This mirrors how most b2p
74
- * partner integrations pass structured collections to their GET endpoints.
75
- *
76
- * Note: this call burns the Look2Book offers ratio — only invoke when the
77
- * user actually intends to see prices / buy.
78
- */
79
- export async function getTripOffers(tripId, travelers, acceptLanguage) {
80
- const passengers = (travelers.length > 0 ? travelers : [{ id: 'p0', type: 'ADULT' }])
81
- .map(toPassengerPayload);
82
- const params = new URLSearchParams({
83
- tripId,
84
- passengers: JSON.stringify(passengers),
85
- });
86
- const data = await smapiRequest(getTicketingBaseUrl(), `/api/trip-offers?${params}`, { method: 'GET', acceptLanguage });
87
- // Extract affiliate deep link from HATEOAS `_links` (OSDM convention) or
88
- // top-level `links` (legacy shape) — we accept either to stay resilient
89
- // across spec revisions.
90
- const hateoas = ((data._links ?? data.links) ?? []);
91
- const affiliateLink = hateoas.find((l) => l.rel === 'online-offers')?.href;
92
- return {
93
- tripId,
94
- containers: [], // Full offer parsing not needed for deep-link flow
95
- affiliateDeepLink: affiliateLink,
96
- };
97
- }
98
- /**
99
- * Map our internal SmapiTraveler → SBB's b2p passenger payload shape.
100
- *
101
- * SBB's /api/trip-offers expects `{ id, age, reduction }` per passenger
102
- * (observed from the RFC 9457 error: "Passenger data has to contain id,
103
- * age and reduction."). Our SmapiTraveler uses {id, type, dateOfBirth,
104
- * reductionCard}; we translate.
105
- *
106
- * - age: derived from dateOfBirth when present; else default 30 for ADULT,
107
- * 10 for CHILD (b2p classifies by age bracket, not by type)
108
- * - reduction: mapped from reductionCard; NONE → 'NO_REDUCTION' by default
109
- */
110
- function toPassengerPayload(t) {
111
- return {
112
- id: t.id || 'p0',
113
- age: deriveAge(t),
114
- reduction: mapReduction(t.reductionCard),
115
- };
116
- }
117
- function deriveAge(t) {
118
- if (t.dateOfBirth) {
119
- const dob = new Date(t.dateOfBirth);
120
- if (!Number.isNaN(dob.getTime())) {
121
- const now = new Date();
122
- let age = now.getFullYear() - dob.getFullYear();
123
- const m = now.getMonth() - dob.getMonth();
124
- if (m < 0 || (m === 0 && now.getDate() < dob.getDate()))
125
- age--;
126
- return age;
127
- }
128
- }
129
- return t.type === 'CHILD' ? 10 : 30;
130
- }
131
- function mapReduction(card) {
132
- switch (card) {
133
- case 'HALF_FARE':
134
- return 'HALFFARE';
135
- case 'GA':
136
- return 'GA';
137
- case 'NONE':
138
- case undefined:
139
- return 'NO_REDUCTION';
140
- default:
141
- return 'NO_REDUCTION';
142
- }
143
- }
144
- //# sourceMappingURL=smapi-prices.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-prices.js","sourceRoot":"","sources":["../../src/transport/smapi-prices.ts"],"names":[],"mappings":"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"}
@@ -1,181 +0,0 @@
1
- export type SmapiPlaceType = 'StopPlace' | 'Address' | 'PointOfInterest';
2
- export type SmapiGeoPosition = {
3
- longitude: number;
4
- latitude: number;
5
- };
6
- export type SmapiPlaceRef = {
7
- objectType: 'StopPlaceRef' | 'AddressRef' | 'PointOfInterestRef';
8
- stopPlaceRef?: string;
9
- };
10
- export type SmapiPlace = {
11
- objectType: SmapiPlaceType;
12
- id: string;
13
- name: string;
14
- geoPosition?: SmapiGeoPosition;
15
- ref?: SmapiPlaceRef;
16
- };
17
- export type SmapiTripStatus = 'PLANNED' | 'CONFIRMED' | 'CHANGED' | 'CANCELLED';
18
- export type SmapiTimedLeg = {
19
- type: 'timed';
20
- board: {
21
- stopPlace: SmapiPlace;
22
- departureTime: string;
23
- platform?: string;
24
- };
25
- alight: {
26
- stopPlace: SmapiPlace;
27
- arrivalTime: string;
28
- platform?: string;
29
- };
30
- service: {
31
- publishedLineName?: string;
32
- operatorName?: string;
33
- };
34
- duration: string;
35
- intermediateStops?: Array<{
36
- stopPlace: SmapiPlace;
37
- arrivalTime?: string;
38
- departureTime?: string;
39
- }>;
40
- occupancy?: {
41
- firstClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
42
- secondClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
43
- };
44
- };
45
- export type SmapiTransferLeg = {
46
- type: 'transfer';
47
- start: SmapiPlaceRef;
48
- end: SmapiPlaceRef;
49
- duration: string;
50
- transferMode: string;
51
- };
52
- export type SmapiContinuousLeg = {
53
- type: 'continuous';
54
- service: {
55
- providerName?: string;
56
- };
57
- duration: string;
58
- };
59
- export type SmapiTripLeg = SmapiTimedLeg | SmapiTransferLeg | SmapiContinuousLeg;
60
- export type SmapiTrip = {
61
- id: string;
62
- origin: SmapiPlace;
63
- destination: SmapiPlace;
64
- startTime: string;
65
- endTime: string;
66
- duration: string;
67
- transfers: number;
68
- legs: SmapiTripLeg[];
69
- tripStatus: SmapiTripStatus;
70
- };
71
- export type SmapiTripSummary = {
72
- id: string;
73
- origin: SmapiPlace;
74
- destination: SmapiPlace;
75
- startTime: string;
76
- endTime: string;
77
- duration: string;
78
- transfers: number;
79
- };
80
- export type SmapiTripsCollection = {
81
- id: string;
82
- trips: SmapiTrip[];
83
- tripSummaries: SmapiTripSummary[];
84
- links?: {
85
- next?: string;
86
- previous?: string;
87
- };
88
- warnings?: string[];
89
- };
90
- export type SmapiTransportMode = 'HIGH_SPEED_TRAIN' | 'HISTORIC_TRAIN' | 'INTERCITY' | 'REGIONAL' | 'INTERREGIONAL' | 'URBAN' | 'TRAIN' | 'TRAM' | 'UNDERGROUND' | 'NIGHT_TRAIN' | 'SHARED_TAXI' | 'MOTOR_RAIL' | 'MOUNTAIN_TRAIN' | 'PLANE' | 'COACH_GROUP' | 'SHIP' | 'BUS';
91
- export type SmapiTripSearchParams = {
92
- origin: string;
93
- destination: string;
94
- departureTime?: string;
95
- arrivalTime?: string;
96
- transferLimit?: number;
97
- vias?: Array<{
98
- stopPlaceRef: string;
99
- dwellTime?: string;
100
- }>;
101
- ptModeFilter?: {
102
- exclude: boolean;
103
- transportModes: SmapiTransportMode[];
104
- };
105
- };
106
- export type SmapiPlaceSearchParams = {
107
- name: string;
108
- type?: 'STOP' | 'ADDRESS' | 'POI';
109
- numberOfResults?: number;
110
- };
111
- export type SmapiReductionCard = 'HALF_FARE' | 'GA' | 'NONE';
112
- export type SmapiTraveler = {
113
- id: string;
114
- type: 'ADULT' | 'CHILD';
115
- dateOfBirth?: string;
116
- reductionCard?: SmapiReductionCard;
117
- firstName?: string;
118
- lastName?: string;
119
- };
120
- export type SmapiPrice = {
121
- amount: number;
122
- currency: string;
123
- class: '1' | '2';
124
- reductionCard?: SmapiReductionCard;
125
- };
126
- export type SmapiPriceResult = {
127
- tripId: string;
128
- prices: SmapiPrice[];
129
- };
130
- export type SmapiOfferCategory = 'TRANSPORT' | 'RESERVATION' | 'ANCILLARY';
131
- export type SmapiOffer = {
132
- offerId: string;
133
- category: SmapiOfferCategory;
134
- price: SmapiPrice;
135
- validUntil: string;
136
- affiliateLink?: string;
137
- };
138
- export type SmapiOfferContainer = {
139
- offers: SmapiOffer[];
140
- totalPrice: SmapiPrice;
141
- };
142
- export type SmapiOfferResult = {
143
- tripId: string;
144
- containers: SmapiOfferContainer[];
145
- affiliateDeepLink?: string;
146
- };
147
- /**
148
- * Error type per RFC 9457 (Problem Details for HTTP APIs), which obsoletes
149
- * RFC 7807 with the `code` and `pointers[]` extensions. SBB's cookbook still
150
- * cites 7807 but the Journey OpenAPI v1.3.17 produces 9457 — the formats are
151
- * compatible: 9457 just adds optional fields. Parse leniently and surface
152
- * pointers in logs to make support tickets actionable.
153
- */
154
- export type SmapiProblemPointer = {
155
- code?: string;
156
- type?: string;
157
- title?: string;
158
- status?: number;
159
- detail?: string;
160
- instance?: string;
161
- /** JSON pointer into the request body for the problematic attribute. */
162
- requestPointer?: string;
163
- /** JSON pointer into the response body for the problematic attribute. */
164
- responsePointer?: string;
165
- /** Identifier of the originating server, useful when SBB chains backends. */
166
- originator?: string;
167
- };
168
- export type SmapiProblem = {
169
- /** RFC 9457 short error code; optional in 3.1, mandatory in upcoming. */
170
- code?: string;
171
- type: string;
172
- title?: string;
173
- status?: number;
174
- detail?: string;
175
- /** Absolute URI identifying this specific occurrence (RFC 9457). */
176
- instance?: string;
177
- /** Per-attribute problem pointers (RFC 9457). */
178
- pointers?: SmapiProblemPointer[];
179
- /** SBB extension: end-user-translatable messages (Accept-Language honored). */
180
- displayMessages?: string[];
181
- };
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=smapi-types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-types.js","sourceRoot":"","sources":["../../src/transport/smapi-types.ts"],"names":[],"mappings":""}
package/dist/types.d.ts DELETED
@@ -1,139 +0,0 @@
1
- export type PlaceType = 'StopPlace' | 'Address' | 'PointOfInterest';
2
- export type GeoPosition = {
3
- longitude: number;
4
- latitude: number;
5
- };
6
- export type PlaceRef = {
7
- objectType: 'StopPlaceRef' | 'AddressRef' | 'PointOfInterestRef';
8
- stopPlaceRef?: string;
9
- };
10
- export type Place = {
11
- objectType: PlaceType;
12
- id: string;
13
- name: string;
14
- geoPosition?: GeoPosition;
15
- ref?: PlaceRef;
16
- };
17
- export type TripStatus = 'PLANNED' | 'CONFIRMED' | 'CHANGED' | 'CANCELLED';
18
- export type TimedLeg = {
19
- type: 'timed';
20
- board: {
21
- stopPlace: Place;
22
- departureTime: string;
23
- platform?: string;
24
- };
25
- alight: {
26
- stopPlace: Place;
27
- arrivalTime: string;
28
- platform?: string;
29
- };
30
- service: {
31
- publishedLineName?: string;
32
- operatorName?: string;
33
- };
34
- duration: string;
35
- intermediateStops?: Array<{
36
- stopPlace: Place;
37
- arrivalTime?: string;
38
- departureTime?: string;
39
- }>;
40
- occupancy?: {
41
- firstClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
42
- secondClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
43
- };
44
- };
45
- export type TransferLeg = {
46
- type: 'transfer';
47
- start: PlaceRef;
48
- end: PlaceRef;
49
- duration: string;
50
- transferMode: string;
51
- };
52
- export type ContinuousLeg = {
53
- type: 'continuous';
54
- service: {
55
- providerName?: string;
56
- };
57
- duration: string;
58
- };
59
- export type TripLeg = TimedLeg | TransferLeg | ContinuousLeg;
60
- export type Trip = {
61
- id: string;
62
- origin: Place;
63
- destination: Place;
64
- startTime: string;
65
- endTime: string;
66
- duration: string;
67
- transfers: number;
68
- legs: TripLeg[];
69
- tripStatus: TripStatus;
70
- };
71
- export type TripsCollection = {
72
- id: string;
73
- trips: Trip[];
74
- tripSummaries: Array<{
75
- id: string;
76
- origin: Place;
77
- destination: Place;
78
- startTime: string;
79
- endTime: string;
80
- duration: string;
81
- transfers: number;
82
- }>;
83
- links?: {
84
- next?: string;
85
- previous?: string;
86
- };
87
- warnings?: string[];
88
- };
89
- export type TransportMode = 'HIGH_SPEED_TRAIN' | 'HISTORIC_TRAIN' | 'INTERCITY' | 'REGIONAL' | 'INTERREGIONAL' | 'URBAN' | 'TRAIN' | 'TRAM' | 'UNDERGROUND' | 'NIGHT_TRAIN' | 'SHARED_TAXI' | 'MOTOR_RAIL' | 'MOUNTAIN_TRAIN' | 'PLANE' | 'COACH_GROUP' | 'SHIP' | 'BUS';
90
- export type TripSearchParams = {
91
- origin: string;
92
- destination: string;
93
- departureTime?: string;
94
- arrivalTime?: string;
95
- transferLimit?: number;
96
- vias?: Array<{
97
- stopPlaceRef: string;
98
- dwellTime?: string;
99
- }>;
100
- ptModeFilter?: {
101
- exclude: boolean;
102
- transportModes: TransportMode[];
103
- };
104
- };
105
- export type PlaceSearchParams = {
106
- name: string;
107
- type?: 'STOP' | 'ADDRESS' | 'POI';
108
- numberOfResults?: number;
109
- };
110
- export type ReductionCard = 'HALF_FARE' | 'GA' | 'NONE';
111
- export type Traveler = {
112
- id: string;
113
- type: 'ADULT' | 'CHILD';
114
- dateOfBirth?: string;
115
- reductionCard?: ReductionCard;
116
- firstName?: string;
117
- lastName?: string;
118
- };
119
- export type Price = {
120
- amount: number;
121
- currency: string;
122
- class: '1' | '2';
123
- reductionCard?: ReductionCard;
124
- };
125
- export type PriceResult = {
126
- tripId: string;
127
- prices: Price[];
128
- };
129
- export type OfferResult = {
130
- tripId: string;
131
- affiliateDeepLink?: string;
132
- };
133
- export type SmapiProblem = {
134
- type: string;
135
- title?: string;
136
- status?: number;
137
- detail?: string;
138
- displayMessages?: string[];
139
- };
package/dist/types.js DELETED
@@ -1,3 +0,0 @@
1
- // SBB SMAPI types — standalone, no external dependencies
2
- export {};
3
- //# sourceMappingURL=types.js.map
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,yDAAyD"}
package/dist/widgets.d.ts DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Registers ChatGPT Apps SDK widget resources on an McpServer.
3
- *
4
- * Each tool that returns structured data links to a widget via
5
- * `_meta["openai/outputTemplate"] = "ui://widget/<name>.html"`.
6
- *
7
- * The HTML shell for every widget embeds the same JS bundle (dist/widgets.js)
8
- * produced by `packages/sbb-mcp/web`. Routing to the right widget component
9
- * happens in the bundle via `data-widget` on the root element.
10
- */
11
- import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
- /** Registered widget IDs (mirrored in web/src/main.tsx). */
13
- export declare const WIDGETS: {
14
- readonly STATIONS_LIST: "stations-list";
15
- readonly CONNECTION_LIST: "connection-list";
16
- readonly TRIP_DETAILS: "trip-details";
17
- readonly PRICES_TABLE: "prices-table";
18
- readonly TICKET_CARD: "ticket-card";
19
- };
20
- export type WidgetId = (typeof WIDGETS)[keyof typeof WIDGETS];
21
- export declare function widgetUri(id: WidgetId): string;
22
- /**
23
- * True when the connected MCP client renders our widget iframe (currently
24
- * ChatGPT via the Apps SDK). Used by tool handlers to decide between a full
25
- * markdown response (non-widget clients need it to display anything useful)
26
- * and a terse one-line summary (widget clients already show the rich UI, and
27
- * the detailed prose just gets paraphrased by the LLM into a duplicate
28
- * narration next to the iframe — see https://…/issues/connections-shown-twice).
29
- *
30
- * Detection is based on `clientInfo.name` from the MCP `initialize` handshake.
31
- * ChatGPT self-identifies as "ChatGPT" / "ChatGPT-Atlas" / similar — we match
32
- * case-insensitively on "chatgpt" or "openai" so future naming tweaks from
33
- * OpenAI don't silently revert us to the noisy path.
34
- *
35
- * Falls back to `false` (full markdown) when no clientInfo has been received
36
- * yet or when the name doesn't match — the safe default for unknown clients
37
- * that might not render widgets.
38
- */
39
- export declare function isWidgetRenderingClient(server: McpServer): boolean;
40
- /**
41
- * Descriptor `_meta` (on the tool and resource registrations).
42
- * This is what tells ChatGPT to render a UI widget for the tool.
43
- */
44
- export declare function widgetToolMeta(id: WidgetId, invoking: string, invoked: string): Record<string, unknown>;
45
- /**
46
- * Invocation `_meta` (on each CallTool response).
47
- *
48
- * IMPORTANT: matches OpenAI's Pizzaz reference server exactly — ONLY the
49
- * invoking/invoked strings. Historically we also set `openai/outputTemplate`
50
- * and `openai/widgetAccessible` here, but those belong on the tool descriptor
51
- * + resource registration (see `widgetToolMeta`). Repeating them on the
52
- * response appears to confuse ChatGPT's widget resolver: the iframe mounts
53
- * but `window.openai.toolOutput` never populates — leaving every widget
54
- * stuck on the "Loading…" fallback.
55
- *
56
- * Ref: https://github.com/openai/openai-apps-sdk-examples/blob/main/pizzaz_server_node/src/server.ts
57
- */
58
- export declare function widgetResponseMeta(_id: WidgetId, invoking: string, invoked: string): Record<string, unknown>;
59
- /** Registers all widget resources on the given MCP server. Call once per server instance. */
60
- export declare function registerWidgets(server: McpServer): void;