placeroot
WHY WE BUILT THIS

Why we built PlaceRoot

Agents are bad at maps: an LLM has no ground truth for what exists where today. That gap doesn't get fixed by giving the agent a bigger map API — most of what's on offer either locks the answer behind a key and a bill, or hands back so much raw data that the agent can't afford to read it.

An agent asks a simple spatial question — "what's within 500 meters of this point?" — and, with a typical map API, the answer comes back as a GeoJSON FeatureCollection. Every feature carries a full geometry, a nested sources array with per-property provenance and confidence, a names struct, a categories struct, contact fields, and more. Multiply that by a few hundred features, a normal count for a dense downtown block, and the response no longer fits in a reasonable slice of a context window. The agent either truncates blindly, burns most of its budget on one tool call, or the integration just doesn't ship.

That's the payload half of the problem, and it's well enough known now to have a name in public, from a company that ships production geospatial infrastructure: Mapbox's "GeoAI in 2026" post calls large geospatial payloads the biggest challenge for MCP-based mapping integrations. A Geoawesome essay from June 2026 puts a number on the shape of it: a real-world GeoJSON payload of roughly 45,000 tokens collapses to about 25 tokens once you return a reference instead of the geometry itself. That gap is most map tooling's original sin — it was built for GIS software and browsers, which have no token budget, not for a model that does.

The other half is quieter and, for an agent, more dangerous: it isn't a data problem so much as a confidence problem. Ask a model where the nearest coffee shop to a given address is, or which of three cities named Cambridge a landmark sits in, and it will answer — fluently, specifically, and with no signal to the caller that it's guessing. A language model's spatial knowledge is frozen at training time and was never grounded in a coordinate system to begin with; it knows the Eiffel Tower is a famous landmark in Paris because that sentence appeared enough times in its training data, not because it consulted a map. Ask it something that wasn't in the training data — which businesses are open on this block right now — and it will still answer, just as fluently, and just as wrong.

Two structural camps

Look at what's available today for grounding an agent in map data, and it splits into two camps, each solving a different half of the problem and ignoring the other.

The first camp is vendor-keyed APIs. You get an API key, you get billing, and in exchange you get curated, often well-ranked place data — sometimes even AI-generated summaries. This solves the payload problem reasonably well, because the vendor controls the response shape. What it doesn't solve is the dependency: your agent's map grounding now lives behind a signup flow, a metered price per call, and someone else's terms of service on caching and training. One current example: Google's Maps Grounding Lite is priced at $14–25 per 1,000 prompts, with no-caching and no-training terms attached — a cost structure built for a per-seat SaaS product, not for an agent that might fire off a hundred spatial lookups planning a single trip.

The second camp is open-data wrappers, most commonly built on OpenStreetMap via Nominatim or Overpass. These are keyless, which solves the dependency problem — but most of them are thin passthroughs: you get raw OSM tags or raw GeoJSON back, at whatever size the underlying query produces. The payload problem is unsolved, just moved from a paid API to a free one. You've traded a metered key for a token budget that blows up on any moderately dense area.

Neither camp treats "fits in an agent's context window" as a first-class design constraint, and neither treats "what the model already knows" as an input worth asking for. A vendor API takes a query string and returns whatever it returns; it has no field for "the agent already knows this is near the Space Needle, it just needs to know what's there." An open-data wrapper takes the same query string and returns even more of it, unfiltered. Both leave the actual division of labor — the model knows the world, the map knows the data — unaddressed, and the caller pays for that omission in tokens, in dollars, or in a wrong answer stated as fact.

The seam

PlaceRoot queries Overture Maps' public GeoParquet release directly with DuckDB — no API key, no signup, no intermediate database, no ETL job. Row-group pushdown on the bbox columns means a query only reads the tiles it needs. That gets the right data quickly. It doesn't, by itself, make the data small — a raw Overture Places record still serializes to a few hundred tokens per feature once you include the nested sources, categories, names, and address structs. Making the payload problem disappear is a separate design rule, applied to every tool: return an answer, not a data dump. Every response is built to a token budget of roughly 2,000 tokens, and when a result would exceed it, rows are dropped nearest-first-preserved and optional fields stripped, with truncated: true and an explicit omitted_count rather than a silent cut. Measured against the committed benchmark fixtures: summarize_area answers a "what's in this area" question in 113 tokens; find_places returning ten ranked, deduplicated coffee shops within a kilometer comes back at 702 tokens. Registering all 29 tools costs 13,438 tokens of schema surface before a single question is asked — that's the other cost, paid once per conversation whether or not the agent ever calls anything, and it's roughly 103 median answers' worth. An install that only needs geocoding and place search can load a narrower tool profile instead and cut that same surface by more than half.

The confidence problem gets its own fix, and it's a narrower one than it sounds: separate what the model knows from what needs to be looked up, and say so in the tool's own instructions. PlaceRoot's server instructions put this plainly — "You know things it cannot: that 'san jose airport' means San Jose, California, that the Eiffel Tower is in Paris. This server knows only what exists at which coordinates in the current Overture release." A caller that sends one combined string — "coffee near the space needle" as a single search term — forces the server to guess which words are the place and which are the thing being looked for, guessing from map data alone, where a fragment like "san" names a division in Henan as readily as a city in California. Passing the location separately (a city name, or coordinates when the agent already has them) means a wrong hint costs a retry, never a wrong answer — every row still comes from the data, not from a guess about what the query string meant.

Surviving how people actually type

None of this is theoretical, because none of it stays fixed on its own. A 148-question corpus, run cold — fresh process, empty cache, every query — across 40-plus cities is what actually exercises it, and a second sweep of 24 fresh phrasings the way people actually type ("coffe," "seatle," "farmacy," "stuff to eat near the space needle," "the bean chicago") turned up five failures with four distinct causes, each now a fix and a regression test:

The corpus methodology has one rule behind all of it: a fast wrong answer scores as a win unless something actually checks the answer. The clearest example on record is a plain geocode lookup — asking for "Casablanca" once returned coordinates in Chile, in 0.2 seconds, faster and more confident-looking than the correct answer would have been. Cold latency without a correctness check would have called that a success. With the fix and the regression test in place, the corpus runs 24 of 24 correct — up from 19 — with every single tool call answering cold, from an empty cache, in 4.6 to 9.8 seconds.

What it doesn't do

Some honest limits, not smoothed over:

Try it

The whole install is one block of JSON and no account:

mcp_config.json
{
  "mcpServers": {
    "placeroot": {
      "command": "uvx",
      "args": ["placeroot"]
    }
  }
}

Or run it directly with uvx placeroot. Source, the full tool catalog, and the design rules behind them: github.com/chuofringer/placeroot. The numbers in this piece come from docs/benchmarks.md and the query corpus in benchmarks/ — regenerated from the code, never hand-edited.

Add to your AI — free

Contact

Developed by vibemapper · Privacy