@mapbox/mcp-server 0.15.0 → 0.16.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 (131) hide show
  1. package/dist/commonjs/resources/place-photo/PlacePhotoResource.d.ts +57 -0
  2. package/dist/commonjs/resources/place-photo/PlacePhotoResource.d.ts.map +1 -0
  3. package/dist/commonjs/resources/place-photo/PlacePhotoResource.js +202 -0
  4. package/dist/commonjs/resources/place-photo/PlacePhotoResource.js.map +1 -0
  5. package/dist/commonjs/resources/resourceRegistry.d.ts +3 -1
  6. package/dist/commonjs/resources/resourceRegistry.d.ts.map +1 -1
  7. package/dist/commonjs/resources/resourceRegistry.js +5 -0
  8. package/dist/commonjs/resources/resourceRegistry.js.map +1 -1
  9. package/dist/commonjs/resources/tool-response/ToolResponseResource.d.ts +20 -0
  10. package/dist/commonjs/resources/tool-response/ToolResponseResource.d.ts.map +1 -0
  11. package/dist/commonjs/resources/tool-response/ToolResponseResource.js +83 -0
  12. package/dist/commonjs/resources/tool-response/ToolResponseResource.js.map +1 -0
  13. package/dist/commonjs/resources/ui-apps/mapAppHtml.d.ts.map +1 -1
  14. package/dist/commonjs/resources/ui-apps/mapAppHtml.js +484 -11
  15. package/dist/commonjs/resources/ui-apps/mapAppHtml.js.map +1 -1
  16. package/dist/commonjs/tools/category-list-tool/CategoryListTool.d.ts.map +1 -1
  17. package/dist/commonjs/tools/category-list-tool/CategoryListTool.input.schema.js +1 -1
  18. package/dist/commonjs/tools/category-list-tool/CategoryListTool.input.schema.js.map +1 -1
  19. package/dist/commonjs/tools/category-list-tool/CategoryListTool.js +1 -1
  20. package/dist/commonjs/tools/category-list-tool/CategoryListTool.js.map +1 -1
  21. package/dist/commonjs/tools/directions-tool/DirectionsTool.d.ts.map +1 -1
  22. package/dist/commonjs/tools/directions-tool/DirectionsTool.input.schema.js +3 -3
  23. package/dist/commonjs/tools/directions-tool/DirectionsTool.input.schema.js.map +1 -1
  24. package/dist/commonjs/tools/directions-tool/DirectionsTool.js +17 -17
  25. package/dist/commonjs/tools/directions-tool/DirectionsTool.js.map +1 -1
  26. package/dist/commonjs/tools/ground-location-tool/GroundLocationTool.d.ts.map +1 -1
  27. package/dist/commonjs/tools/ground-location-tool/GroundLocationTool.js +1 -1
  28. package/dist/commonjs/tools/ground-location-tool/GroundLocationTool.js.map +1 -1
  29. package/dist/commonjs/tools/isochrone-tool/IsochroneTool.d.ts.map +1 -1
  30. package/dist/commonjs/tools/isochrone-tool/IsochroneTool.js +6 -13
  31. package/dist/commonjs/tools/isochrone-tool/IsochroneTool.js.map +1 -1
  32. package/dist/commonjs/tools/render-map-tool/RenderMapTool.d.ts.map +1 -1
  33. package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.d.ts +4 -0
  34. package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.d.ts.map +1 -1
  35. package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.js +18 -2
  36. package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.js.map +1 -1
  37. package/dist/commonjs/tools/render-map-tool/RenderMapTool.js +9 -5
  38. package/dist/commonjs/tools/render-map-tool/RenderMapTool.js.map +1 -1
  39. package/dist/commonjs/tools/render-map-tool/RenderMapTool.output.schema.d.ts +4 -0
  40. package/dist/commonjs/tools/render-map-tool/RenderMapTool.output.schema.d.ts.map +1 -1
  41. package/dist/commonjs/utils/directionsRef.d.ts +27 -0
  42. package/dist/commonjs/utils/directionsRef.d.ts.map +1 -0
  43. package/dist/commonjs/utils/directionsRef.js +52 -0
  44. package/dist/commonjs/utils/directionsRef.js.map +1 -0
  45. package/dist/commonjs/utils/index.d.ts +8 -0
  46. package/dist/commonjs/utils/index.d.ts.map +1 -1
  47. package/dist/commonjs/utils/index.js +23 -1
  48. package/dist/commonjs/utils/index.js.map +1 -1
  49. package/dist/commonjs/utils/mapAppPayload.d.ts +21 -0
  50. package/dist/commonjs/utils/mapAppPayload.d.ts.map +1 -1
  51. package/dist/commonjs/utils/mapAppPayload.js +5 -1
  52. package/dist/commonjs/utils/mapAppPayload.js.map +1 -1
  53. package/dist/commonjs/utils/responseRef.d.ts +33 -0
  54. package/dist/commonjs/utils/responseRef.d.ts.map +1 -0
  55. package/dist/commonjs/utils/responseRef.js +73 -0
  56. package/dist/commonjs/utils/responseRef.js.map +1 -0
  57. package/dist/commonjs/utils/serverInstructions.d.ts +2 -2
  58. package/dist/commonjs/utils/serverInstructions.js +2 -2
  59. package/dist/commonjs/utils/storeMapPayload.js +1 -1
  60. package/dist/commonjs/utils/storeMapPayload.js.map +1 -1
  61. package/dist/commonjs/utils/urlSafety.d.ts +6 -0
  62. package/dist/commonjs/utils/urlSafety.d.ts.map +1 -1
  63. package/dist/commonjs/utils/urlSafety.js +15 -4
  64. package/dist/commonjs/utils/urlSafety.js.map +1 -1
  65. package/dist/commonjs/version.json +3 -3
  66. package/dist/esm/resources/place-photo/PlacePhotoResource.d.ts +57 -0
  67. package/dist/esm/resources/place-photo/PlacePhotoResource.d.ts.map +1 -0
  68. package/dist/esm/resources/place-photo/PlacePhotoResource.js +198 -0
  69. package/dist/esm/resources/place-photo/PlacePhotoResource.js.map +1 -0
  70. package/dist/esm/resources/resourceRegistry.d.ts +3 -1
  71. package/dist/esm/resources/resourceRegistry.d.ts.map +1 -1
  72. package/dist/esm/resources/resourceRegistry.js +5 -0
  73. package/dist/esm/resources/resourceRegistry.js.map +1 -1
  74. package/dist/esm/resources/tool-response/ToolResponseResource.d.ts +20 -0
  75. package/dist/esm/resources/tool-response/ToolResponseResource.d.ts.map +1 -0
  76. package/dist/esm/resources/tool-response/ToolResponseResource.js +79 -0
  77. package/dist/esm/resources/tool-response/ToolResponseResource.js.map +1 -0
  78. package/dist/esm/resources/ui-apps/mapAppHtml.d.ts.map +1 -1
  79. package/dist/esm/resources/ui-apps/mapAppHtml.js +484 -11
  80. package/dist/esm/resources/ui-apps/mapAppHtml.js.map +1 -1
  81. package/dist/esm/tools/category-list-tool/CategoryListTool.d.ts.map +1 -1
  82. package/dist/esm/tools/category-list-tool/CategoryListTool.input.schema.js +1 -1
  83. package/dist/esm/tools/category-list-tool/CategoryListTool.input.schema.js.map +1 -1
  84. package/dist/esm/tools/category-list-tool/CategoryListTool.js +1 -1
  85. package/dist/esm/tools/category-list-tool/CategoryListTool.js.map +1 -1
  86. package/dist/esm/tools/directions-tool/DirectionsTool.d.ts.map +1 -1
  87. package/dist/esm/tools/directions-tool/DirectionsTool.input.schema.js +3 -3
  88. package/dist/esm/tools/directions-tool/DirectionsTool.input.schema.js.map +1 -1
  89. package/dist/esm/tools/directions-tool/DirectionsTool.js +17 -17
  90. package/dist/esm/tools/directions-tool/DirectionsTool.js.map +1 -1
  91. package/dist/esm/tools/ground-location-tool/GroundLocationTool.d.ts.map +1 -1
  92. package/dist/esm/tools/ground-location-tool/GroundLocationTool.js +1 -1
  93. package/dist/esm/tools/ground-location-tool/GroundLocationTool.js.map +1 -1
  94. package/dist/esm/tools/isochrone-tool/IsochroneTool.d.ts.map +1 -1
  95. package/dist/esm/tools/isochrone-tool/IsochroneTool.js +6 -13
  96. package/dist/esm/tools/isochrone-tool/IsochroneTool.js.map +1 -1
  97. package/dist/esm/tools/render-map-tool/RenderMapTool.d.ts.map +1 -1
  98. package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.d.ts +4 -0
  99. package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.d.ts.map +1 -1
  100. package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.js +18 -2
  101. package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.js.map +1 -1
  102. package/dist/esm/tools/render-map-tool/RenderMapTool.js +9 -5
  103. package/dist/esm/tools/render-map-tool/RenderMapTool.js.map +1 -1
  104. package/dist/esm/tools/render-map-tool/RenderMapTool.output.schema.d.ts +4 -0
  105. package/dist/esm/tools/render-map-tool/RenderMapTool.output.schema.d.ts.map +1 -1
  106. package/dist/esm/utils/directionsRef.d.ts +27 -0
  107. package/dist/esm/utils/directionsRef.d.ts.map +1 -0
  108. package/dist/esm/utils/directionsRef.js +49 -0
  109. package/dist/esm/utils/directionsRef.js.map +1 -0
  110. package/dist/esm/utils/index.d.ts +8 -0
  111. package/dist/esm/utils/index.d.ts.map +1 -1
  112. package/dist/esm/utils/index.js +15 -0
  113. package/dist/esm/utils/index.js.map +1 -1
  114. package/dist/esm/utils/mapAppPayload.d.ts +21 -0
  115. package/dist/esm/utils/mapAppPayload.d.ts.map +1 -1
  116. package/dist/esm/utils/mapAppPayload.js +5 -1
  117. package/dist/esm/utils/mapAppPayload.js.map +1 -1
  118. package/dist/esm/utils/responseRef.d.ts +33 -0
  119. package/dist/esm/utils/responseRef.d.ts.map +1 -0
  120. package/dist/esm/utils/responseRef.js +68 -0
  121. package/dist/esm/utils/responseRef.js.map +1 -0
  122. package/dist/esm/utils/serverInstructions.d.ts +2 -2
  123. package/dist/esm/utils/serverInstructions.js +2 -2
  124. package/dist/esm/utils/storeMapPayload.js +1 -1
  125. package/dist/esm/utils/storeMapPayload.js.map +1 -1
  126. package/dist/esm/utils/urlSafety.d.ts +6 -0
  127. package/dist/esm/utils/urlSafety.d.ts.map +1 -1
  128. package/dist/esm/utils/urlSafety.js +14 -4
  129. package/dist/esm/utils/urlSafety.js.map +1 -1
  130. package/dist/esm/version.json +3 -3
  131. package/package.json +9 -2
