Skip to content

Feature request: use MCP tasks extension to stream ground_location_tool results #197

Closed

Description

@mattpodwysocki

Summary

Convert ground_location_tool to use the MCP tasks extension (SEP-1686, official extension in the 2026-07-28 RC) so the agent gets the cheap, deterministic pieces (place name) immediately while the slower pieces (sampling-classified POI search, isochrone) stream in as they resolve.

Why

ground_location_tool today does roughly:

  1. (Optional) MCP sampling to classify the grounding strategy as routing / neighborhood / poi / region — round-trip to the client's model, variable latency, can take several hundred ms
  2. Parallel fan-out of three Mapbox API calls: reverse geocode, category search (if query), isochrone
  3. Block until all three return
  4. Format + return as one text payload

Steps 2 and 3 are individually quick (each <1s typically) but the sampling step in 1 makes the whole call feel sluggish, and the user sees nothing until everything is done — even though the reverse-geocoded place name is usually available in ~100ms.

A task-based shape lets the server:

  • Return a task handle from tools/call immediately
  • Resolve the reverse-geocode and surface it as the first task update (the agent now has the place name + coords)
  • Resolve the sampling classification in parallel
  • Surface POI and isochrone results as additional updates as they complete
  • Final task state has the formatted summary

For an agent that just needs "what place is this?", it can read the first update and move on — no need to wait for the isochrone. For a user that wants the full grounded context, they get it incrementally rather than as one delayed blob.

Less obvious benefit

The current PR #173 implementation has a fallback path when the client doesn't support sampling: it defaults to neighborhood strategy. A task-based version makes this fallback more graceful — the task can declare immediately what strategies it ran with and what's pending, instead of either blocking on sampling or silently downgrading.

Compatibility

Like the MTS proposal in #196, clients that don't advertise the tasks extension would just get the current behavior: synchronous call, full result at the end. The task path is purely additive.

Implementation notes

  • MapboxApiBasedTool.execute() would need a sibling like executeTask() that returns an in-progress task with hooks to update state — exact shape depends on the final task extension spec (still experimental)
  • Progress updates carry the same structuredContent shape we already return, just incrementally — UI surfaces (Claude Desktop, etc.) that want to render the streaming state can do so without us defining a custom progress contract
  • This would be a good pilot for the tasks extension before tackling the longer-running MTS workflows in Feature request: Mapbox Tiling Service (MTS) tools, leveraging the new MCP tasks extension #196, since the failure mode here is gentle (worst case: same behavior as today)

Related

Activity

  1. rehan243 commented on Jun 10, 2026

    @rehan243

    oh interesting, we've actually hacked something similar with MCP tasks for streaming slow POI lookups — totally agree: blocking on the sampling step kills UX, especially when reverse-geocode is ready way faster. What worked for us was firing off the initial MCP task like:

    task = mcp.create_task("ground_location", input)
    task.update({"place_name": fast_reverse_geocode(input['coords'])})
    # Then async for the rest
    sampling = await classify_grounding(input)
    task.update({"sampling_classification": sampling})
    poi = await search_poi(input)
    isochrone = await compute_isochrone(input)
    task.update({"poi": poi, "isochrone": isochrone})

    We ended up using a combo of asyncio and task.update() to stream partial results — not gonna lie, took a few tries to get the agent to handle mid-task updates cleanly. Are you on the 2026-07-28 RC or earlier? Curious if you've got anything weird happening with task update ordering, since our POI search sometimes beats sampling (which feels odd UX-wise).

  2. mattpodwysocki commented on Jun 10, 2026

    @mattpodwysocki
    ContributorAuthor

    Picking this up in draft PR #210. The MCP SDK 1.29.0 already ships server.experimental.tasks.registerToolTask so we don't need to wait for the 2026-07-28 RC to start building against it.

    What's in the draft:

    • ground_location_tool now overrides installTo to use registerToolTask instead of registerTool
    • createTask returns the task handle immediately and kicks off background work without awaiting it
    • Reverse geocode and sampling classification run in parallel in the background, same as before but async -- once sampling resolves, POI search and isochrone fan out based on the strategy
    • InMemoryTaskStore wired into the server so task lifecycle is managed

    Two known gaps before this can ship: taskSupport: 'required' currently breaks clients without task support, and the in-memory store loses state on restart. Both are intentional for now since this is a design spike. The taskSupport: 'supported' fallback path should be clearer once the RC lands in July.

    One thing worth noting from the SDK branch: the valid task statuses are working, input_required, completed, failed, cancelled -- there is no running state, tasks start as working already. Tripped over that while writing the tests.

  3. rehan243 commented on Jun 10, 2026

    @rehan243

    oh nice, didn’t realize 1.29.0 already had registerToolTask baked in — that should speed things up a lot. yeah, the taskSupport: 'required' issue is a pain; we hit that on our end too and ended up adding a fallback for legacy clients. curious if you’re planning to handle the partial support case in the next patch or workaround it for now? lmk if you want me to share our fallback snippet.

  4. mattpodwysocki commented on Jun 11, 2026

    @mattpodwysocki
    ContributorAuthor

    yes please share it, that would be really helpful. the plan is to tackle the fallback in a follow-up once we see how the RC shapes the supported path, but having a working reference for the inline execution case would save some guesswork.

  5. mattpodwysocki commented on Jun 11, 2026

    @mattpodwysocki
    ContributorAuthor

    went ahead and pushed the fallback in the same draft PR (#210) -- curious if this is along the lines of what you had. ended up using taskSupport: 'optional' which the SDK already handles automatically: non-task clients get the same synchronous result as before, the SDK internally creates the task, polls at 50ms intervals, and returns the result directly without the caller ever seeing a task handle.

    the relevant bit in createTask:

    const task = await extra.taskStore.createTask({
      ttl: 60_000,
      pollInterval: 50
    });
    void this.runTaskBackground(args, accessToken, task.taskId, extra.taskStore);
    return { task };

    and in runTaskBackground, sampling and the initial geocode kick off in parallel, then POI search and isochrone fan out once the strategy resolves:

    const [strategy, initialGeocode] = await Promise.all([
      this.classifyGroundingStrategy(query, longitude, latitude),
      this.reverseGeocode(longitude, latitude, accessToken, 'neighborhood,locality,place', language)
    ]);
    // refine geocode types if needed, then fan out POI + isochrone
    const [poisResult, isochroneResult] = await Promise.all([...]);
    await taskStore.storeTaskResult(taskId, 'completed', { ... });

    no custom fallback code needed since the SDK automatic polling path handles it. still keen to see what your snippet looks like though, especially if you solved the update ordering issue differently.

  6. rehan243 commented on Jun 15, 2026

    @rehan243
  7. Valiunia commented on Jun 23, 2026

    @Valiunia
    Contributor

    closing in favour of internal ticket

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions