Skip to content

Fix static_map_image_tool's hosted stateless follow-up (fast follow to #257) - #258

Open
mattpodwysocki wants to merge 1 commit into
mainfrom
fix/static-map-hosted-stateless-followup
Open

Fix static_map_image_tool's hosted stateless follow-up (fast follow to #257)#258
mattpodwysocki wants to merge 1 commit into
mainfrom
fix/static-map-hosted-stateless-followup

Conversation

@mattpodwysocki

Copy link
Copy Markdown
Contributor

Summary

Fast follow-up to #257, for the one tool deliberately deferred there: static_map_image_tool.

Same bug: images over the 700KB inline threshold were stashed in temporaryResourceManager (an in-process Map, 30-minute TTL) behind a mapbox://temp/static-map-{id} ref. On the hosted deployment's stateless, multi-ECS-task setup with no session stickiness, a follow-up resources/read landing on a different task than the one that fetched the image found nothing.

Why this isn't just "apply #257's fix to this tool too"

#257's mapbox://inline-response/ scheme encodes the tool's full JSON output directly into the ref. That doesn't work for images: base64-encoding raw bytes into a URI blows past the MCP SDK's own hard 1,000,000-character resource-URI cap almost immediately. Confirmed live — a 750KB image (barely over this tool's 700KB threshold) alone encodes to a 1,024,055-character URI, and the SDK rejects the read. Images that actually need this fallback can run into multiple megabytes (e.g. 1280x1280 @2x), so there's no realistic threshold below which encoding bytes directly would be safe.

Fix

The new mapbox://inline-image/static-map?data=... ref encodes only the original request params (center, zoom, style, overlays — a few hundred bytes at most), not the image. resources/read re-issues the Static Images API request at read time, using those params and the reader's own resolved access token. The Static Images API is a deterministic renderer of those params, so this reproduces the exact same image on demand instead of storing it:

  • No size ceiling — the ref is tiny regardless of how large the resulting image is.
  • Works from any task/process — nothing is kept in memory between the original call and the read.
  • Costs one extra Static Images API call per follow-up read, in exchange for correctness.

Extracted buildStaticMapRequestUrl.ts (URL building + overlay encoding, previously inlined in the tool) so both StaticMapImageTool and the new InlineImageResource share the exact same request-construction logic.

Verification

  • test/integration/hostedMultiTaskFollowupStaticMap.test.ts: a real two-process integration test — fetches a large image in one spawned server process, kills that process outright, and re-fetches the identical bytes from a second, completely independent process via a real resources/read call.
  • Confirmed the test is meaningful: stashed the fix, confirmed the test fails against the old mapbox://temp/ code path, then restored the fix and confirmed it passes.
  • npm run build, npx vitest run (951 tests passing), npx eslint --fix on all changed/new files — all clean.
  • CHANGELOG.md updated under Unreleased.

Test plan

  • npm run build
  • npx vitest run — 951 tests passing
  • npx eslint --fix — clean
  • Real two-process integration test proving the cross-task fix, validated against both old and new code paths

🤖 Generated with Claude Code

…ching inline-image ref

Same hosted multi-task bug as directions_tool/isochrone_tool (#257):
static_map_image_tool stashed large images in temporaryResourceManager
(an in-process Map, 30-min TTL) behind a mapbox://temp/static-map-{id}
ref, which a follow-up resources/read landing on a different ECS task
couldn't see.

That fix's JSON-in-ref approach doesn't transfer to images: base64-
encoding raw bytes into a URI blows past the MCP SDK's own 1,000,000-
character resource-URI cap almost immediately (a 750KB image alone
encodes to 1,024,055 characters), and images needing this fallback can
run into multiple megabytes.

Instead, the new mapbox://inline-image/static-map?data=... ref encodes
only the original request params (center, zoom, style, overlays - a
few hundred bytes), and resources/read re-issues the Static Images API
request at read time using those params and the reader's own resolved
access token. The API is a deterministic renderer, so this reproduces
the exact same image on demand instead of storing it - no size
ceiling, works from any task. Extracted buildStaticMapRequestUrl.ts so
the tool and the resource share the same URL-building logic.

Verified with a real two-process integration test (fetch in one
spawned server, kill it, re-fetch identical bytes from a second,
independent process), and confirmed it fails against the old
mapbox://temp/ code path before the fix and passes after.
@mattpodwysocki
mattpodwysocki requested a review from a team as a code owner August 27, 2026 17:50
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