Repository navigation
feat(tools): add export_map_html_tool for maps in agent-built pages - #275
Merged
Merged
Conversation
render_map_tool only renders inside the chat, so an agent building a site had to hand-write Mapbox GL JS and ask the user for a public token. The new tool takes render_map_tool's input and returns a ~3 KB standalone HTML page: map data and a public token in JSON tags, plus the shared map script loaded from jsDelivr, pinned by package version and sha384 integrity hash. The in-chat page is split into mapAppShell() and mapAppScript() with byte-identical output; the build writes a standalone copy of the script to dist/map-app/standalone.js. Payload assembly moves to assembleMapPayload() so both tools share it. Server instructions point page building at the new tool when it is enabled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mattpodwysocki
added a commit
that referenced
this pull request
Oct 13, 2026
- EVAL_PROVIDER=openai runs the same cases through the Responses API, with server-side tool search for deferred mode (eval:tools:openai). - Each stubbed tool result gets its own ref, so an invented or hand-built render_map_tool call is caught by ref_ok. - Two site-building cases add a write_file host tool and expect export_map_html_tool (#275); needs_tools lets them fail cleanly on servers without it. - New no_token_ask metric flags asking the user for a Mapbox token or writing a placeholder token into a file. - EVAL_MODES selects eager/deferred; rescore reads EVAL_FLOW. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 tasks done
ctufts
reviewed
Oct 13, 2026
| ].filter(Boolean); | ||
|
|
||
| const structured: ExportMapHtmlOutput = { | ||
| html, |
Contributor
There was a problem hiding this comment.
Is there any potential size issue:
render_map_tool respects MAX_INLINE_PAYLOAD_BYTES, but this tool puts the whole merged payload into the HTML without a limit. That includes geometry resolved from refs, such as union/buffer results and large GeoJSON. The HTML is also returned twice, once in the text content and once in structuredContent.html (ExportMapHtmlTool.ts:184 and :171).
Contributor
Author
There was a problem hiding this comment.
Good catch, both fixed in 69af1e0.
- Map data over 200 KB now gets rejected with next steps (run big polygons/lines through simplify_tool, drop stuff the page doesn't need, or split it across maps). render_map_tool can fall back to a stored ref when it's over its limit, but this page has to carry its data and the agent copies the whole thing into a file write, so a hard cap made more sense here.
- The HTML is only in the text content now. structuredContent just has bytes, counts, token source and script URL, so it's not sent to the model twice.
Added tests for both.
The page has to carry its data, with no server-side ref to fall back on like render_map_tool, and the agent copies the whole page into a file write. Reject map data over 200 KB with next steps (simplify_tool, drop unneeded features, split across maps). The HTML was also in both the text content and structuredContent.html, doubling the tokens sent to the model. structuredContent now has only the size and metadata. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mattpodwysocki
added a commit
that referenced
this pull request
Oct 13, 2026
…274) * feat(evals): add tool-selection eval for description routing regressions Runs 17 prompts against Claude and grades which tools it calls against expected trajectories, with all descriptions loaded and again behind the API's tool search. The tool surface comes from a real McpServer read back over MCP; tool results are canned per case, so no Mapbox calls are made. EVAL_SERVER_ROOT compares another checkout (e.g. a PR worktree). Runner and lite report builder are vendored from the claude-api skill's eval scaffold. Adds @anthropic-ai/sdk and tsx as dev dependencies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): add PR number for tool-selection eval Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(evals): add OpenAI provider, site-building cases and token check - EVAL_PROVIDER=openai runs the same cases through the Responses API, with server-side tool search for deferred mode (eval:tools:openai). - Each stubbed tool result gets its own ref, so an invented or hand-built render_map_tool call is caught by ref_ok. - Two site-building cases add a write_file host tool and expect export_map_html_tool (#275); needs_tools lets them fail cleanly on servers without it. - New no_token_ask metric flags asking the user for a Mapbox token or writing a placeholder token into a file. - EVAL_MODES selects eager/deferred; rescore reads EVAL_FLOW. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tool # Conflicts: # CHANGELOG.md
ctufts
approved these changes
Oct 14, 2026
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.
Why
render_map_toolonly renders inside the chat, as an MCP App. When an agent is building a site or file that needs a map (ChatGPT/Codex in the report), it had nothing to put in the page, so it wrote its own Mapbox GL JS code and asked the user for apk.token or leftYOUR_MAPBOX_ACCESS_TOKENin the file.What
New
export_map_html_tool. It takes the same input asrender_map_tool(markers, layers,payload_refs) and returns a complete standalone HTML page of about 3 KB.@mapbox/mcp-server@<version>/dist/map-app/standalone.jswith a sha384 integrity hash. Keeping it small matters because the agent copies the whole page into a file write; inlining the ~60 KB script would mean re-emitting ~16k tokens.public_tokenif passed (must bepk.). The result recommends a URL-restricted token for published sites.mapAppShell()andmapAppScript()(output byte-for-byte identical), andrender_map_tool's ref resolution and payload merging moved toassembleMapPayload()so both tools share it.dist/map-app/standalone.js, which ships in the npm package. When running from source, where the version reads0.0.0, the tool inlines the script instead of linking to a URL that would 404.Eval
This uses the tool-selection eval from #274, with two new site-building cases: the agent has a
write_filehost tool and is asked to build a page with walksheds or a route. A run passes when the agent callsexport_map_html_tooland then writes the file. A separate check looks for any token ask or placeholder token. Three reps each:render_map_tool.Testing
npm test: 995 pass, including new tests for:dist/map-app/standalone.jsmatches what the tool writes into pages, andnpm packincludes the file.Before release
A page from version X loads the map script from
@mapbox/mcp-server@Xon jsDelivr. The hosted server needs to run a version that's already been published to npm, or new pages won't load their map until it is.🤖 Generated with Claude Code