map-server: interactive annotations (markers, routes, areas, circles)#505
Open
map-server: interactive annotations (markers, routes, areas, circles)#505
Conversation
@modelcontextprotocol/ext-apps
@modelcontextprotocol/server-basic-preact
@modelcontextprotocol/server-basic-react
@modelcontextprotocol/server-basic-solid
@modelcontextprotocol/server-basic-svelte
@modelcontextprotocol/server-basic-vanillajs
@modelcontextprotocol/server-basic-vue
@modelcontextprotocol/server-budget-allocator
@modelcontextprotocol/server-cohort-heatmap
@modelcontextprotocol/server-customer-segmentation
@modelcontextprotocol/server-debug
@modelcontextprotocol/server-map
@modelcontextprotocol/server-pdf
@modelcontextprotocol/server-scenario-modeler
@modelcontextprotocol/server-shadertoy
@modelcontextprotocol/server-sheet-music
@modelcontextprotocol/server-system-monitor
@modelcontextprotocol/server-threejs
@modelcontextprotocol/server-transcript
@modelcontextprotocol/server-video-resource
@modelcontextprotocol/server-wiki-explorer
commit: |
Add semi-transparent dark background behind labels, use bold font, thicker outline, and disable depth test so labels are always visible. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change add_marker to add_markers accepting an array of markers - Assign UUIDs to each marker and return them in the tool result - Add update_markers and remove_markers actions to the interact tool - Track markers by ID with Cesium entity references for edit/removal - Add copy button (clipboard icon) that appears when markers exist - Multi-mime clipboard: text/plain gets Markdown table + GeoJSON code block, text/html gets an HTML table with GeoJSON in a details block Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Geocode tool now accepts `queries` (string array) and performs batched lookups (respecting Nominatim rate limit between each) - Show-map accepts initial `markers` array — IDs are assigned server-side and returned in structuredContent - Show-map supports center+radius mode: `latitude`/`longitude` with optional `radiusKm` (default 50) as alternative to bounding box - App handles initial markers from both ontoolinput and ontoolresult Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
structuredContent is only seen by the model when content is empty (per MCP spec). Move initial marker data to _meta for the app, and include marker IDs in the content text for the model. Also remove structuredContent from add_markers (IDs already in text). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Marker id is now required and chosen by the caller on show-map (initial markers), add_markers, and update_markers. Removes sequential counter and server-side ID allocation — simpler and produces shorter, more meaningful IDs in the conversation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Cesium's built-in label with canvas-rendered billboard images:
- Proper vertical text alignment (textBaseline: middle)
- Rounded rectangle background (quadraticCurveTo corners)
- System font stack, DPR-aware sizing
- Dark semi-transparent background (rgba 30,30,30,0.78)
Persist markers to localStorage keyed by `${viewUUID}:markers`.
Restored on reconnect before adding any new initial markers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The model already knows the IDs it picked — no need to repeat them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Split marker into two Cesium entities (point + billboard label) to avoid point/billboard conflict that suppressed labels entirely - Remove heightReference: CLAMP_TO_GROUND (no terrain loaded, caused 3D offset on tilted views) - Use canvas roundRect() for cleaner rounded corners - Use actualBoundingBoxAscent/Descent for precise text centering - Add hint in show-map description: skip markers for single location - Update/remove now correctly handles both entities Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Markers from ontoolinput are added before viewUUID is available, so persistMarkers() was a no-op. Now explicitly persist after ontoolresult finishes setting up viewUUID and all markers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move persistMarkers() out of addMarker/updateMarker/removeMarker into processCommands (once after the entire batch). The ontoolresult handler already has its own explicit persistMarkers() call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The app runs in a sandboxed iframe — Clipboard API requires the host to set allow="clipboard-write". Declare it via permissions metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace marker-specific types with a discriminated union (oneOf in JSON Schema) supporting four annotation types: marker, route, area, circle. - z.discriminatedUnion for proper oneOf in tool schemas - Separate full schemas (show-map/add) and update schemas (partial fields) - Unified add/update/remove actions replacing add_markers/update_markers/remove_markers - Cesium rendering: point (marker), polyline (route), polygon (area), ellipse (circle) - Canvas label billboards for all types at appropriate anchor points - ontoolinputpartial streaming: render all-but-last annotation progressively - Persistence and clipboard export updated for all annotation types - GeoJSON export: Point/LineString/Polygon with type-specific properties Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Annotations were added after `await waitForTilesLoaded()` which blocks up to 10 seconds. If the host doesn't forward `_meta.initialAnnotations` to ontoolresult, annotations wouldn't appear until tiles finish loading. Now annotations are added immediately after camera positioning, before the tile-loading await. Also adds field validation in ontoolinputpartial to prevent creating broken entities from truncated streaming data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The camera only moved after the full tool input was received. Now ontoolinputpartial positions the camera as soon as bbox/center fields are available (once), so markers appear in the right location during progressive streaming. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If the camera was positioned during streaming (ontoolinputpartial), skip re-positioning in ontoolinput. This preserves the user's view if they panned or zoomed while annotations were streaming in. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds full interactivity and rich annotation support to the CesiumJS map server:
oneOfin JSON Schema) supporting marker, route (polyline), area (polygon), and circle (ellipse) annotation typesnavigate,add,update,removeactions via command queue with 200ms batchingontoolinputpartialprogressively renders annotations as the model streams them (skipping last potentially-truncated item)Annotation types
markerrouteareacircleJSON Schema
Uses
z.discriminatedUnion("type", [...])which produces properoneOfwithconstdiscriminator values. Separate full schemas (foradd) and update schemas (partial fields, forupdate).Test plan
show-mapwith no annotations → globe loads at default/bbox locationshow-mapwith mixed annotations → markers, routes, areas, circles render correctlyinteractadd/update/remove → annotations appear/change/disappearontoolinputpartial) → annotations render progressively🤖 Generated with Claude Code