@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.
@@ -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,CAsiBT"}
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
- var routeRendered = false;
90
- var selfFetchStarted = false;
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
- if (!TOKEN || !params || !params.coordinates) return;
165
- selfFetchStarted = true;
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 (routeRendered) return;
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 (routeRendered) return;
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
- if (routeRendered) return;
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 (routeRendered) return;
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 (routeRendered) return;
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
- // Self-fetch (kicked off by the earlier tool-input notification) is
348
- // either still in flight or already reported its own error via its
349
- // .catch() handler — either way, this isn't the right error to show.
350
- if (selfFetchStarted) return;
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
- routeRendered = true;
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,0DA2iBC;AAllBD;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAif1C,CAAC;AACT,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,qEAAqE;IACrE,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC"}
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,6 +1,6 @@
1
1
  {
2
- "sha": "2ed270d66ec380f01160969621355fe9d33b0f5d",
3
- "tag": "v0.12.6-4-g2ed270d",
2
+ "sha": "9aeac5deaa06eef59d932a7a1896ab2b8aed5613",
3
+ "tag": "v0.12.6-3-g9aeac5d",
4
4
  "branch": "HEAD",
5
- "version": "0.12.7-dev"
5
+ "version": "0.12.7"
6
6
  }
@@ -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,CAsiBT"}
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
- var routeRendered = false;
86
- var selfFetchStarted = false;
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
- if (!TOKEN || !params || !params.coordinates) return;
161
- selfFetchStarted = true;
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 (routeRendered) return;
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 (routeRendered) return;
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
- if (routeRendered) return;
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 (routeRendered) return;
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 (routeRendered) return;
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
- // Self-fetch (kicked off by the earlier tool-input notification) is
344
- // either still in flight or already reported its own error via its
345
- // .catch() handler — either way, this isn't the right error to show.
346
- if (selfFetchStarted) return;
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
- routeRendered = true;
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAif1C,CAAC;AACT,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,qEAAqE;IACrE,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AAClD,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"}
@@ -1,6 +1,6 @@
1
1
  {
2
- "sha": "2ed270d66ec380f01160969621355fe9d33b0f5d",
3
- "tag": "v0.12.6-4-g2ed270d",
2
+ "sha": "9aeac5deaa06eef59d932a7a1896ab2b8aed5613",
3
+ "tag": "v0.12.6-3-g9aeac5d",
4
4
  "branch": "HEAD",
5
- "version": "0.12.7-dev"
5
+ "version": "0.12.7"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapbox/mcp-server",
3
- "version": "0.12.7-dev",
3
+ "version": "0.12.7",
4
4
  "description": "Mapbox MCP server.",
5
5
  "mcpName": "io.github.mapbox/mcp-server",
6
6
  "main": "./dist/commonjs/index.js",