@@ -71,12 +71,51 @@ export function renderMapAppHtml(params) {
71
71
  box-shadow: 0 2px 6px rgba(0,0,0,0.4);
72
72
  cursor: pointer;
73
73
  }
74
+ #side-panel {
75
+ position: absolute; top: 12px; right: 12px; bottom: 12px; z-index: 10;
76
+ width: 280px; max-width: calc(100% - 24px);
77
+ background: rgba(15, 23, 42, 0.88); color: #f1f5f9;
78
+ border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
79
+ display: flex; flex-direction: column; overflow: hidden;
80
+ }
81
+ #side-panel .panel-head {
82
+ padding: 10px 12px; font-size: 13px; font-weight: 600;
83
+ border-bottom: 1px solid rgba(255,255,255,0.12); flex: 0 0 auto;
84
+ display: flex; justify-content: space-between; gap: 8px; cursor: pointer;
85
+ }
86
+ /* Click the heading to collapse the list (see togglePanel). */
87
+ #side-panel .panel-head::after { content: '▾'; opacity: 0.7; }
88
+ #side-panel.collapsed { bottom: auto; }
89
+ #side-panel.collapsed .panel-list { display: none; }
90
+ #side-panel.collapsed .panel-head { border-bottom: none; }
91
+ #side-panel.collapsed .panel-head::after { content: '▸'; }
92
+ #side-panel .panel-list { list-style: none; margin: 0; padding: 6px; overflow-y: auto; flex: 1; }
93
+ #side-panel .panel-item {
94
+ display: flex; align-items: flex-start; gap: 6px; padding: 6px; border-radius: 6px; cursor: pointer;
95
+ }
96
+ #side-panel .panel-item:hover { background: rgba(255,255,255,0.08); }
97
+ /* Only added once a photo has loaded (see loadPanelPhoto), at the end of
98
+ the row so names stay aligned whether or not a row has one. */
99
+ #side-panel .panel-thumb {
100
+ flex: 0 0 auto; width: 40px; height: 40px; border-radius: 6px; margin-left: auto;
101
+ background-size: cover; background-position: center;
102
+ }
103
+ /* Visit-order badge, matching the numbered map marker. */
104
+ #side-panel .panel-badge {
105
+ flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%; margin-top: 1px;
106
+ background: #f97316; color: #1a0f04;
107
+ display: flex; align-items: center; justify-content: center;
108
+ font-size: 10px; font-weight: 700;
109
+ }
110
+ #side-panel .panel-name { font-size: 12.5px; font-weight: 600; }
111
+ #side-panel .panel-meta { font-size: 11px; color: #cbd5e1; margin-top: 2px; }
74
112
  </style>
