Skip to content

refactor: render union/intersect/difference tools as live Mapbox GL JS maps#195

Closed
mattpodwysocki wants to merge 6 commits into
feat/ground-location-app-toolfrom
feat/polygon-ops-app-tools
Closed

refactor: render union/intersect/difference tools as live Mapbox GL JS maps#195
mattpodwysocki wants to merge 6 commits into
feat/ground-location-app-toolfrom
feat/polygon-ops-app-tools

Conversation

@mattpodwysocki
Copy link
Copy Markdown
Contributor

@mattpodwysocki mattpodwysocki commented May 27, 2026

Summary

Fold MCP App rendering directly into the existing offline polygon-op tools
instead of shipping parallel *_app_tool siblings. Same pattern applied
across #189, #190, #191, #192, #193, #194 — and now the polygon ops finish the stack.

union_tool, intersect_tool, and difference_tool now:

  • Advertise _meta.ui.resourceUri pointing to a single shared MCP App
    resource (ui://mapbox/polygon-ops-app/index.html). One iframe template
    serves all three operations — coloring is keyed on payload.operation.
  • Emit an inline MCP-UI rawHtml createUIResource block when
    isMcpUiEnabled() is true, with both inputs and the result baked in.
  • Share one renderPolygonOpsAppHtml module so the MCP Apps and MCP-UI
    paths render identical output.

Color key

  • union: green result fill
  • intersect: purple result fill
  • difference: orange result fill

Input polygons render in muted blue underneath; a small bottom-left
legend swaps in the right swatch and label per operation.

Token resolution

Polygon ops are fully offline (no API calls, no per-request access token
plumbed through). The inline UI path therefore uses MAPBOX_PUBLIC_TOKEN
from env directly rather than calling the Tokens API — if it isn't set,
no inline UI is emitted but the tool still returns its text + structured
content. The MCP Apps resource path goes through the normal
resolveMapboxPublicToken chain via its constructor-injected
httpRequest.

Graceful no-result

When intersect or difference produces no overlapping geometry, the
tool still returns isError: false — the agent gets a clear text
summary and the UI shows just the input polygons, making the empty case
visually obvious.

Test plan

  • Build, lint, format all clean
  • Full suite: 717 tests passing across 60 files
  • Polygon ops tools still extend offline BaseTool — no constructor
    signature changes, no new dependency injection
  • PolygonOpsAppUIResource slimmed to ~70 lines using shared render

🤖 Generated with Claude Code

Seventh-through-ninth tools in the MCP Apps series. Three Turf.js-based
offline polygon-operation tools share a single PolygonOpsAppUIResource
that renders the input polygons in muted blue and overlays the result
in an operation-keyed color (green=union, purple=intersect,
orange=difference).

When intersect/difference produces no overlapping geometry, the tool
still succeeds (isError=false) and the UI shows just the inputs with a
"no overlapping geometry" summary.

Also adds the three new offline tools to the annotations test's
offlineTools list so the open-world-hint test continues to pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mattpodwysocki mattpodwysocki requested a review from a team as a code owner May 27, 2026 15:30
mattpodwysocki and others added 2 commits May 27, 2026 11:45
Final resource in the series. Same pattern: send size-changed first,
delay 60ms, then map.resize() + fitBounds so polygons fill the
viewport instead of appearing as tiny dots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mattpodwysocki and others added 2 commits June 1, 2026 16:35
…p-tools

# Conflicts:
#	CHANGELOG.md
#	src/tools/index.ts
#	src/tools/toolRegistry.ts
Polygon ops now render as a live Mapbox GL JS map directly from the
existing offline tools instead of via parallel `*_app_tool` siblings.

- Extract the rendering pipeline into a shared `renderPolygonOpsAppHtml`
  module so the MCP Apps resource and the inline MCP-UI rawHtml block use
  one template and one source of truth.
- Slim down `PolygonOpsAppUIResource` to a thin wrapper around the shared
  render function (only the empty/postMessage variant).
- Add `_meta.ui.resourceUri` to `union_tool`, `intersect_tool`, and
  `difference_tool` pointing to the shared `ui://mapbox/polygon-ops-app/
  index.html` resource (MCP Apps path).
- Gate inline MCP-UI rawHtml on `isMcpUiEnabled()` and bake both inputs
  and the result polygon into the iframe with operation-keyed result
  coloring (green=union, purple=intersect, orange=difference).
- Polygon ops are fully offline, so the inline path uses
  `MAPBOX_PUBLIC_TOKEN` from env directly rather than the tokens API.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@mattpodwysocki mattpodwysocki changed the title feat: union/intersect/difference app tools — polygon ops on a map refactor: render union/intersect/difference tools as live Mapbox GL JS maps Jun 1, 2026
@mattpodwysocki mattpodwysocki marked this pull request as draft June 1, 2026 20:43
@mattpodwysocki
Copy link
Copy Markdown
Contributor Author

Superseded by #199.

This PR (and the rest of the per-tool app stack: #190, #191, #192, #193, #194, #195) declares its own meta.ui.resourceUri and emits inline createUIResource via @mcp-ui/server. In practice that approach has two blockers:

  1. Chained tool calls only render the last iframe. Claude Desktop dedupes/short-circuits earlier iframes in a chain, so geocode→directions or isochrone→union flows only paint the final tool's map.
  2. Depends on @mcp-ui/server, which feat: render_map_tool — single visualization primitive (server-side refs) #199 removes.

#199 funnels all rendering through one terminal render_map_tool primitive. Every data tool stores a MapAppPayload server-side and returns a mapbox://temp/map-payload-<uuid> ref; the LLM passes the refs to render_map_tool, which merges them and renders. Closing in favor of that approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant