imprint-mcp 0.4.10 → 0.4.11
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.
|
@@ -19,8 +19,10 @@ function buildLeg(leg: any): any[] {
|
|
|
19
19
|
out[1] = [[[leg?.dest, 0]]];
|
|
20
20
|
out[2] = leg?.times ?? null;
|
|
21
21
|
out[3] = leg?.stops ?? 0;
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// Google uses slot 4 for included alliances and carrier codes. Slot 5 is an
|
|
23
|
+
// exclusion list; putting carrier codes there inverts the filter.
|
|
24
|
+
out[4] = leg?.includeAirlines ?? leg?.alliances ?? null;
|
|
25
|
+
out[5] = leg?.excludeAirlines ?? null;
|
|
24
26
|
out[6] = leg?.date ?? null;
|
|
25
27
|
out[7] = leg?.duration ?? null;
|
|
26
28
|
out[8] = Array.isArray(leg?.selected)
|
|
@@ -19,7 +19,13 @@ interface Itinerary {
|
|
|
19
19
|
flight_token: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
interface AirlineFilter {
|
|
23
|
+
code: string;
|
|
24
|
+
name: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
const AIRPORT = /^[A-Z]{3}$/;
|
|
28
|
+
const ALLIANCE_CODES = new Set(['ONEWORLD', 'SKYTEAM', 'STAR_ALLIANCE']);
|
|
23
29
|
|
|
24
30
|
// A leg is [carrierCode, [carrierNames], [segments], originIATA, [departDate],
|
|
25
31
|
// [departTime], destIATA, [arriveDate], [arriveTime], durationMinutes, ...].
|
|
@@ -79,6 +85,38 @@ function walk(node: unknown, found: unknown[][]): void {
|
|
|
79
85
|
for (const child of node) walk(child, found);
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
function isPairList(node: unknown): node is string[][] {
|
|
89
|
+
return (
|
|
90
|
+
Array.isArray(node) &&
|
|
91
|
+
node.length > 0 &&
|
|
92
|
+
node.every(
|
|
93
|
+
(item) =>
|
|
94
|
+
Array.isArray(item) && typeof item[0] === 'string' && typeof item[1] === 'string',
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function toFilters(pairs: string[][]): AirlineFilter[] {
|
|
100
|
+
return pairs.map((pair) => ({ code: pair[0] as string, name: pair[1] as string }));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function collectAirlineFilters(
|
|
104
|
+
node: unknown,
|
|
105
|
+
found: { alliances: AirlineFilter[]; carriers: AirlineFilter[] },
|
|
106
|
+
): void {
|
|
107
|
+
if (!Array.isArray(node)) return;
|
|
108
|
+
if (
|
|
109
|
+
node.length >= 2 &&
|
|
110
|
+
isPairList(node[0]) &&
|
|
111
|
+
isPairList(node[1]) &&
|
|
112
|
+
node[0].some((pair) => ALLIANCE_CODES.has(pair[0] as string))
|
|
113
|
+
) {
|
|
114
|
+
found.alliances = toFilters(node[0]);
|
|
115
|
+
found.carriers = toFilters(node[1]);
|
|
116
|
+
}
|
|
117
|
+
for (const child of node) collectAirlineFilters(child, found);
|
|
118
|
+
}
|
|
119
|
+
|
|
82
120
|
function normalize(it: unknown[]): Itinerary {
|
|
83
121
|
const legs = legsOf(it);
|
|
84
122
|
const priceTok = it[1] as unknown[];
|
|
@@ -158,6 +196,11 @@ export function extract(
|
|
|
158
196
|
|
|
159
197
|
const found: unknown[][] = [];
|
|
160
198
|
if (payload != null) walk(payload, found);
|
|
199
|
+
const availableAirlineFilters = {
|
|
200
|
+
alliances: [] as AirlineFilter[],
|
|
201
|
+
carriers: [] as AirlineFilter[],
|
|
202
|
+
};
|
|
203
|
+
if (payload != null) collectAirlineFilters(payload, availableAirlineFilters);
|
|
161
204
|
|
|
162
205
|
const byToken = new Map<string, Itinerary>();
|
|
163
206
|
for (const it of found) {
|
|
@@ -175,5 +218,11 @@ export function extract(
|
|
|
175
218
|
return {
|
|
176
219
|
count: itineraries.length,
|
|
177
220
|
itineraries,
|
|
221
|
+
resultScope: {
|
|
222
|
+
exhaustive: false,
|
|
223
|
+
note:
|
|
224
|
+
'Google Flights GetShoppingResults returns a limited sorted subset. A carrier can be available in availableAirlineFilters without appearing in itineraries; call search_flights again with airlines=<code> to fetch that carrier.',
|
|
225
|
+
},
|
|
226
|
+
availableAirlineFilters,
|
|
178
227
|
};
|
|
179
228
|
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
// Adapter around the shared FlightsFrontendService body builder.
|
|
2
2
|
// The tool exposes flat snake_case params (origin, destination, departure_date,
|
|
3
3
|
// max_stops, …); the shared encoder consumes a structured camelCase shape
|
|
4
|
-
// ({ tripType, legs:[{origin,dest,date,times,stops,
|
|
4
|
+
// ({ tripType, legs:[{origin,dest,date,times,stops,includeAirlines,duration}],
|
|
5
5
|
// maxPrice, bags }). We map between them here and delegate the byte-for-byte
|
|
6
6
|
// positional encoding to the shared module (required reuse).
|
|
7
7
|
import { transform as sharedTransform } from '../_shared/flights_request.ts';
|
|
8
8
|
|
|
9
9
|
type Params = Record<string, string | number | boolean | undefined | null>;
|
|
10
10
|
|
|
11
|
-
const ALLIANCES = new Set(['ONEWORLD', 'SKYTEAM', 'STAR_ALLIANCE']);
|
|
12
|
-
|
|
13
11
|
function mapTripType(v: unknown): number {
|
|
14
12
|
if (v == null || v === '') return 1;
|
|
15
13
|
if (typeof v === 'number') return v;
|
|
@@ -42,18 +40,14 @@ function parseTimes(v: unknown): number[] | null {
|
|
|
42
40
|
return [Number(m[1]), Number(m[2]), 0, 23];
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
function parseAirlines(v: unknown):
|
|
46
|
-
if (v == null || v === '') return
|
|
47
|
-
const
|
|
43
|
+
function parseAirlines(v: unknown): string[] | null {
|
|
44
|
+
if (v == null || v === '') return null;
|
|
45
|
+
const includeAirlines = String(v)
|
|
48
46
|
.split(',')
|
|
49
47
|
.map((x) => x.trim())
|
|
50
|
-
.filter(Boolean)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
alliances: alliances.length ? alliances : null,
|
|
55
|
-
carriers: carriers.length ? carriers : null,
|
|
56
|
-
};
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.map((p) => p.toUpperCase());
|
|
50
|
+
return includeAirlines.length ? includeAirlines : null;
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
function num(v: unknown): number | undefined {
|
|
@@ -73,7 +67,7 @@ export function transform(
|
|
|
73
67
|
const hasReturnDate = p.return_date != null && String(p.return_date).trim() !== '';
|
|
74
68
|
const tripType = requestedTripType === 1 && !hasReturnDate ? 2 : requestedTripType;
|
|
75
69
|
const stops = p.max_stops != null && p.max_stops !== '' ? mapStops(p.max_stops) : 0;
|
|
76
|
-
const
|
|
70
|
+
const includeAirlines = parseAirlines(p.airlines);
|
|
77
71
|
const maxDur = num(p.max_duration);
|
|
78
72
|
const duration = maxDur != null ? [maxDur] : null;
|
|
79
73
|
|
|
@@ -87,8 +81,7 @@ export function transform(
|
|
|
87
81
|
date: p.departure_date ? String(p.departure_date) : null,
|
|
88
82
|
times: parseTimes(p.outbound_times),
|
|
89
83
|
stops,
|
|
90
|
-
|
|
91
|
-
carriers,
|
|
84
|
+
includeAirlines,
|
|
92
85
|
duration,
|
|
93
86
|
},
|
|
94
87
|
];
|
|
@@ -101,8 +94,7 @@ export function transform(
|
|
|
101
94
|
date: String(p.return_date),
|
|
102
95
|
times: parseTimes(p.return_times),
|
|
103
96
|
stops,
|
|
104
|
-
|
|
105
|
-
carriers,
|
|
97
|
+
includeAirlines,
|
|
106
98
|
duration,
|
|
107
99
|
});
|
|
108
100
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imprint-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "Teach an AI agent how to use any website. Once. Records a real browser session + narration; generates a deterministic MCP tool plus a DOM-replay playbook fallback.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|