75
113
  </head>
76
114
  <body>
77
115
  <div id="map"></div>
78
116
  <div id="summary" style="display:none"></div>
79
117
  <div id="legend" style="display:none"></div>
118
+ <div id="side-panel" style="display:none"></div>
80
119
  <div id="loading">Loading…</div>
81
120
  <div id="error" style="display:none"></div>
82
121
  ${initialDataScript}
@@ -90,6 +129,11 @@ ${initialDataScript}
90
129
  var errorEl = document.getElementById('error');
91
130
  var summaryEl = document.getElementById('summary');
92
131
  var legendEl = document.getElementById('legend');
132
+ var panelEl = document.getElementById('side-panel');
133
+ // Populated by buildSearchMiniPayload's marker tagging so the panel (click
134
+ // -> flyTo) and Place Details enrichment can find a marker by mapbox_id
135
+ // without re-querying the map for it.
136
+ var markersById = {};
93
137
 
94
138
  var map = null;
95
139
  var mapLoaded = false;
@@ -109,7 +153,9 @@ ${initialDataScript}
109
153
  var messageId = 0;
110
154
  var pendingRequests = new Map();
111
155
 
112
- function sendRequest(method, params) {
156
+ // timeoutMs (optional) rejects a request nobody answers, for hosts that
157
+ // don't implement the method.
158
+ function sendRequest(method, params, timeoutMs) {
113
159
  var id = ++messageId;
114
160
  try {
115
161
  window.parent.postMessage({
@@ -118,6 +164,13 @@ ${initialDataScript}
118
164
  } catch (_) {}
119
165
  return new Promise(function(resolve, reject) {
120
166
  pendingRequests.set(id, { resolve: resolve, reject: reject });
167
+ if (timeoutMs) {
168
+ setTimeout(function() {
169
+ if (!pendingRequests.has(id)) return;
170
+ pendingRequests.delete(id);
171
+ reject(new Error(method + ' timed out'));
172
+ }, timeoutMs);
173
+ }
121
174
  });
122
175
  }
123
176
  function sendNotification(method, params) {
@@ -152,13 +205,22 @@ ${initialDataScript}
152
205
  }
153
206
  });
154
207
 
208
+ // Set once a host answers ui/initialize, even with an error. A page from
209
+ // export_map_html_tool framed by some other site never gets an answer.
210
+ var hostAnswered = false;
155
211
  sendRequest('ui/initialize', {
156
212
  protocolVersion: '2026-01-26',
157
213
  appCapabilities: {},
158
214
  appInfo: { name: 'Mapbox Map', version: '1.0.0' }
159
215
  }).then(
160
- function() { sendNotification('ui/notifications/initialized', {}); },
161
- function() { sendNotification('ui/notifications/initialized', {}); }
216
+ function() {
217
+ hostAnswered = true;
218
+ sendNotification('ui/notifications/initialized', {});
219
+ },
220
+ function() {
221
+ hostAnswered = true;
222
+ sendNotification('ui/notifications/initialized', {});
223
+ }
162
224
  );
163
225
 
164
226
  // --- Map setup ------------------------------------------------------------
@@ -362,6 +424,13 @@ ${initialDataScript}
362
424
  trackedLayerIds = [];
363
425
  trackedSourceIds = [];
364
426
  trackedMarkers = [];
427
+ markersById = {};
428
+ panelEl.style.display = 'none';
429
+ clearPanel();
430
+ // Place Details responses still in flight belong to the old render.
431
+ panelGeneration += 1;
432
+ panelCount = 0;
433
+ panelIds = {};
365
434
  }
366
435
 
