@mapbox/mcp-server 0.12.7-dev → 0.12.7
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.
- package/dist/commonjs/resources/ui-apps/directionsAppHtml.d.ts.map +1 -1
- package/dist/commonjs/resources/ui-apps/directionsAppHtml.js +76 -15
- package/dist/commonjs/resources/ui-apps/directionsAppHtml.js.map +1 -1
- package/dist/commonjs/version.json +3 -3
- package/dist/esm/resources/ui-apps/directionsAppHtml.d.ts.map +1 -1
- package/dist/esm/resources/ui-apps/directionsAppHtml.js +76 -15
- package/dist/esm/resources/ui-apps/directionsAppHtml.js.map +1 -1
- package/dist/esm/version.json +3 -3
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directionsAppHtml.d.ts","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,0BAA0B,CAAC;CACpC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"directionsAppHtml.d.ts","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,0BAA0B,CAAC;CACpC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC,GAAG,MAAM,CAmmBT"}
|
|
@@ -86,8 +86,15 @@ ${initialDataScript}
|
|
|
86
86
|
// Track markers across re-renders so we can remove them before drawing the
|
|
87
87
|
// next route — otherwise a second tool-result delivery stacks pins.
|
|
88
88
|
var routeMarkers = [];
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// One render "cycle" per tool call delivered to this iframe. A new
|
|
90
|
+
// tool-input (or baked initial-data) delivery starts a new cycle by
|
|
91
|
+
// bumping \`generation\`; async callbacks that captured an older value are
|
|
92
|
+
// stale and must not touch the UI. This dedupes self-fetch vs tool-result
|
|
93
|
+
// within one call WITHOUT permanently blocking later calls that reuse
|
|
94
|
+
// this same iframe instance.
|
|
95
|
+
var generation = 0;
|
|
96
|
+
var renderedGeneration = -1; // generation whose route is on screen (-1 = none)
|
|
97
|
+
var selfFetchGeneration = -1; // generation that started a self-fetch (-1 = none)
|
|
91
98
|
|
|
92
99
|
// -------------------------------------------------------------------------
|
|
93
100
|
// Self-fetch: build a Directions API request directly from the tool call's
|
|
@@ -160,9 +167,42 @@ ${initialDataScript}
|
|
|
160
167
|
// Exposed so the parity test (Node vm sandbox) can call this in isolation.
|
|
161
168
|
window.__buildDirectionsApiUrl = buildDirectionsApiUrl;
|
|
162
169
|
|
|
170
|
+
// Params arrive via postMessage and are untrusted: they get interpolated
|
|
171
|
+
// into the request URL, so validate the pieces our encoding does not
|
|
172
|
+
// neutralize. routing_profile is spliced into the URL path unencoded, and
|
|
173
|
+
// encodeExcludeClient only escapes ',', '(', ')' and spaces — characters
|
|
174
|
+
// like '&' or '../' path segments would otherwise let a hostile sibling
|
|
175
|
+
// frame reshape the request. Server-side input is zod-validated before it
|
|
176
|
+
// reaches buildDirectionsRequestUrl; this mirrors those guarantees for
|
|
177
|
+
// the client copy.
|
|
178
|
+
function isSafeDirectionsParams(params) {
|
|
179
|
+
if (!params || !Array.isArray(params.coordinates)) return false;
|
|
180
|
+
if (params.coordinates.length < 2) return false;
|
|
181
|
+
for (var i = 0; i < params.coordinates.length; i++) {
|
|
182
|
+
var c = params.coordinates[i];
|
|
183
|
+
if (!c || typeof c.longitude !== 'number' || !isFinite(c.longitude)) return false;
|
|
184
|
+
if (typeof c.latitude !== 'number' || !isFinite(c.latitude)) return false;
|
|
185
|
+
}
|
|
186
|
+
if (params.routing_profile !== undefined && params.routing_profile !== null) {
|
|
187
|
+
if (typeof params.routing_profile !== 'string') return false;
|
|
188
|
+
if (!/^mapbox\\/[a-z-]+$/.test(params.routing_profile)) return false;
|
|
189
|
+
}
|
|
190
|
+
if (params.exclude !== undefined && params.exclude !== null) {
|
|
191
|
+
if (typeof params.exclude !== 'string') return false;
|
|
192
|
+
if (!/^[A-Za-z0-9_,.() -]+$/.test(params.exclude)) return false;
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
163
197
|
function fetchRouteFromDirectionsApi(params) {
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
// Every delivery (tool-input notification or baked initial-data) starts
|
|
199
|
+
// a new render cycle, even when the guard below prevents the fetch from
|
|
200
|
+
// actually running — the new call must still invalidate the previous
|
|
201
|
+
// cycle's in-flight callbacks and error-suppression state.
|
|
202
|
+
generation++;
|
|
203
|
+
if (!TOKEN || !isSafeDirectionsParams(params)) return;
|
|
204
|
+
var gen = generation; // captured by the callbacks below
|
|
205
|
+
selfFetchGeneration = gen; // marked only when the fetch actually proceeds
|
|
166
206
|
var url = buildDirectionsApiUrl(params, TOKEN, API_ENDPOINT);
|
|
167
207
|
fetch(url)
|
|
168
208
|
.then(function(res) {
|
|
@@ -181,7 +221,8 @@ ${initialDataScript}
|
|
|
181
221
|
return res.json();
|
|
182
222
|
})
|
|
183
223
|
.then(function(data) {
|
|
184
|
-
if (
|
|
224
|
+
if (gen !== generation) return; // stale: a newer call started
|
|
225
|
+
if (renderedGeneration === gen) return; // cycle already drawn (tool-result won)
|
|
185
226
|
var route = data && data.routes && data.routes[0];
|
|
186
227
|
var picked = route ? pickRouteGeometry(route) : null;
|
|
187
228
|
if (picked) {
|
|
@@ -191,7 +232,8 @@ ${initialDataScript}
|
|
|
191
232
|
}
|
|
192
233
|
})
|
|
193
234
|
.catch(function(err) {
|
|
194
|
-
if (
|
|
235
|
+
if (gen !== generation) return;
|
|
236
|
+
if (renderedGeneration === gen) return;
|
|
195
237
|
showError(
|
|
196
238
|
'Could not fetch route: ' + (err && err.message ? err.message : err)
|
|
197
239
|
);
|
|
@@ -220,6 +262,14 @@ ${initialDataScript}
|
|
|
220
262
|
}
|
|
221
263
|
|
|
222
264
|
window.addEventListener('message', function(event) {
|
|
265
|
+
// Only the embedding host (parent frame) is a legitimate sender. Any
|
|
266
|
+
// frame in the page can obtain this window's reference and postMessage
|
|
267
|
+
// crafted notifications; pinning the source window blocks that while
|
|
268
|
+
// staying host-agnostic — the host's origin is unknowable at
|
|
269
|
+
// HTML-generation time, so an origin allowlist isn't possible for a
|
|
270
|
+
// portable MCP App. (Standalone/top-level, window.parent === window,
|
|
271
|
+
// so self-posted messages still work for manual testing.)
|
|
272
|
+
if (event.source !== window.parent) return;
|
|
223
273
|
var message = event.data;
|
|
224
274
|
if (!message || typeof message !== 'object') return;
|
|
225
275
|
|
|
@@ -311,9 +361,17 @@ ${initialDataScript}
|
|
|
311
361
|
}
|
|
312
362
|
|
|
313
363
|
function handleToolResult(result) {
|
|
314
|
-
|
|
364
|
+
// A tool-result that isn't paired with this cycle's self-fetch (no
|
|
365
|
+
// tool-input was delivered for it — e.g. hosts that only send
|
|
366
|
+
// tool-result) represents a new tool call, so it starts its own render
|
|
367
|
+
// cycle. A paired result belongs to the cycle its tool-input opened;
|
|
368
|
+
// bumping for it would wrongly invalidate the in-flight self-fetch.
|
|
369
|
+
if (selfFetchGeneration !== generation) generation++;
|
|
315
370
|
var route = extractRoute(result);
|
|
316
371
|
if (route) {
|
|
372
|
+
// Authoritative geometry: always (re)render — drawRoute removes the
|
|
373
|
+
// previous route's layers/markers, which is what lets a host reuse
|
|
374
|
+
// one iframe across sequential directions_tool calls.
|
|
317
375
|
renderRoute(route);
|
|
318
376
|
return;
|
|
319
377
|
}
|
|
@@ -324,36 +382,39 @@ ${initialDataScript}
|
|
|
324
382
|
// MCP Apps host resources/read bridge.
|
|
325
383
|
var tempUri = findTempResourceUri(result);
|
|
326
384
|
if (tempUri) {
|
|
385
|
+
var gen = generation;
|
|
327
386
|
loadingEl.textContent = 'Fetching full route…';
|
|
328
387
|
sendRequest('resources/read', { uri: tempUri }).then(
|
|
329
388
|
function(rr) {
|
|
330
|
-
if (
|
|
389
|
+
if (gen !== generation) return; // stale: a newer call started
|
|
331
390
|
var fetched = readResourceJson(rr);
|
|
332
391
|
var fetchedRoute = fetched ? extractRoute({ structuredContent: fetched }) : null;
|
|
333
392
|
if (fetchedRoute) {
|
|
334
393
|
renderRoute(fetchedRoute);
|
|
335
|
-
} else {
|
|
394
|
+
} else if (renderedGeneration !== gen) {
|
|
336
395
|
showError('Could not parse route from the temporary resource.');
|
|
337
396
|
}
|
|
338
397
|
},
|
|
339
398
|
function(err) {
|
|
340
|
-
if (
|
|
399
|
+
if (gen !== generation || renderedGeneration === gen) return;
|
|
341
400
|
showError('Could not read temporary resource: ' + (err && err.message ? err.message : err));
|
|
342
401
|
}
|
|
343
402
|
);
|
|
344
403
|
return;
|
|
345
404
|
}
|
|
346
405
|
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
|
|
406
|
+
// No geometry in this result. Suppress the hard error only when this
|
|
407
|
+
// cycle's self-fetch is handling it (in flight, already rendered, or it
|
|
408
|
+
// already reported its own error via its .catch() handler). An unpaired
|
|
409
|
+
// result opened a fresh cycle above, so a route left over from an
|
|
410
|
+
// earlier call never suppresses this error.
|
|
411
|
+
if (selfFetchGeneration === generation) return;
|
|
351
412
|
|
|
352
413
|
showError('Could not find route data in tool result.');
|
|
353
414
|
}
|
|
354
415
|
|
|
355
416
|
function renderRoute(route) {
|
|
356
|
-
|
|
417
|
+
renderedGeneration = generation;
|
|
357
418
|
if (route.summary) {
|
|
358
419
|
summaryEl.textContent = route.summary;
|
|
359
420
|
summaryEl.style.display = 'block';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directionsAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,kCAAkC;;;AAyClC,
|
|
1
|
+
{"version":3,"file":"directionsAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":";AAAA,6BAA6B;AAC7B,kCAAkC;;;AAyClC,0DAwmBC;AA/oBD;;;;;;;;;;;;;;;;;;;GAmBG;AAEU,QAAA,iBAAiB,GAAG,QAAQ,CAAC;AAkB1C,SAAgB,uBAAuB,CAAC,MAKvC;IACC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACzD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,yBAAiB,CAAC;IAExD,MAAM,iBAAiB,GAAG,WAAW;QACnC,CAAC,CAAC,qDAAqD,eAAe,CAClE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAC5B,WAAW;QACd,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;mDAM0C,SAAS;oDACR,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D,iBAAiB;;;;gBAIH,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;uBACpB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8iB1C,CAAC;AACT,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,qEAAqE;IACrE,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directionsAppHtml.d.ts","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,0BAA0B,CAAC;CACpC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"directionsAppHtml.d.ts","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,0BAA0B,CAAC;CACpC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC,GAAG,MAAM,CAmmBT"}
|
|
@@ -82,8 +82,15 @@ ${initialDataScript}
|
|
|
82
82
|
// Track markers across re-renders so we can remove them before drawing the
|
|
83
83
|
// next route — otherwise a second tool-result delivery stacks pins.
|
|
84
84
|
var routeMarkers = [];
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
// One render "cycle" per tool call delivered to this iframe. A new
|
|
86
|
+
// tool-input (or baked initial-data) delivery starts a new cycle by
|
|
87
|
+
// bumping \`generation\`; async callbacks that captured an older value are
|
|
88
|
+
// stale and must not touch the UI. This dedupes self-fetch vs tool-result
|
|
89
|
+
// within one call WITHOUT permanently blocking later calls that reuse
|
|
90
|
+
// this same iframe instance.
|
|
91
|
+
var generation = 0;
|
|
92
|
+
var renderedGeneration = -1; // generation whose route is on screen (-1 = none)
|
|
93
|
+
var selfFetchGeneration = -1; // generation that started a self-fetch (-1 = none)
|
|
87
94
|
|
|
88
95
|
// -------------------------------------------------------------------------
|
|
89
96
|
// Self-fetch: build a Directions API request directly from the tool call's
|
|
@@ -156,9 +163,42 @@ ${initialDataScript}
|
|
|
156
163
|
// Exposed so the parity test (Node vm sandbox) can call this in isolation.
|
|
157
164
|
window.__buildDirectionsApiUrl = buildDirectionsApiUrl;
|
|
158
165
|
|
|
166
|
+
// Params arrive via postMessage and are untrusted: they get interpolated
|
|
167
|
+
// into the request URL, so validate the pieces our encoding does not
|
|
168
|
+
// neutralize. routing_profile is spliced into the URL path unencoded, and
|
|
169
|
+
// encodeExcludeClient only escapes ',', '(', ')' and spaces — characters
|
|
170
|
+
// like '&' or '../' path segments would otherwise let a hostile sibling
|
|
171
|
+
// frame reshape the request. Server-side input is zod-validated before it
|
|
172
|
+
// reaches buildDirectionsRequestUrl; this mirrors those guarantees for
|
|
173
|
+
// the client copy.
|
|
174
|
+
function isSafeDirectionsParams(params) {
|
|
175
|
+
if (!params || !Array.isArray(params.coordinates)) return false;
|
|
176
|
+
if (params.coordinates.length < 2) return false;
|
|
177
|
+
for (var i = 0; i < params.coordinates.length; i++) {
|
|
178
|
+
var c = params.coordinates[i];
|
|
179
|
+
if (!c || typeof c.longitude !== 'number' || !isFinite(c.longitude)) return false;
|
|
180
|
+
if (typeof c.latitude !== 'number' || !isFinite(c.latitude)) return false;
|
|
181
|
+
}
|
|
182
|
+
if (params.routing_profile !== undefined && params.routing_profile !== null) {
|
|
183
|
+
if (typeof params.routing_profile !== 'string') return false;
|
|
184
|
+
if (!/^mapbox\\/[a-z-]+$/.test(params.routing_profile)) return false;
|
|
185
|
+
}
|
|
186
|
+
if (params.exclude !== undefined && params.exclude !== null) {
|
|
187
|
+
if (typeof params.exclude !== 'string') return false;
|
|
188
|
+
if (!/^[A-Za-z0-9_,.() -]+$/.test(params.exclude)) return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
159
193
|
function fetchRouteFromDirectionsApi(params) {
|
|
160
|
-
|
|
161
|
-
|
|
194
|
+
// Every delivery (tool-input notification or baked initial-data) starts
|
|
195
|
+
// a new render cycle, even when the guard below prevents the fetch from
|
|
196
|
+
// actually running — the new call must still invalidate the previous
|
|
197
|
+
// cycle's in-flight callbacks and error-suppression state.
|
|
198
|
+
generation++;
|
|
199
|
+
if (!TOKEN || !isSafeDirectionsParams(params)) return;
|
|
200
|
+
var gen = generation; // captured by the callbacks below
|
|
201
|
+
selfFetchGeneration = gen; // marked only when the fetch actually proceeds
|
|
162
202
|
var url = buildDirectionsApiUrl(params, TOKEN, API_ENDPOINT);
|
|
163
203
|
fetch(url)
|
|
164
204
|
.then(function(res) {
|
|
@@ -177,7 +217,8 @@ ${initialDataScript}
|
|
|
177
217
|
return res.json();
|
|
178
218
|
})
|
|
179
219
|
.then(function(data) {
|
|
180
|
-
if (
|
|
220
|
+
if (gen !== generation) return; // stale: a newer call started
|
|
221
|
+
if (renderedGeneration === gen) return; // cycle already drawn (tool-result won)
|
|
181
222
|
var route = data && data.routes && data.routes[0];
|
|
182
223
|
var picked = route ? pickRouteGeometry(route) : null;
|
|
183
224
|
if (picked) {
|
|
@@ -187,7 +228,8 @@ ${initialDataScript}
|
|
|
187
228
|
}
|
|
188
229
|
})
|
|
189
230
|
.catch(function(err) {
|
|
190
|
-
if (
|
|
231
|
+
if (gen !== generation) return;
|
|
232
|
+
if (renderedGeneration === gen) return;
|
|
191
233
|
showError(
|
|
192
234
|
'Could not fetch route: ' + (err && err.message ? err.message : err)
|
|
193
235
|
);
|
|
@@ -216,6 +258,14 @@ ${initialDataScript}
|
|
|
216
258
|
}
|
|
217
259
|
|
|
218
260
|
window.addEventListener('message', function(event) {
|
|
261
|
+
// Only the embedding host (parent frame) is a legitimate sender. Any
|
|
262
|
+
// frame in the page can obtain this window's reference and postMessage
|
|
263
|
+
// crafted notifications; pinning the source window blocks that while
|
|
264
|
+
// staying host-agnostic — the host's origin is unknowable at
|
|
265
|
+
// HTML-generation time, so an origin allowlist isn't possible for a
|
|
266
|
+
// portable MCP App. (Standalone/top-level, window.parent === window,
|
|
267
|
+
// so self-posted messages still work for manual testing.)
|
|
268
|
+
if (event.source !== window.parent) return;
|
|
219
269
|
var message = event.data;
|
|
220
270
|
if (!message || typeof message !== 'object') return;
|
|
221
271
|
|
|
@@ -307,9 +357,17 @@ ${initialDataScript}
|
|
|
307
357
|
}
|
|
308
358
|
|
|
309
359
|
function handleToolResult(result) {
|
|
310
|
-
|
|
360
|
+
// A tool-result that isn't paired with this cycle's self-fetch (no
|
|
361
|
+
// tool-input was delivered for it — e.g. hosts that only send
|
|
362
|
+
// tool-result) represents a new tool call, so it starts its own render
|
|
363
|
+
// cycle. A paired result belongs to the cycle its tool-input opened;
|
|
364
|
+
// bumping for it would wrongly invalidate the in-flight self-fetch.
|
|
365
|
+
if (selfFetchGeneration !== generation) generation++;
|
|
311
366
|
var route = extractRoute(result);
|
|
312
367
|
if (route) {
|
|
368
|
+
// Authoritative geometry: always (re)render — drawRoute removes the
|
|
369
|
+
// previous route's layers/markers, which is what lets a host reuse
|
|
370
|
+
// one iframe across sequential directions_tool calls.
|
|
313
371
|
renderRoute(route);
|
|
314
372
|
return;
|
|
315
373
|
}
|
|
@@ -320,36 +378,39 @@ ${initialDataScript}
|
|
|
320
378
|
// MCP Apps host resources/read bridge.
|
|
321
379
|
var tempUri = findTempResourceUri(result);
|
|
322
380
|
if (tempUri) {
|
|
381
|
+
var gen = generation;
|
|
323
382
|
loadingEl.textContent = 'Fetching full route…';
|
|
324
383
|
sendRequest('resources/read', { uri: tempUri }).then(
|
|
325
384
|
function(rr) {
|
|
326
|
-
if (
|
|
385
|
+
if (gen !== generation) return; // stale: a newer call started
|
|
327
386
|
var fetched = readResourceJson(rr);
|
|
328
387
|
var fetchedRoute = fetched ? extractRoute({ structuredContent: fetched }) : null;
|
|
329
388
|
if (fetchedRoute) {
|
|
330
389
|
renderRoute(fetchedRoute);
|
|
331
|
-
} else {
|
|
390
|
+
} else if (renderedGeneration !== gen) {
|
|
332
391
|
showError('Could not parse route from the temporary resource.');
|
|
333
392
|
}
|
|
334
393
|
},
|
|
335
394
|
function(err) {
|
|
336
|
-
if (
|
|
395
|
+
if (gen !== generation || renderedGeneration === gen) return;
|
|
337
396
|
showError('Could not read temporary resource: ' + (err && err.message ? err.message : err));
|
|
338
397
|
}
|
|
339
398
|
);
|
|
340
399
|
return;
|
|
341
400
|
}
|
|
342
401
|
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
|
|
402
|
+
// No geometry in this result. Suppress the hard error only when this
|
|
403
|
+
// cycle's self-fetch is handling it (in flight, already rendered, or it
|
|
404
|
+
// already reported its own error via its .catch() handler). An unpaired
|
|
405
|
+
// result opened a fresh cycle above, so a route left over from an
|
|
406
|
+
// earlier call never suppresses this error.
|
|
407
|
+
if (selfFetchGeneration === generation) return;
|
|
347
408
|
|
|
348
409
|
showError('Could not find route data in tool result.');
|
|
349
410
|
}
|
|
350
411
|
|
|
351
412
|
function renderRoute(route) {
|
|
352
|
-
|
|
413
|
+
renderedGeneration = generation;
|
|
353
414
|
if (route.summary) {
|
|
354
415
|
summaryEl.textContent = route.summary;
|
|
355
416
|
summaryEl.style.display = 'block';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directionsAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAElC;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAkB1C,MAAM,UAAU,uBAAuB,CAAC,MAKvC;IACC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACzD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,iBAAiB,CAAC;IAExD,MAAM,iBAAiB,GAAG,WAAW;QACnC,CAAC,CAAC,qDAAqD,eAAe,CAClE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAC5B,WAAW;QACd,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;mDAM0C,SAAS;oDACR,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D,iBAAiB;;;;gBAIH,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;uBACpB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC
|
|
1
|
+
{"version":3,"file":"directionsAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/directionsAppHtml.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAElC;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAkB1C,MAAM,UAAU,uBAAuB,CAAC,MAKvC;IACC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACzD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,iBAAiB,CAAC;IAExD,MAAM,iBAAiB,GAAG,WAAW;QACnC,CAAC,CAAC,qDAAqD,eAAe,CAClE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAC5B,WAAW;QACd,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;mDAM0C,SAAS;oDACR,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D,iBAAiB;;;;gBAIH,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;uBACpB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8iB1C,CAAC;AACT,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,qEAAqE;IACrE,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/esm/version.json
CHANGED