We will be undergoing planned maintenance on Oct 7th 6:00AM UTC / Oct 7th 2:00AM ET

What it is

When the Grand River comes up, help is a group chat: forty unread messages, a spreadsheet nobody updates, and a volunteer with a truck who doesn't know where to go. Highwater is one map. Requests and volunteers are pins. The coordinator drags pins, bumps urgency and marks things done by hand. The dispatch agent reads the same board through WebMCP, logs what comes in, matches the nearest suitable volunteer, draws the dispatch line, and broadcasts what's still unmet.

Live: https://highwater-six.vercel.app · Code: https://github.com/chinesepowered/highwater

Why this is a strong fit for WebMCP

Dispatch is a shared-state problem. An agent that only sees a screenshot of a map cannot know that the coordinator just moved Jamal's truck to the far side of the closed bridge. Highwater exposes the board as eleven structured tools registered with document.modelContext.registerTool:

get_situation, get_stats, add_request, add_offer, find_volunteers_for, match, unmatch, update_request, update_volunteer, broadcast, focus_map

add_request accepts a street name and places it on the map through a small gazetteer, so the agent can work from raw text messages. find_volunteers_for returns capable volunteers nearest-first with distance in km and availability, and match draws the dispatch line. Every human edit (a dragged pin, an urgency click) changes the same model the tools read; every agent edit lands in the event feed with a 🤖 mark.

How it creates a better experience

  • Paste, don't transcribe. Three incoming texts become two requests, one volunteer, two matches, dispatch lines on the map, and a broadcast for what is still unmet. Eight tool calls, one paste.
  • Human corrections are first-class. Drag Priya's boat across the river because the bridge closed, say "Priya is stuck", and the agent re-reads the board, sees her new position, releases her, and re-matches the seniors' residence to the accessible van. The map flies to it with focus_map.
  • Triage you can see. Urgency 1–5 colours the pins; open-and-urgent requests sort to the top; stats show people waiting and volunteers free.

What people and agents can do together that was hard before

Before: one exhausted coordinator reading messages aloud while someone else types into a sheet, and matching by memory. Now: the coordinator keeps judgment and local knowledge (which bridge is closed, who is reliable) and expresses it by moving pins and setting urgency; the agent keeps the bookkeeping and the distance math, and never acts on stale state because it reads the board before every action.

How WebMCP is implemented

  • lib/webmcp.ts: registry that calls document.modelContext.registerTool(..., { signal }) for each tool and unregisters via AbortController on unmount. Native browser-agent calls and in-page agent calls share one execute path and one activity log.
  • lib/tools.ts: the eleven tools with JSON Schema inputs and read-only / destructive annotations.
  • lib/aid.ts: the shared store (requests, offers, events, map focus), haversine distances, the street gazetteer, and match/unmatch bookkeeping. Dragging a Leaflet marker calls the same store the tools mutate.
  • components/AidMap.tsx: react-leaflet map with draggable pins, dispatch polylines, and a fly-to controller driven by focus_map.
  • components/AgentPanel.tsx + app/api/agent/route.ts: a Gemini function-calling loop that discovers the registered tools and dispatches through WebMCP, with a model fallback chain for free-tier limits.

Tested in Chrome 153 with chrome://flags/#enable-webmcp-testing (DevTools → Application → WebMCP lists all eleven tools) and with the in-page Gemini dispatch agent.

Fictional scenario and people; not an emergency service.

Stack

Next.js 16 · Leaflet + react-leaflet (CARTO / OpenStreetMap tiles) · Tailwind 4 · Gemini function calling · WebMCP · Vercel

Built With

Share this project:

Updates

Submission history