367
436
  function bboxAccumulator() {
@@ -447,14 +516,108 @@ ${initialDataScript}
447
516
  if (m.popup) marker.setPopup(new mapboxgl.Popup().setText(m.popup));
448
517
  marker.addTo(map);
449
518
  trackedMarkers.push(marker);
519
+ // Tagged by buildSearchMiniPayload with the result's mapbox_id so the
520
+ // side panel (click-to-flyTo) and Place Details enrichment can look the
521
+ // marker back up later without walking trackedMarkers.
522
+ if (m.id) markersById[m.id] = { marker: marker, popupText: m.popup || '' };
450
523
  bbox.extend(m.coordinates);
451
524
  }
452
525
 
526
+ // Guarantees a marker that opts into the side panel (has .id) also gets
527
+ // a visible number on the map matching its panel row, unless the caller
528
+ // already gave it an explicit label. Without this, a caller that builds
529
+ // plain unlabeled pins for POI markers (observed live: Claude did this
530
+ // for inline markers, leaving the map showing generic blue pins with no
531
+ // way to tell which panel row is which) breaks the whole point of the
532
+ // panel numbering. Mutates markers in place, so it must run before
533
+ // addOneMarker draws them.
534
+ //
535
+ // Numbering runs across the whole render, not per call: a render can
536
+ // draw inline markers and then merge in self-fetched search results, and
537
+ // each set arrives numbered from 1. The first set keeps the caller's
538
+ // numbers when they're unique (a visit order, say); a later set, or one
539
+ // with repeated numbers, is renumbered to continue the panel's count.
540
+ // Letter labels ("A") are kept as they are. A place already in the panel
541
+ // this render is dropped rather than drawn and listed twice. Returns the
542
+ // markers to draw.
543
+ function applyPanelNumbering(markers) {
544
+ if (!Array.isArray(markers)) return [];
545
+ var seen = {};
546
+ var fresh = markers.filter(function(m) {
547
+ if (!m || !m.id) return true;
548
+ if (panelIds[m.id] || seen[m.id]) return false;
549
+ seen[m.id] = true;
550
+ return true;
551
+ });
552
+ var keep = panelCount === 0;
553
+ var used = {};
554
+ fresh.forEach(function(m) {
555
+ if (!m || !m.id || !isWholeNumber(m.label)) return;
556
+ if (used[m.label]) keep = false;
557
+ used[m.label] = true;
558
+ });
559
+ if (keep) {
560
+ Object.keys(used).forEach(function(n) {
561
+ panelCount = Math.max(panelCount, parseInt(n, 10));
562
+ });
563
+ }
564
+ fresh.forEach(function(m) {
565
+ if (!m || !m.id) return;
566
+ if (m.label && (keep || !isWholeNumber(m.label))) return;
567
+ panelCount += 1;
568
+ var n = String(panelCount);
569
+ if (!m.label) {
570
+ if (m.style !== 'start' && m.style !== 'end') m.style = 'numbered';
571
+ if (!m.color) m.color = '#f97316';
572
+ } else if (typeof m.popup === 'string' && m.popup.indexOf(m.label + '. ') === 0) {
573
+ // Search popups start with the old number ("3. Name").
574
+ m.popup = n + '. ' + m.popup.slice(String(m.label).length + 2);
575
+ }
576
+ m.label = n;
577
+ });
578
+ return fresh;
579
+ }
580
+
581
+ function isWholeNumber(label) {
582
+ if (label === undefined || label === null || label === '') return false;
583
+ var n = parseInt(label, 10);
584
+ return n > 0 && String(n) === String(label);
585
+ }
586
+
587
+ // Bottom padding that keeps fitted data out from under the legend
588
+ // (bottom-left), unless the map is too short to spare the room.
589
+ function bottomPadding() {
590
+ if (legendEl.style.display !== 'block') return 50;
591
+ var bottom = (legendEl.offsetHeight || 0) + 24;
592
+ var el = document.getElementById('map');
593
+ var height = (el && el.clientHeight) || window.innerHeight || 0;
594
+ if (bottom <= 50 || (height && height - bottom - 70 < 160)) return 50;
595
+ return bottom;
596
+ }
597
+
598
+ // Right padding that keeps fitted data out from under an open panel. On
599
+ // a narrow map there's no room beside it, so fit as if it weren't there
600
+ // (the panel starts collapsed there anyway; see appendPanelRows).
601
+ function rightPadding() {
602
+ if (panelEl.style.display !== 'flex' || panelCollapsed) return 30;
603
+ var right = (panelEl.offsetWidth || 280) + 24;
604
+ var mapWidth = mapContainerWidth();
605
+ if (mapWidth && mapWidth - right < 240) return 30;
606
+ return right;
607
+ }
608
+
609
+ function mapContainerWidth() {
610
+ var el = document.getElementById('map');
611
+ return (el && el.clientWidth) || window.innerWidth || 0;
612
+ }
613
+
614
+ var lastFitBounds = null;
453
615
  function fitToBounds(bounds) {
616
+ lastFitBounds = bounds;
454
617
  setTimeout(function() {
455
618
  map.resize();
456
619
  map.fitBounds(bounds, {
457
- padding: { top: 70, bottom: 50, left: 30, right: 30 },
620
+ padding: { top: 70, bottom: bottomPadding(), left: 30, right: rightPadding() },
458
621
  duration: 600
459
622
  });
460
623
  }, 60);
@@ -470,13 +633,13 @@ ${initialDataScript}
470
633
  (Array.isArray(payload.layers) ? payload.layers : []).forEach(function(layer) {
471
634
  addOneLayer(layer, bbox);
472
635
  });
473
- (Array.isArray(payload.markers) ? payload.markers : []).forEach(function(m) {
474
- addOneMarker(m, bbox);
475
- });
636
+ var markers = applyPanelNumbering(payload.markers);
637
+ markers.forEach(function(m) { addOneMarker(m, bbox); });
476
638
  if (payload.summary && summaryEl.style.display === 'none') {
477
639
  summaryEl.textContent = payload.summary;
478
640
  summaryEl.style.display = 'block';
479
641
  }
642
+ maybeShowSidePanel(markers, payload.summary);
480
643
  var bounds = bbox.bounds();
481
644
  if (bounds) fitToBounds(bounds);
482
645
  }
@@ -515,10 +678,14 @@ ${initialDataScript}
515
678
 
516
679
  var bbox = bboxAccumulator();
517
680
  var layers = Array.isArray(payload.layers) ? payload.layers : [];
518
- var markers = Array.isArray(payload.markers) ? payload.markers : [];
519
-
520
681
  layers.forEach(function(layer) { addOneLayer(layer, bbox); });
682
+ var markers = applyPanelNumbering(payload.markers);
521
683
  markers.forEach(function(m) { addOneMarker(m, bbox); });
684
+ // Covers both inline markers (a caller-composed payload) and self-fetch
685
+ // markers (merged in later via mergeAdditionalPayload) — see
686
+ // maybeShowSidePanel below. Whichever path produced id-bearing POI
687
+ // markers gets the same results panel.
688
+ maybeShowSidePanel(markers, payload.summary);
522
689
 
523
690
  // Legend
524
691
  if (Array.isArray(payload.legend) && payload.legend.length > 0) {
@@ -1229,12 +1396,22 @@ ${initialDataScript}
1229
1396
  popupParts.push(Math.round(props.distance) + ' m');
1230
1397
  }
1231
1398
  var coords = f.geometry.coordinates;
1399
+ // id/name/category/distanceMeters are the same generic POI fields an
1400
+ // inline (hand-composed) marker can carry — see maybeShowSidePanel,
1401
+ // which builds the results panel from either source identically.
1402
+ // mapbox_id is missing on the rare result that lacks one; the panel
1403
+ // just skips that row rather than show an unclickable/unenrichable
1404
+ // entry.
1232
1405
  markers.push({
1406
+ id: props.mapbox_id,
1233
1407
  coordinates: coords,
1234
1408
  style: 'numbered',
1235
1409
  label: String(i + 1),
1236
1410
  color: '#f97316',
1237
- popup: popupParts.join(' — ')
1411
+ popup: popupParts.join(' — '),
1412
+ name: props.name || ('Result ' + (i + 1)),
1413
+ category: Array.isArray(props.poi_category) ? props.poi_category[0] : undefined,
1414
+ distanceMeters: typeof props.distance === 'number' ? props.distance : undefined
1238
1415
  });
1239
1416
  features.push({
1240
1417
  type: 'Feature',
@@ -1266,6 +1443,296 @@ ${initialDataScript}
1266
1443
  return { summary: summary, layers: layers, markers: markers };
1267
1444
  }
1268
1445
 
1446
+ // --- Side panel: result list + Place Details enrichment ----------------
1447
+ // Triggered generically by maybeShowSidePanel for ANY marker set that
1448
+ // includes id-bearing entries — self-fetch search/category-search
1449
+ // results (via mergeAdditionalPayload) and hand-composed inline markers
1450
+ // from render_map_tool's own "markers" input (via renderBody) both funnel
1451
+ // through the same path. Renders the initial (Search Box-only, or
1452
+ // whatever the caller already provided) list immediately so the panel
1453
+ // never blocks on the slower enrichment call. sourceLabel is plain text
1454
+ // shown as the panel heading (e.g. the query, category, or the payload's
1455
+ // own summary).
1456
+ function derivePanelItems(markers) {
1457
+ if (!Array.isArray(markers)) return [];
1458
+ var items = [];
1459
+ markers.forEach(function(m) {
1460
+ if (!m || !m.id) return;
1461
+ // The pin's own label, so a row reads the same as its pin ("A" too).
1462
+ var badge = isWholeNumber(m.label) ? parseInt(m.label, 10)
1463
+ : (m.label ? String(m.label) : items.length + 1);
1464
+ items.push({
1465
+ id: m.id,
1466
+ number: badge,
1467
+ name: m.name || ('Result ' + (items.length + 1)),
1468
+ category: m.category,
1469
+ distanceMeters: typeof m.distanceMeters === 'number' ? m.distanceMeters : undefined,
1470
+ color: m.color
1471
+ });
1472
+ });
1473
+ return items;
1474
+ }
1475
+ // Exposed so tests can exercise the pure derivation logic without a DOM.
1476
+ window.__derivePanelItems = derivePanelItems;
1477
+
1478
+ // Adds rows for places not yet in this render's panel and enriches just
1479
+ // those. A response is applied only if no newer render has started since
1480
+ // (panelGeneration), so a slow lookup can't write into a newer panel.
1481
+ function maybeShowSidePanel(markers, summary) {
1482
+ var items = derivePanelItems(markers).filter(function(it) {
1483
+ return !panelIds[it.id];
1484
+ });
1485
+ if (items.length === 0) return;
1486
+ items.forEach(function(it) { panelIds[it.id] = true; });
1487
+ appendPanelRows(items, summary || 'Results');
1488
+ var generation = panelGeneration;
1489
+ fetchPlaceDetailsBatch(
1490
+ items.map(function(it) { return it.id; }),
1491
+ TOKEN,
1492
+ API_ENDPOINT
1493
+ ).then(function(result) {
1494
+ if (generation === panelGeneration) enrichSidePanel(result);
1495
+ });
1496
+ }
1497
+
1498
+ // Keyed by mapbox_id -> { li, meta } elements, so enrichSidePanel can
1499
+ // update a row directly once Place Details resolves, without re-parsing
1500
+ // panelEl's markup back into a queryable DOM (innerHTML + querySelector
1501
+ // round-trips don't work in every MCP Apps host's iframe sandbox, and
1502
+ // building real nodes up front is simpler to reason about either way).
1503
+ // Reset by clearPanel, same lifecycle as the panel itself.
1504
+ var panelRowsById = {};
1505
+ // Bumped by teardown(), so responses from an earlier render are dropped.
1506
+ var panelGeneration = 0;
1507
+ // Id-bearing markers numbered so far this render, and the ids listed.
1508
+ var panelCount = 0;
1509
+ var panelIds = {};
1510
+ // The viewer's collapse choice, kept across renders; null until the first
1511
+ // panel decides a default from the map's width.
1512
+ var panelCollapsed = null;
1513
+ var panelListEl = null;
1514
+
1515
+ function buildPanelRow(it) {
1516
+ var li = document.createElement('li');
1517
+ li.className = 'panel-item';
1518
+
1519
+ var badge = document.createElement('span');
1520
+ badge.className = 'panel-badge';
1521
+ badge.textContent = String(it.number);
1522
+ // Match the pin when the caller colored it (e.g. gray for "other
1523
+ // results" next to a legend); the default orange is in the CSS.
1524
+ if (typeof it.color === 'string' && it.color.toLowerCase() !== '#f97316' &&
1525
+ (/^#[0-9a-f]{3,8}$/i).test(it.color)) {
1526
+ badge.style.background = it.color;
1527
+ badge.style.color = '#fff';
1528
+ }
1529
+
1530
+ var name = document.createElement('div');
1531
+ name.className = 'panel-name';
1532
+ name.textContent = it.name;
1533
+
1534
+ var metaParts = [];
1535
+ if (it.category) metaParts.push(it.category);
1536
+ if (typeof it.distanceMeters === 'number') {
1537
+ metaParts.push(Math.round(it.distanceMeters) + ' m');
1538
+ }
1539
+ var meta = document.createElement('div');
1540
+ meta.className = 'panel-meta';
1541
+ meta.textContent = metaParts.join(' · ');
1542
+
1543
+ var body = document.createElement('div');
1544
+ body.appendChild(name);
1545
+ body.appendChild(meta);
1546
+ li.appendChild(badge);
1547
+ li.appendChild(body);
1548
+
1549
+ li.addEventListener('click', function() {
1550
+ var entry = markersById[it.id];
1551
+ if (entry) map.flyTo({ center: entry.marker.getLngLat(), zoom: 15, duration: 500 });
1552
+ });
1553
+
1554
+ panelRowsById[it.id] = { li: li, meta: meta };
1555
+ return li;
1556
+ }
1557
+
1558
+ function clearPanel() {
1559
+ while (panelEl.firstChild) panelEl.removeChild(panelEl.firstChild);
1560
+ panelRowsById = {};
1561
+ panelListEl = null;
1562
+ }
1563
+
1564
+ function applyPanelCollapsed() {
1565
+ panelEl.className = panelCollapsed ? 'collapsed' : '';
1566
+ }
1567
+
1568
+ function togglePanel() {
1569
+ panelCollapsed = !panelCollapsed;
1570
+ applyPanelCollapsed();
1571
+ if (lastFitBounds) fitToBounds(lastFitBounds);
1572
+ }
1573
+
1574
+ // Creates the panel on the render's first id-bearing markers, and adds
1575
+ // rows to it for later ones (e.g. search results merged in after inline
1576
+ // markers), so every numbered pin has a row.
1577
+ function appendPanelRows(items, sourceLabel) {
1578
+ if (!Array.isArray(items) || items.length === 0) return;
1579
+ if (!panelListEl) {
1580
+ var head = document.createElement('div');
1581
+ head.className = 'panel-head';
1582
+ head.textContent = sourceLabel || 'Results';
1583
+ head.addEventListener('click', togglePanel);
1584
+
1585
+ panelListEl = document.createElement('ul');
1586
+ panelListEl.className = 'panel-list';
1587
+
1588
+ panelEl.appendChild(head);
1589
+ panelEl.appendChild(panelListEl);
1590
+ // On a narrow map the open panel would cover most of it.
1591
+ if (panelCollapsed === null) {
1592
+ var width = mapContainerWidth();
1593
+ panelCollapsed = width > 0 && width < 560;
1594
+ }
1595
+ applyPanelCollapsed();
1596
+ panelEl.style.display = 'flex';
1597
+ }
1598
+ var list = panelListEl;
1599
+ items.forEach(function(it) { list.appendChild(buildPanelRow(it)); });
1600
+ }
1601
+
1602
+ // Search Box results mix Mapbox-native POI ids (decode to
1603
+ // "urn:mbxpoi:<uuid>") with OSM-sourced ones (decode to
1604
+ // "urn:mbxpoi-osm:n<osm-node-id>") — confirmed live against a real
1605
+ // category_search_tool-style query near Herndon, VA, where 2 of 10
1606
+ // "cafe" results were OSM-sourced. The Places API's batch endpoint
1607
+ // rejects the ENTIRE request with a 422 if even one id isn't its native
1608
+ // scheme (same 422 PlaceDetailsTool.ts already works around server-side
1609
+ // for boundary ids) — so filter client-side before sending, rather than
1610
+ // let one incompatible id in a 10-result batch silently zero out
1611
+ // enrichment for the other 9. The prefix alone isn't enough: near Union
1612
+ // Square, SF, a Starbucks decodes to "urn:mbxpoi:mapbox-n408854", which
1613
+ // the endpoint also rejects, so require the UUID form.
1614
+ var PLACES_ID = /^urn:mbxpoi:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1615
+ function isPlacesApiCompatibleId(id) {
1616
+ if (typeof id !== 'string' || id.length === 0) return false;
1617
+ try {
1618
+ return PLACES_ID.test(atob(id));
1619
+ } catch (e) {
1620
+ return false;
1621
+ }
1622
+ }
1623
+
1624
+ // Places API Details endpoint — batch form. Mirrors the single-record
1625
+ // GET src/tools/place-details-tool/PlaceDetailsTool.ts calls server-side,
1626
+ // but the panel needs details for every result at once, so this uses the
1627
+ // POST /details/retrieve batch form (up to 100 ids) instead of one
1628
+ // request per pin. Docs: https://docs.mapbox.com/api/search/places/
1629
+ // Like every other self-fetch call, this runs client-side against the
1630
+ // public token — the Places API's docs list no secret scope requirement
1631
+ // for this endpoint, same as Search Box/Directions/etc.
1632
+ function fetchPlaceDetailsBatch(ids, publicToken, apiEndpoint) {
1633
+ var compatibleIds = (Array.isArray(ids) ? ids : []).filter(isPlacesApiCompatibleId);
1634
+ if (compatibleIds.length === 0) return Promise.resolve(null);
1635
+ var qp = new URLSearchParams();
1636
+ qp.append('access_token', publicToken);
1637
+ var url = apiEndpoint + 'places/v1/details/retrieve?' + qp.toString();
1638
+ var post = function(batch, retried) {
1639
+ return fetch(url, {
1640
+ method: 'POST',
1641
+ headers: { 'Content-Type': 'application/json' },
1642
+ body: JSON.stringify({ ids: batch })
1643
+ }).then(function(res) {
1644
+ // 206 is a partial batch (some ids missing/unprocessed) — still
1645
+ // has usable "results", so treat it the same as 200.
1646
+ if (res.ok || res.status === 206) return res.json();
1647
+ // A 422 names the one id it rejected ("Invalid mapbox_id format:
1648
+ // <id>"), in case an id format slips past the filter above. Drop
1649
+ // that id and try once more, rather than lose the whole batch.
1650
+ if (res.status !== 422 || retried) return null;
1651
+ return res.json().then(function(body) {
1652
+ var msg = body && typeof body.message === 'string' ? body.message : '';
1653
+ var bad = msg.slice(msg.lastIndexOf(' ') + 1).split('=')[0];
1654
+ var rest = batch.filter(function(id) { return id.split('=')[0] !== bad; });
1655
+ if (!bad || rest.length === batch.length || rest.length === 0) return null;
1656
+ return post(rest, true);
1657
+ });
1658
+ });
1659
+ };
1660
+ return post(compatibleIds.slice(0, 100), false)
1661
+ // Enrichment is a nice-to-have on top of an already-rendered panel —
1662
+ // Public Preview quota (1,000 records/month) or a transient error
1663
+ // here should never surface as a map error, just leave the
1664
+ // Search-Box-only rows as they are.
1665
+ .catch(function() { return null; });
1666
+ }
1667
+
1668
+ // Place Details photos are hosted wherever each business's site hosts
1669
+ // them, so the in-chat CSP (mapAppCsp.ts) can't allow them. Inside a host,
1670
+ // the page asks the server for the photo (PlacePhotoResource, by mapbox_id)
1671
+ // and shows it as a data: URL. A standalone page from export_map_html_tool
1672
+ // has no host and no CSP, so it loads the photo URL directly.
1673
+ var MAX_PANEL_PHOTOS = 10;
1674
+ var EMBEDDED = window.parent !== window;
1675
+ var PHOTO_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
1676
+ var PHOTO_TIMEOUT_MS = 15000;
1677
+
1678
+ function loadPanelPhoto(row, id, photoUrl) {
1679
+ var show = function(src) {
1680
+ // A later render may have replaced the panel while this loaded.
1681
+ if (panelRowsById[id] !== row) return;
1682
+ var thumb = document.createElement('span');
1683
+ thumb.className = 'panel-thumb';
1684
+ thumb.style.backgroundImage = 'url("' + src.replace(/"/g, '') + '")';
1685
+ row.li.appendChild(thumb);
1686
+ };
1687
+ if (!EMBEDDED || !hostAnswered) {
1688
+ // No MCP host (a standalone or framed exported page), so no CSP of
1689
+ // ours either. Load it first: some sites refuse hotlinked photos (403), which
1690
+ // would leave an empty box in the row.
1691
+ if (photoUrl.indexOf('https://') !== 0 || typeof Image !== 'function') return;
1692
+ var img = new Image();
1693
+ img.onload = function() { show(photoUrl); };
1694
+ img.src = photoUrl;
1695
+ return;
1696
+ }
1697
+ // Hosts that don't support resources/read (ChatGPT) may never answer.
1698
+ sendRequest('resources/read', {
1699
+ uri: 'mapbox://place-photo/' + encodeURIComponent(id)
1700
+ }, PHOTO_TIMEOUT_MS).then(function(rr) {
1701
+ var c = rr && Array.isArray(rr.contents) ? rr.contents[0] : null;
1702
+ if (c && typeof c.blob === 'string' &&
1703
+ PHOTO_TYPES.indexOf(c.mimeType) !== -1) {
1704
+ show('data:' + c.mimeType + ';base64,' + c.blob);
1705
+ }
1706
+ }, function() {});
1707
+ }
1708
+
1709
+ function enrichSidePanel(detailsResult) {
1710
+ if (!detailsResult || !Array.isArray(detailsResult.results)) return;
1711
+ var photosRequested = 0;
1712
+ detailsResult.results.forEach(function(place) {
1713
+ if (!place || !place.mapbox_id) return;
1714
+ var row = panelRowsById[place.mapbox_id];
1715
+ if (row) {
1716
+ var photoUrl = place.photos && place.photos[0] && place.photos[0].url;
1717
+ if (typeof photoUrl === 'string' && photosRequested < MAX_PANEL_PHOTOS) {
1718
+ photosRequested++;
1719
+ loadPanelPhoto(row, place.mapbox_id, photoUrl);
1720
+ }
1721
+ if (place.score && typeof place.score.popularity === 'number') {
1722
+ var extra = Math.round(place.score.popularity * 100) + '% popularity';
1723
+ row.meta.textContent = row.meta.textContent ? row.meta.textContent + ' · ' + extra : extra;
1724
+ }
1725
+ }
1726
+
1727
+ var entry = markersById[place.mapbox_id];
1728
+ if (entry && place.phone) {
1729
+ entry.marker.setPopup(
1730
+ new mapboxgl.Popup().setText(entry.popupText + ' — ' + place.phone)
1731
+ );
1732
+ }
1733
+ });
1734
+ }
1735
+
1269
1736
  function selfFetchSearch(params) {
1270
1737
  if (!TOKEN || !isSafeSearchParams(params)) {
1271
1738
  showError('Could not fetch search results: missing token or invalid parameters.');
@@ -1595,11 +2062,15 @@ ${initialDataScript}
1595
2062
  parts.push(Math.round(poi.distance_meters) + ' m');
1596
2063
  }
1597
2064
  markers.push({
2065
+ id: poi.id,
1598
2066
  coordinates: [poi.longitude, poi.latitude],
1599
2067
  style: 'numbered',
1600
2068
  label: String(i + 1),
1601
2069
  color: '#f97316',
1602
- popup: parts.join(' — ')
2070
+ popup: parts.join(' — '),
2071
+ name: poi.name,
2072
+ category: poi.category,
2073
+ distanceMeters: typeof poi.distance_meters === 'number' ? poi.distance_meters : undefined
1603
2074
  });
1604
2075
  });
1605
2076
  return { summary: place, layers: [], markers: markers };
@@ -1658,8 +2129,10 @@ ${initialDataScript}
1658
2129
  var props = f.properties || {};
1659
2130
  var coords = (f.geometry && f.geometry.coordinates) || [params.longitude, params.latitude];
1660
2131
  return {
2132
+ id: props.mapbox_id,
1661
2133
  name: props.name || 'Unknown',
1662
2134
  address: props.full_address || props.place_formatted,
2135
+ category: Array.isArray(props.poi_category) ? props.poi_category[0] : undefined,
1663
2136
  longitude: coords[0],
1664
2137
  latitude: coords[1],
1665
2138
  distance_meters: props.distance
@@ -1 +1 @@
1
- {"version":3,"file":"mapAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/mapAppHtml.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mapAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/mapAppHtml.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAIlC;;;;;;;;;GASG;AAEH,yEAAyE;AACzE,wEAAwE;AACxE,6EAA6E;AAC7E,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C,MAAM,UAAU,gBAAgB,CAAC,MAKhC;IACC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,yBAAyB,CAAC;IACpE,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyF3D,iBAAiB;;;;gBAIH,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;uBACpB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAm/D1C,CAAC;AACT,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CategoryListTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAiBjF;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,kBAAkB,CACtD,OAAO,uBAAuB,EAC9B,OAAO,0BAA0B,CAClC;IACC,IAAI,SAAwB;IAC5B,WAAW,SAC0iB;IACrjB,WAAW;;;;;;MAMT;gBAEU,MAAM,EAAE;QAAE,WAAW,EAAE,WAAW,CAAA;KAAE;cAQhC,OAAO,CACrB,KAAK,EAAE,iBAAiB,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC;CAmE3B"}
1
+ {"version":3,"file":"CategoryListTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAiBjF;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,kBAAkB,CACtD,OAAO,uBAAuB,EAC9B,OAAO,0BAA0B,CAClC;IACC,IAAI,SAAwB;IAC5B,WAAW,SACgY;IAC3Y,WAAW;;;;;;MAMT;gBAEU,MAAM,EAAE;QAAE,WAAW,EAAE,WAAW,CAAA;KAAE;cAQhC,OAAO,CACrB,KAAK,EAAE,iBAAiB,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC;CAmE3B"}
@@ -9,7 +9,7 @@ export const CategoryListInputSchema = z.object({
9
9
  .min(1)
10
10
  .max(100)
11
11
  .optional()
12
- .describe('Number of categories to return (1-100). WARNING: Only use this parameter if you need to optimize token usage. If using pagination, please make multiple calls to retrieve all categories before proceeding with other tasks. If not specified, returns all categories.'),
12
+ .describe('Number of categories to return (1-100). If not specified, returns all categories.'),
13
13
  offset: z
14
14
  .number()
15
15
  .min(0)
@@ -1 +1 @@
1
- {"version":3,"file":"CategoryListTool.input.schema.js","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.input.schema.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAElC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,wQAAwQ,CACzQ;IACH,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,4DAA4D,CAAC;CAC1E,CAAC,CAAC"}
1
+ {"version":3,"file":"CategoryListTool.input.schema.js","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.input.schema.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAElC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,mFAAmF,CACpF;IACH,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,4DAA4D,CAAC;CAC1E,CAAC,CAAC"}
@@ -9,7 +9,7 @@ import { CategoryListResponseSchema } from './CategoryListTool.output.schema.js'
9
9
  */
10
10
  export class CategoryListTool extends MapboxApiBasedTool {
11
11
  name = 'category_list_tool';
12
- description = '[DEPRECATED: Use resource_reader_tool with "mapbox://categories" URI instead] Tool for retrieving the list of supported categories from Mapbox Search API. This tool is kept for backward compatibility with clients that do not support MCP resources. Use this when another function requires a list of categories. Returns all available category IDs by default. Only use pagination (limit/offset) if token usage optimization is required. If using pagination, make multiple calls to retrieve ALL categories before proceeding with other tasks to ensure complete data.';
12
+ description = '[DEPRECATED: Use resource_reader_tool with "mapbox://categories" URI instead] Tool for retrieving the list of supported categories from Mapbox Search API. This tool is kept for backward compatibility with clients that do not support MCP resources. Use this when another function requires a list of categories. Returns all available category IDs by default. Supports limit/offset pagination.';
13
13
  annotations = {
14
14
  title: 'Category List Tool (Deprecated)',
15
15
  readOnlyHint: true,