placeroot 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. placeroot-0.3.0/.gitignore +8 -0
  2. placeroot-0.3.0/LICENSE +21 -0
  3. placeroot-0.3.0/PKG-INFO +103 -0
  4. placeroot-0.3.0/PLAN.md +180 -0
  5. placeroot-0.3.0/README.md +78 -0
  6. placeroot-0.3.0/ROADMAP.md +94 -0
  7. placeroot-0.3.0/benchmarks/README.md +116 -0
  8. placeroot-0.3.0/benchmarks/results.md +51 -0
  9. placeroot-0.3.0/benchmarks/token_benchmark.py +583 -0
  10. placeroot-0.3.0/docs/METRICS.md +69 -0
  11. placeroot-0.3.0/docs/MIRROR.md +178 -0
  12. placeroot-0.3.0/docs/launch/outreach.md +66 -0
  13. placeroot-0.3.0/docs/launch/post-why-agents-are-bad-at-maps.md +150 -0
  14. placeroot-0.3.0/docs/launch/registry-submissions.md +128 -0
  15. placeroot-0.3.0/docs/launch/show-hn.md +42 -0
  16. placeroot-0.3.0/examples/site_selection/README.md +178 -0
  17. placeroot-0.3.0/examples/site_selection/run_demo.py +355 -0
  18. placeroot-0.3.0/pyproject.toml +77 -0
  19. placeroot-0.3.0/scripts/build_fixture.py +378 -0
  20. placeroot-0.3.0/scripts/build_geocode_fixture.py +237 -0
  21. placeroot-0.3.0/scripts/build_routing_fixture.py +343 -0
  22. placeroot-0.3.0/scripts/geocode_benchmark.py +184 -0
  23. placeroot-0.3.0/scripts/mirror_theme.py +495 -0
  24. placeroot-0.3.0/site/_headers +13 -0
  25. placeroot-0.3.0/site/demo-map.html +320 -0
  26. placeroot-0.3.0/site/index.html +316 -0
  27. placeroot-0.3.0/site/style.css +319 -0
  28. placeroot-0.3.0/src/placeroot/budget.py +90 -0
  29. placeroot-0.3.0/src/placeroot/buildings.py +395 -0
  30. placeroot-0.3.0/src/placeroot/cache.py +428 -0
  31. placeroot-0.3.0/src/placeroot/db.py +152 -0
  32. placeroot-0.3.0/src/placeroot/divisions.py +129 -0
  33. placeroot-0.3.0/src/placeroot/errors.py +35 -0
  34. placeroot-0.3.0/src/placeroot/geo.py +124 -0
  35. placeroot-0.3.0/src/placeroot/geocode.py +1069 -0
  36. placeroot-0.3.0/src/placeroot/mapview.py +911 -0
  37. placeroot-0.3.0/src/placeroot/overture.py +657 -0
  38. placeroot-0.3.0/src/placeroot/release.py +95 -0
  39. placeroot-0.3.0/src/placeroot/routing.py +1137 -0
  40. placeroot-0.3.0/src/placeroot/server.py +541 -0
  41. placeroot-0.3.0/src/placeroot/simplify.py +325 -0
  42. placeroot-0.3.0/tests/__init__.py +0 -0
  43. placeroot-0.3.0/tests/_geo.py +17 -0
  44. placeroot-0.3.0/tests/_routing_fixture.py +12 -0
  45. placeroot-0.3.0/tests/conftest.py +79 -0
  46. placeroot-0.3.0/tests/fixtures/addresses.parquet +0 -0
  47. placeroot-0.3.0/tests/fixtures/buildings.parquet +0 -0
  48. placeroot-0.3.0/tests/fixtures/division_areas.parquet +0 -0
  49. placeroot-0.3.0/tests/fixtures/divisions.parquet +0 -0
  50. placeroot-0.3.0/tests/fixtures/places.parquet +0 -0
  51. placeroot-0.3.0/tests/fixtures/transportation.parquet +0 -0
  52. placeroot-0.3.0/tests/test_admin_lookup.py +79 -0
  53. placeroot-0.3.0/tests/test_budget.py +90 -0
  54. placeroot-0.3.0/tests/test_buildings.py +213 -0
  55. placeroot-0.3.0/tests/test_cache.py +368 -0
  56. placeroot-0.3.0/tests/test_compare_areas.py +85 -0
  57. placeroot-0.3.0/tests/test_demo.py +54 -0
  58. placeroot-0.3.0/tests/test_find_places.py +83 -0
  59. placeroot-0.3.0/tests/test_geocode.py +363 -0
  60. placeroot-0.3.0/tests/test_geometry.py +121 -0
  61. placeroot-0.3.0/tests/test_http.py +130 -0
  62. placeroot-0.3.0/tests/test_launch_docs.py +138 -0
  63. placeroot-0.3.0/tests/test_live.py +68 -0
  64. placeroot-0.3.0/tests/test_mapview.py +448 -0
  65. placeroot-0.3.0/tests/test_mirror_theme.py +267 -0
  66. placeroot-0.3.0/tests/test_place_details.py +215 -0
  67. placeroot-0.3.0/tests/test_release.py +130 -0
  68. placeroot-0.3.0/tests/test_resilience.py +97 -0
  69. placeroot-0.3.0/tests/test_resolve_place.py +101 -0
  70. placeroot-0.3.0/tests/test_routing.py +503 -0
  71. placeroot-0.3.0/tests/test_security.py +262 -0
  72. placeroot-0.3.0/tests/test_server.py +134 -0
  73. placeroot-0.3.0/tests/test_simplify.py +121 -0
  74. placeroot-0.3.0/tests/test_site.py +99 -0
  75. placeroot-0.3.0/tests/test_summarize_area.py +58 -0
  76. placeroot-0.3.0/tests/test_token_benchmark.py +91 -0
  77. placeroot-0.3.0/tests/test_tool_registry.py +40 -0
  78. placeroot-0.3.0/tests/test_upstream_mirror.py +129 -0
  79. placeroot-0.3.0/tests/test_within_distance.py +53 -0
  80. placeroot-0.3.0/uv.lock +1048 -0
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ .DS_Store
6
+
7
+ # Local Claude Code session state (agent worktrees, etc.) — never shipped.
8
+ .claude/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vibe Mapper
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: placeroot
3
+ Version: 0.3.0
4
+ Summary: Ground AI agents in open map data. MCP server for Overture Maps — compact answers, no API key.
5
+ Project-URL: Homepage, https://placeroot.dev
6
+ Project-URL: Repository, https://github.com/chuofringer/placeroot
7
+ Project-URL: Issues, https://github.com/chuofringer/placeroot/issues
8
+ Author-email: Vibe Mapper <chuo.fringer@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,duckdb,geospatial,mcp,openstreetmap,overture-maps
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: GIS
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: duckdb>=1.5.5
23
+ Requires-Dist: mcp[cli]>=2.0.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # PlaceRoot
27
+
28
+ **Ground AI agents in open map data.**
29
+
30
+ PlaceRoot is an MCP server that answers spatial questions from [Overture Maps](https://overturemaps.org) — queried live with DuckDB, no API key, no signup, no vendor platform.
31
+
32
+ - **Answers, not data dumps.** Every tool returns compact, ranked results that fit in an agent's context window (~2K tokens), never raw GeoJSON.
33
+ - **Fresh, rich place data.** Operating status, confidence scores, and brands from Overture (contributed by Meta, Uber, TomTom, and others).
34
+ - **Zero setup.** Queries run directly against Overture's public GeoParquet on S3 — no ETL, no database, no key.
35
+
36
+ ## Quick start
37
+
38
+ Add to Claude Desktop / Claude Code:
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "placeroot": {
44
+ "command": "uvx",
45
+ "args": ["placeroot"]
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ Or run it directly:
52
+
53
+ ```bash
54
+ uv run placeroot # stdio MCP server
55
+ uv run placeroot --http # streamable-HTTP endpoint at http://127.0.0.1:8321/mcp
56
+ ```
57
+
58
+ `--http` serves plain HTTP with no TLS or auth — put a reverse proxy in front for anything beyond local use.
59
+
60
+ ## Tools
61
+
62
+ | Tool | Answers |
63
+ |---|---|
64
+ | `find_places` | Named places near a point, nearest first, with category, confidence, and operating status |
65
+ | `summarize_area` | What's in an area: total places and top categories |
66
+ | `place_details` | One place in full: addresses, contacts, brand, sources, confidence |
67
+ | `admin_lookup` | The admin hierarchy containing a point: neighborhood up to country |
68
+ | `compare_areas` | 2–5 areas side by side: category mix, density, and what differs most |
69
+ | `within_distance` | Is the nearest matching place within N meters of a point? |
70
+ | `geocode` | Free-text place name → ranked candidates with coordinates and admin context |
71
+ | `resolve_place` | Free-text place reference → ranked, typed GERS ids an agent can hold onto |
72
+ | `reverse_geocode` | Point → nearest address plus its containing division chain |
73
+ | `summarize_buildings` | Building stock in an area: count, footprint area, height and use mix |
74
+ | `buildings_at` | Nearest building footprints to a point |
75
+ | `isochrone` | The area reachable within N minutes on foot, bike, or car — on PlaceRoot's own routing graph |
76
+ | `render_map` | Any result → a self-contained interactive HTML map |
77
+ | `simplify_geometry` | Any GeoJSON geometry → simplified to a token budget |
78
+
79
+ More on the way — see [ROADMAP.md](ROADMAP.md).
80
+
81
+ ## Why
82
+
83
+ Agents are bad at maps. Existing map tools either require vendor API keys or return raw GeoJSON far too large for a context window. PlaceRoot's design rule: every answer fits in ~2K tokens, and anything bigger returns a summary plus a link.
84
+
85
+ A few things that make it work:
86
+
87
+ - **GERS ids everywhere.** Every place carries its stable Overture [GERS](https://docs.overturemaps.org/gers/) id, so an agent can hold onto a place across turns and look it up again with `place_details(id=...)` instead of re-searching.
88
+ - **Keyless geocoding.** `geocode`/`reverse_geocode` are built entirely on Overture's divisions and addresses themes — deterministic matching, no third-party geocoding API. 100% hit@1 on a ~113-query real-world benchmark (`scripts/geocode_benchmark.py`).
89
+ - **Local caching.** Hot data is cached on first use, so repeat queries answer in milliseconds and keep working offline. Set `PLACEROOT_CACHE=off` to always query upstream.
90
+ - **Self-hostable end to end.** Optionally mirror the data to your own S3-compatible storage and point PlaceRoot at it — see [docs/MIRROR.md](docs/MIRROR.md).
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ uv sync # installs pytest + ruff (dev dependency group)
96
+ uv run pytest # offline tests against committed fixtures
97
+ uv run pytest -m live # also run opt-in tests against real Overture S3
98
+ uv run ruff check .
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT
@@ -0,0 +1,180 @@
1
+ # PlaceRoot — Product Plan
2
+
3
+ **One-liner:** An open-source MCP server that grounds AI agents in the real world using
4
+ Overture Maps / OSM open data — answering spatial questions in compact, token-efficient
5
+ form, with no API key and no vendor platform.
6
+
7
+ **Repo:** https://github.com/chuofringer/placeroot
8
+
9
+ ## Positioning
10
+
11
+ | Against | They are | PlaceRoot is |
12
+ |---|---|---|
13
+ | Google Maps grounding (Gemini-native + Maps Grounding Lite hosted MCP, GA Oct–Dec 2025) | Keyed + billed ($14–25/1K prompts), no-caching/no-training terms, Google-data-only | Keyless, open data you may store, cache, and train on |
14
+ | Mapbox / TomTom / Esri / CARTO / HERE / Precisely MCP servers | Funnels into paid platforms (API keys, metered) | Open data, keyless |
15
+ | srivinod1/overture-mcp-server (the one near-clone) | Same architecture (DuckDB over Overture S3, keyless), but 4★, dormant since Mar 2026, pinned six releases stale; budgets tool *schemas*, not answers; punts geocoding/routing/display to other MCPs | Maintained, honest geometry, token-budgeted *responses*, current release |
16
+ | New Overture adjacents (thatapicompany/overture-maps-mcp, Jul 2026; soapboxbuild/overture-mcp, Jun 2026) | Keyed wrappers over hosted APIs or buildings-only slices; 0★ each; thatapicompany claims "token-efficient summaries" qualitatively, no budgets, no GERS in responses beyond soapboxbuild's buildings lookups | Keyless, full theme coverage, measured budgets, GERS everywhere |
17
+ | gis-mcp and GIS-function servers (176★, active) | Raw computation toolboxes (geometry ops, data out) | Answers, not data |
18
+ | OSM MCP servers (largest 215★, abandoned >1yr; no *active* successor over 30★ — wiseman/osm-mcp has 86★ but is equally dead since Mar 2025) | Overpass/Nominatim wrappers: rate limits, usage policies, stale data | Overture GeoParquet direct: operating status, confidence, brands |
19
+ | geowire (multi-provider gateway, launched 2026-07-18) | BYOK vendor-API aggregator; "keyless" floor is rate-limited Nominatim/OSRM (incl. a keyless OSRM isochrone); 0★ as of 2026-08-06 | Truly keyless at scale; Overture-native; own routing graph |
20
+ | Camino AI (getcamino.ai) | Pure-play "location intelligence for agents" startup with a native MCP server — but API-keyed, metered, "17x cheaper than Google Places" as the pitch | Keyless and free; open data you may store and train on |
21
+ | ORATOR (Overture's experimental knowledge-graph MCP, built by Wherobots) | Regional prototype (SF Bay Area) on proprietary Wherobots/Iceberg infra, explicitly a proof-of-concept | Keyless, self-hostable, global today |
22
+ | GeoLibre / GeoAgent (opengeos) | An app for humans / a Strands-based agent that drives GIS software; no MCP layer, no Overture focus | Plumbing for agents (and a future GeoLibre bridge) |
23
+
24
+ Market context (extensive study 2026-08-05; superseding the initial Aug 2026 scan):
25
+ ~40 location MCP servers across the registries; the official MCP registry returns **zero
26
+ results for "overture"**. Nobody ships token-budgeted answers as the headline feature —
27
+ the closest is Google Grounding Lite's "AI-generated place data summaries" (keyed,
28
+ billed) and srivinod1's tool-schema "progressive disclosure" (dormant). The payload
29
+ problem is now publicly named — Mapbox's "GeoAI in 2026" post (Dec 2025) calls large
30
+ geospatial payloads MCP's biggest challenge; a Geoawesome essay (June 2026) shows a
31
+ 45K-token GeoJSON collapsing to a 25-token reference — but no one ships the fix as a
32
+ product. Overture itself now markets our thesis: "50 members converge on open data to
33
+ ground AI" (July 2026), with GERS pitched as the anti-hallucination anchor and headed
34
+ toward OGC standardization. Foursquare's open places data is flowing *into* Overture
35
+ (~6M POIs merged in 2025), while FSQ's own MCP server targets its paid API and is
36
+ near-dormant. OpenAI and Anthropic have picked no maps partner — Claude's connector
37
+ directory lists only TomTom; the "default maps tool" slot in both ecosystems is open.
38
+ Category traction ceiling remains low (Mapbox official: 350★; only geo-MCP story with
39
+ real HN traction: 105 points).
40
+
41
+ Re-verification sweep 2026-08-06 (three-track: open-source registry sweep, commercial
42
+ vendor survey, claim-by-claim check): **all ten claims of the 2026-08-05 study stand.**
43
+ The registry search for "overture" is still empty; the near-clone is still dormant
44
+ (last commit 2026-02-28); nobody ships numeric token budgets, GERS ids in responses,
45
+ keyless global isochrones, or self-contained offline map artifacts. The token-payload
46
+ pain point gained a second corporate validator: HERE's "Location Reasoning" (announced
47
+ May 2026) is an entire enterprise product marketed on reducing token usage. New since
48
+ the study, all adjacent rather than head-on: thatapicompany/overture-maps-mcp
49
+ (2026-07-16, keyed wrapper over a hosted API, 0★ — first competitor to even gesture at
50
+ token efficiency), capan/isochrone (registered to the official MCP registry
51
+ **2026-08-06**, keyless own-graph isochrones — but Berlin-only, pedestrian-only, one
52
+ tool), and sparkgeo/geo-mcp-servers (2026-08-02, 36★) — a curated tracker of 77
53
+ geospatial MCP servers whose only Overture entry is the dormant near-clone. The
54
+ sparkgeo tracker is both a listing target and the best ongoing competitive radar for
55
+ this category. Discoverability hazard: "Overture" searches are polluted by an
56
+ unrelated 629★ coding-agent tool of the same name (SixHq/Overture). The `placeroot`
57
+ name remained unclaimed on PyPI, npm, and GitHub as of 2026-08-06 — re-verified, but
58
+ verification is not a reservation; only #16's publishes are.
59
+
60
+ ## Design rules
61
+
62
+ 1. **Answers, not data.** Every tool response fits in ~2K tokens. Big results return a
63
+ summary + retrievable artifact, never raw GeoJSON dumps.
64
+ 2. **Open data, keyless by default.** Overture S3 GeoParquet via DuckDB (no ETL, no
65
+ database); OSM services that permit anonymous use.
66
+ 3. **No hard dependency on anyone else.** Nothing on the critical path may be gated on
67
+ another project's roadmap, another service's rate limit, or another team's willingness
68
+ to integrate. External systems may make PlaceRoot faster or fresher; none may make it
69
+ stop working. Consequences: geocoding is built on Overture rather than wrapping
70
+ Nominatim; the map viewer is a self-contained artifact rather than a bridge to someone
71
+ else's app; routing is our own graph rather than a hosted Valhalla; the upstream release
72
+ is discovered, pinned, cached, and mirrorable.
73
+ 4. **Permanent scope exclusion: hazard- and property-risk scoring.** No flood/fire/wind
74
+ risk scores, property risk ratings, or insurance-flavored analytics — ever.
75
+
76
+ ## Naming
77
+
78
+ "placeroot" chosen 2026-08-05 after availability research: PyPI, npm, GitHub repo names,
79
+ and .com/.io/.dev/.ai all unclaimed; no company, product, social account, or trademark
80
+ found under the name. Runners-up rejected: geoplinth (SEO collision with Geoplin),
81
+ groundfact (social channels exist), 15 others (registered domains or package collisions).
82
+
83
+ ## Phases
84
+
85
+ ### Phase 1 — MVP (in progress)
86
+
87
+ - [x] DuckDB query layer over Overture S3 (release 2026-07-22.0), bbox pushdown
88
+ - [x] `find_places` — nearest named places; taxonomy, confidence, operating_status
89
+ (verified live: downtown Austin, 8 results ≈ 390 tokens, 5.6s cold)
90
+ - [x] `summarize_area` — category mix (1,944 places ≈ 320 tokens)
91
+ - [x] MCP server (Python SDK 2.0), `uv run placeroot`
92
+ - [x] Fix radius geometry and count correctness (#1, #2, #3)
93
+ - [x] Overture release auto-discovery + graceful degradation (#4, #5)
94
+ - [x] Offline test suite and CI (#6); measured token budget (#7)
95
+ - [x] Local tile cache — warm ~21ms, cold under ~5s, works offline (#8, #31)
96
+ - [x] `place_details`, `compare_areas`, `within_distance` (#9, #12, #13, #41)
97
+ - [x] `geocode` / `reverse_geocode` on Overture data (hit@1 100% live, saturated set), `admin_lookup` (#10, #11, #43, #46, #47)
98
+ - [x] `simplify_geometry` (the payload tool) (#14)
99
+ - [x] Expose GERS ids in every tool response (#25)
100
+ - [x] Register domains: placeroot.dev (canonical) + placeroot.com (redirect), done
101
+ 2026-08-05; .io intentionally skipped (#27)
102
+ - [ ] Publish PyPI + npm (#16 — P0 as of 2026-08-05: the architecture is replicable
103
+ and the registry search is empty)
104
+
105
+ Full triage on the [project board](https://github.com/users/chuofringer/projects/2).
106
+
107
+ ### Phase 2 — Distribution (weeks 8–13)
108
+
109
+ - [x] Self-contained map artifact — any result opens as a live HTML map we ship
110
+ ourselves (#15). A GeoLibre bridge is an additional output target and a community
111
+ relationship, not a dependency; engage opengeos early either way.
112
+ - [x] placeroot.dev landing page built (#28) — deploy awaits DNS/Pages setup (yours)
113
+ - [x] Listing entries drafted for all 8 registries (docs/launch/registry-submissions.md,
114
+ incl. the sparkgeo/geo-mcp-servers curated tracker found 2026-08-06);
115
+ submission itself is owner-side, gated on #16's publishes
116
+ - [x] Show HN text + community outreach notes drafted (docs/launch/); posting is owner-side
117
+ - [x] "Why agents are bad at maps" essay drafted (docs/launch/), Mapbox/Geoawesome cited
118
+ - [x] Tokens-per-correct-answer benchmark shipped and run live (#26): 90% accuracy,
119
+ median 2,543x fewer tokens than raw payloads (benchmarks/results.md)
120
+ - [x] Flagship demo: site-selection agent entirely on open data (#17 — live-verified, ~5.9K tokens for a 17-call analysis)
121
+
122
+ **Traction bar (~week 14):** judged on signals we own (#19, defined with
123
+ collection methods and the decision rule in [docs/METRICS.md](docs/METRICS.md)) — download trend over four
124
+ weeks, distinct connecting clients, inbound issues/PRs from people we did not contact, and
125
+ named real uses. Stars are reported, not a gate: they measure other people's attention, not
126
+ whether this is worth building.
127
+
128
+ ### Phase 3 — Hosted tier (months 4–7, gated on Phase 2 bar)
129
+
130
+ - Free: local server forever. Hosted $0/$29/$99: remote streamable-HTTP endpoint,
131
+ pre-warmed indexes, monthly Overture refresh, isochrones/routing on our own graph (#17).
132
+ The hosted tier sells latency and convenience — never access.
133
+ - Infra <$100/mo (Cloudflare Workers + R2, or single box + DuckDB).
134
+ - Expectation: $500–3K MRR year one; primary value is audience + substrate for what's next.
135
+
136
+ ### Phase 4 — Demand-driven expansions
137
+
138
+ GERS id resolution service; live layers (transit, weather context — not hazard risk);
139
+ buildings/transportation themes; team features; white-label for agent platforms.
140
+
141
+ ## Risks
142
+
143
+ (Reassessed 2026-08-05 against the extensive competitive study.)
144
+
145
+ 1. **opengeos ships an MCP layer first** — *downgraded*. Verified: no MCP layer in
146
+ GeoLibre or GeoAgent; their agent effort targets GIS analysis via Strands, not place
147
+ grounding. Still engage their community; still structurally defused (#15). Watch
148
+ GeoAgent's roadmap — one Overture tool factory would change this.
149
+ 2. **Foundation labs bake maps in via one big partner** — *partially materialized, at
150
+ Google only*: Gemini has native Maps grounding and Maps Grounding Lite exports it to
151
+ any MCP client — but keyed, billed ($14–25/1K), rate-limited, no-caching/no-training
152
+ terms. OpenAI (Yelp/Zillow verticals via Apps SDK) and Anthropic (TomTom connector
153
+ only) picked no horizontal partner. Keyless open data remains the wedge; Google's
154
+ pricing gives us a concrete cost comparison.
155
+ 3. **Attention, not code, is the bottleneck** → Phase 2 checklist is scheduled work;
156
+ half of weekly hours in weeks 8–13 go to distribution.
157
+ 4. **Upstream Overture changes layout, schedule, or access** → release discovery with a
158
+ pinned fallback (#4), a schema probe that degrades per-tool rather than failing all
159
+ (#5), a local cache that answers offline (#8), and eventually our own mirror (#20).
160
+ 5. **The near-clone revives, or someone wraps existing pieces** — *escalating on a
161
+ weeks timescale, not months*. srivinod1's overture-mcp-server has our architecture
162
+ (dormant, 4★), and the DuckDB `overture` community extension (geocode, place
163
+ readers, category normalization) is one wrapper away from being an MCP server.
164
+ Two adjacents appeared within three weeks of the 2026-08-05 study and one more
165
+ the day after (thatapicompany 2026-07-16, capan/isochrone registered 2026-08-06);
166
+ none is head-on yet, but each occupies a fragment of our combination. Moat is
167
+ execution: maintenance, honest geometry, token-budgeted answers, GERS ids (#22),
168
+ current releases — and shipping #16 before the empty "overture" registry search
169
+ fills. Standing radar: watch sparkgeo/geo-mcp-servers for new entrants.
170
+ 6. **Foursquare ships keyless agent tooling on FSQ OS Places** — license permits it;
171
+ today their MCP server is API-keyed and near-dormant, and FSQ open data flows into
172
+ Overture, strengthening our substrate. Monitor.
173
+ 7. **Overture-official tooling subsumes third parties** — ORATOR (Wherobots) is the
174
+ foundation's experimental MCP prototype. Treat as opportunity first: engage the
175
+ Product Council discussion, adopt GERS, stay the keyless self-hostable option
176
+ ORATOR is not.
177
+
178
+ ## Cadence
179
+
180
+ Solo, ~6–8 hrs/week alongside day job. Phase 1 ≈ 35–45 focused hours.
@@ -0,0 +1,78 @@
1
+ # PlaceRoot
2
+
3
+ **Ground AI agents in open map data.**
4
+
5
+ PlaceRoot is an MCP server that answers spatial questions from [Overture Maps](https://overturemaps.org) — queried live with DuckDB, no API key, no signup, no vendor platform.
6
+
7
+ - **Answers, not data dumps.** Every tool returns compact, ranked results that fit in an agent's context window (~2K tokens), never raw GeoJSON.
8
+ - **Fresh, rich place data.** Operating status, confidence scores, and brands from Overture (contributed by Meta, Uber, TomTom, and others).
9
+ - **Zero setup.** Queries run directly against Overture's public GeoParquet on S3 — no ETL, no database, no key.
10
+
11
+ ## Quick start
12
+
13
+ Add to Claude Desktop / Claude Code:
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "placeroot": {
19
+ "command": "uvx",
20
+ "args": ["placeroot"]
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ Or run it directly:
27
+
28
+ ```bash
29
+ uv run placeroot # stdio MCP server
30
+ uv run placeroot --http # streamable-HTTP endpoint at http://127.0.0.1:8321/mcp
31
+ ```
32
+
33
+ `--http` serves plain HTTP with no TLS or auth — put a reverse proxy in front for anything beyond local use.
34
+
35
+ ## Tools
36
+
37
+ | Tool | Answers |
38
+ |---|---|
39
+ | `find_places` | Named places near a point, nearest first, with category, confidence, and operating status |
40
+ | `summarize_area` | What's in an area: total places and top categories |
41
+ | `place_details` | One place in full: addresses, contacts, brand, sources, confidence |
42
+ | `admin_lookup` | The admin hierarchy containing a point: neighborhood up to country |
43
+ | `compare_areas` | 2–5 areas side by side: category mix, density, and what differs most |
44
+ | `within_distance` | Is the nearest matching place within N meters of a point? |
45
+ | `geocode` | Free-text place name → ranked candidates with coordinates and admin context |
46
+ | `resolve_place` | Free-text place reference → ranked, typed GERS ids an agent can hold onto |
47
+ | `reverse_geocode` | Point → nearest address plus its containing division chain |
48
+ | `summarize_buildings` | Building stock in an area: count, footprint area, height and use mix |
49
+ | `buildings_at` | Nearest building footprints to a point |
50
+ | `isochrone` | The area reachable within N minutes on foot, bike, or car — on PlaceRoot's own routing graph |
51
+ | `render_map` | Any result → a self-contained interactive HTML map |
52
+ | `simplify_geometry` | Any GeoJSON geometry → simplified to a token budget |
53
+
54
+ More on the way — see [ROADMAP.md](ROADMAP.md).
55
+
56
+ ## Why
57
+
58
+ Agents are bad at maps. Existing map tools either require vendor API keys or return raw GeoJSON far too large for a context window. PlaceRoot's design rule: every answer fits in ~2K tokens, and anything bigger returns a summary plus a link.
59
+
60
+ A few things that make it work:
61
+
62
+ - **GERS ids everywhere.** Every place carries its stable Overture [GERS](https://docs.overturemaps.org/gers/) id, so an agent can hold onto a place across turns and look it up again with `place_details(id=...)` instead of re-searching.
63
+ - **Keyless geocoding.** `geocode`/`reverse_geocode` are built entirely on Overture's divisions and addresses themes — deterministic matching, no third-party geocoding API. 100% hit@1 on a ~113-query real-world benchmark (`scripts/geocode_benchmark.py`).
64
+ - **Local caching.** Hot data is cached on first use, so repeat queries answer in milliseconds and keep working offline. Set `PLACEROOT_CACHE=off` to always query upstream.
65
+ - **Self-hostable end to end.** Optionally mirror the data to your own S3-compatible storage and point PlaceRoot at it — see [docs/MIRROR.md](docs/MIRROR.md).
66
+
67
+ ## Development
68
+
69
+ ```bash
70
+ uv sync # installs pytest + ruff (dev dependency group)
71
+ uv run pytest # offline tests against committed fixtures
72
+ uv run pytest -m live # also run opt-in tests against real Overture S3
73
+ uv run ruff check .
74
+ ```
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,94 @@
1
+ # PlaceRoot Roadmap
2
+
3
+ Tracked on the [project board](https://github.com/users/chuofringer/projects/2).
4
+
5
+ ## Design rules
6
+
7
+ 1. **Answers, not data.** Every tool response fits in ~2K tokens. Large results
8
+ return a summary plus a retrievable artifact, never raw GeoJSON dumps.
9
+ 2. **Open data only, keyless by default.** Overture Maps GeoParquet and
10
+ OpenStreetMap services that permit anonymous use.
11
+ 3. **No hard dependency on anyone else.** Nothing on the critical path may be
12
+ gated on another project's roadmap, another service's rate limit, or another
13
+ team's willingness to integrate. External systems may make PlaceRoot faster
14
+ or fresher; none may make it stop working.
15
+ 4. **Out of scope, permanently: hazard- and property-risk scoring.** PlaceRoot
16
+ answers "what is where" questions. It will not ship flood/fire/wind risk
17
+ scores, property risk ratings, or insurance-flavored analytics.
18
+
19
+ ## v0.1
20
+
21
+ - [x] `find_places` — nearest named places with taxonomy, confidence, operating status
22
+ - [x] `summarize_area` — category mix for an area
23
+
24
+ ## v0.2 — correct, resilient, useful (shipped 2026-08-06)
25
+
26
+ Correctness and independence first: the query layer has known geometry bugs, no
27
+ tests, and a hardcoded upstream release.
28
+
29
+ - [x] Fix radius geometry: circular distance in SQL, honest counts (#1, #2, #3)
30
+ - [x] Overture release auto-discovery with pinned fallback (#4)
31
+ - [x] Graceful degradation when upstream is slow, down, or renamed (#5)
32
+ - [x] Offline test suite and CI on committed fixtures (#6)
33
+ - [x] Measured token-budget enforcement with visible truncation (#7)
34
+ - [x] Local row-group cache — warm queries under 500ms, works offline (#8)
35
+ - [x] `place_details` — one place in full (#9)
36
+ - [x] `geocode` / `reverse_geocode` built on Overture, not Nominatim (#10) —
37
+ live benchmark hit@1 100% over 113 queries (saturated set), warm under 0.3s (#43, #46, #47, #53)
38
+ - [x] `admin_lookup` — point → admin hierarchy (#11)
39
+ - [x] `compare_areas`, `within_distance` (#12, #13)
40
+ - [x] `simplify_geometry` — the payload tool (#14)
41
+ - [x] Expose GERS ids in every tool response — stable place references, no
42
+ competitor surfaces them (#25)
43
+
44
+ ## v0.3 — capabilities nobody else has keyless (core shipped 2026-08-06)
45
+
46
+ - [x] Self-contained map artifact: any result renders as a live HTML map, no
47
+ CDN, no tile key, no external viewer (#15)
48
+ - [x] Own routing stack, walking MVP: routable graph and isochrones from
49
+ Overture transportation, no hosted routing service on the critical path
50
+ (#18; drive/cycle, concave hulls, graph caching tracked in #36–#39)
51
+ - [x] Buildings and transportation themes (#23; transportation via the routing
52
+ stack, buildings via summarize_buildings / buildings_at)
53
+ - [ ] Hosted streamable-HTTP endpoint — sells latency, never access (#24;
54
+ transport code shipped — `placeroot --http` — hosting/TLS/DNS is the
55
+ owner-side remainder)
56
+
57
+ ## Later, demand-driven
58
+
59
+ - [x] GERS id resolution — `resolve_place`: free-text place references →
60
+ GERS ids, divisions and places merged (#22, #25)
61
+ - [ ] Own mirror of the places theme (#20; tooling + switchover shipped —
62
+ scripts/mirror_theme.py, PLACEROOT_UPSTREAM_BASE, docs/MIRROR.md —
63
+ bucket + the 10.5 GB transfer are the owner-side remainder)
64
+ - [ ] Live layers: transit feeds, weather context
65
+
66
+ ## Competitive watch (verified 2026-08-06)
67
+
68
+ A three-track market re-verification (details in [PLAN.md](PLAN.md)) confirmed the
69
+ category position: no other project ships token-budgeted answers, GERS ids in every
70
+ response, keyless Overture geocoding, keyless global own-graph isochrones, or
71
+ self-contained offline map artifacts. The position is features, not a moat — two
72
+ adjacent projects appeared within three weeks and one the day of the check. Standing
73
+ watch items, none blocking:
74
+
75
+ - **sparkgeo/geo-mcp-servers** — curated tracker of 77 geo MCP servers; our listing
76
+ target and the category radar. Check on each release.
77
+ - **capan/isochrone** — keyless own-graph isochrones (Berlin-only, pedestrian-only);
78
+ the nearest conceptual neighbor to our routing stack.
79
+ - **thatapicompany/overture-maps-mcp** — keyed hosted-API wrapper claiming
80
+ "token-efficient summaries"; first competitor gesturing at our headline.
81
+ - **Camino AI** — commercial "location intelligence for agents" startup, keyed/metered.
82
+ - **Google Maps Grounding Lite** — free while Experimental; a GA pricing decision
83
+ ($2.80–7.00/1K published as potential) changes the cost-comparison story.
84
+ - **Overture first-party tooling** — ORATOR is still an SF-Bay prototype; any move
85
+ toward an official Overture MCP server changes risk 7 in PLAN.md.
86
+
87
+ The empty "overture" search in the official MCP registry is the clock: #16 ships
88
+ before someone else fills it.
89
+
90
+ ## Relationships, not dependencies
91
+
92
+ A GeoLibre bridge and upstream contributions to Overture tooling are both
93
+ worth doing and actively wanted. Neither blocks a release. If a partner ships
94
+ first, PlaceRoot still works the same day.
@@ -0,0 +1,116 @@
1
+ # Token benchmark (issue #26)
2
+
3
+ ## Reality check first
4
+
5
+ [GeoBenchX](https://github.com/Solirinai/GeoBenchX) and GeoAgentBench are
6
+ *agent-with-LLM* benchmarks: they measure whether an LLM correctly chooses
7
+ and chains geospatial tools across a conversation. Running either faithfully
8
+ needs LLM API calls, which this repo has no credentials for and won't fake.
9
+
10
+ What's here instead is something narrower, but real and runnable today:
11
+ `token_benchmark.py` runs 30 spatial questions — inspired by GeoBenchX's own
12
+ task categories (nearest-POI, area comparison, point-in-admin,
13
+ within-distance, isochrone reachability) — directly against **live**
14
+ Overture data, checks each answer programmatically, and measures **tokens
15
+ per correct answer**: placeroot's compact tool response vs. the token cost
16
+ of the equivalent *raw* payload (unprocessed rows / full geometry) an agent
17
+ would have had to fetch and read itself without placeroot's tool doing the
18
+ filtering, ranking, point-in-polygon, or graph-building.
19
+
20
+ No LLM is called anywhere in this benchmark. It measures the *data* side of
21
+ the "answers, not data dumps" design rule (README.md), not an agent's tool
22
+ choice — that's what a real GeoBenchX/GeoAgentBench integration would add.
23
+ The task format here (`Task(name, category, fn)`, where `fn()` calls the
24
+ placeroot tool(s) and returns a checked `Outcome`) is deliberately generic
25
+ enough that a future LLM-driven harness could reuse the same task
26
+ definitions and checkers, swapping in an agent's own tool calls for the
27
+ direct calls used here — that integration is tracked as future work under
28
+ this issue's lineage, not solved here.
29
+
30
+ ## Running it
31
+
32
+ ```bash
33
+ uv run python benchmarks/token_benchmark.py
34
+ ```
35
+
36
+ Hits live Overture S3 (no fixture, no mock — same posture as
37
+ `scripts/geocode_benchmark.py`). Each task is 1-3 tool calls; with a warm
38
+ tile cache this finishes in a couple of minutes, longer cold (the isochrone
39
+ tasks build a street graph from scratch and are the slowest part). Prints
40
+ the report and overwrites `benchmarks/results.md` with the run's actual
41
+ output — every number in `results.md` comes from that run, nothing hand-
42
+ edited in.
43
+
44
+ The harness itself (task definitions, the `Outcome`/`run_task` scoring
45
+ machinery, the raw-payload comparator helpers) is also exercised offline in
46
+ `tests/test_token_benchmark.py`, against the same committed fixtures the
47
+ rest of the test suite uses — `uv run pytest` runs that as part of the
48
+ normal suite, no network required.
49
+
50
+ ## Task categories and what "raw" means per category
51
+
52
+ Every task calls placeroot's tool(s) through `placeroot.server`'s plain
53
+ Python functions directly (the same functions the MCP server exposes, see
54
+ `server.py`), not over an MCP client transport — this measures response
55
+ payload size, which doesn't depend on the transport. Both the placeroot
56
+ side and the raw side are measured with the same `len(json)//4` heuristic
57
+ `placeroot.budget.estimate_tokens` uses, so every ratio below is directly
58
+ comparable to the server's own token-budget accounting.
59
+
60
+ - **point_in_admin** (`admin_lookup`): does the containing-division chain
61
+ include the point's real county and state? Raw side: every division
62
+ polygon that actually contains the point, with full unsimplified GeoJSON
63
+ geometry (`ST_AsGeoJSON`) — the full division polygon, not the tool's
64
+ `{name, type, id}` chain entry.
65
+ - **within_distance**: is a place matching a category within N meters,
66
+ true/false. Raw side: every place row (every column, not the curated
67
+ id/name/category/distance the tool returns) inside the same search
68
+ window (`max_distance_m * 2`, matching the tool's own search radius).
69
+ - **nearest_poi** (`find_places`): does a coffee_shop search near an iconic
70
+ downtown core turn up a Starbucks in the top 3 (top-3, not top-1,
71
+ specifically to tolerate ordinary data drift between Overture releases)?
72
+ Raw side: every place row in the same radius/category predicate.
73
+ - **area_comparison** (`compare_areas`): does a permanently dense downtown
74
+ core show more total places than a permanently rural/wilderness point at
75
+ the same radius — a coarse, durable fact, not a fragile exact-count
76
+ check. Raw side: every place row in both areas' radii, combined.
77
+ - **isochrone**: sanity checks (a well-connected urban point reaches a
78
+ nonzero area) plus two durable physical invariants — a longer time budget
79
+ reaches an equal-or-larger area than a shorter one, and a faster mode
80
+ (drive/cycle) reaches an equal-or-larger area than walking in the same
81
+ time, at the same point. Raw side: every transportation segment row
82
+ (with WKT geometry) in the same auto-derived graph-extraction radius the
83
+ tool itself used — i.e. the raw street-network edges an agent would have
84
+ had to fetch and read to answer the question itself, not the tool's
85
+ compact polygon + stats.
86
+
87
+ ## Honesty rules
88
+
89
+ - A task whose checker fails is counted in the aggregate, not dropped —
90
+ `results.md`'s table includes every task that ran, correct or not, and a
91
+ "Failure detail" section spells out exactly what each miss got instead of
92
+ what it expected.
93
+ - The raw-payload methodology is documented per category above rather than
94
+ left implicit; where it undercounts the true raw payload (isochrone's raw
95
+ segments use WKT text in place of the raw binary WKB geometry column, so
96
+ the row stays JSON-serializable without a custom encoder), that's called
97
+ out in the code, not smoothed over.
98
+ - No number in this file is asserted without `benchmarks/results.md`
99
+ backing it — `results.md` is regenerated by, and only by, actually running
100
+ the script above against live data.
101
+
102
+ ## A finding from running this
103
+
104
+ The first live run of this benchmark hit a real, reproducible-at-the-time
105
+ tile-cache incompleteness: `find_places` near Times Square returned zero
106
+ results (with `PLACEROOT_CACHE` at its default, on) even though the same
107
+ query with `PLACEROOT_CACHE=off` found a restaurant 10m away. Clearing
108
+ `~/.cache/placeroot` and rerunning made it disappear — a fresh cache
109
+ answered the very first query correctly, and stayed correct on repeat
110
+ queries. That points to a one-off race in cache.py's background tile
111
+ materialization (see its module docstring: a cache miss should always fall
112
+ back to a direct upstream scan for *that* query, so this shouldn't be
113
+ possible even before the background fetch resolves) rather than a
114
+ systematic data gap, but it wasn't chased further here — flagged as a
115
+ follow-up worth its own issue, not fixed in this one. The committed
116
+ `results.md` reflects the rerun against a cleared cache.
@@ -0,0 +1,51 @@
1
+ # Token benchmark results (issue #26)
2
+
3
+ - Run date: 2026-08-06
4
+ - Overture release: 2026-07-22.0
5
+ - Tasks: 30
6
+ - Accuracy: 27/30 (90.0%)
7
+ - Median tokens ratio (raw / placeroot), over 30 scored tasks: 2543.0x
8
+ - Mean tokens ratio: 11246.6x
9
+
10
+ Every task that ran is in the table below, including failures — nothing is dropped from the aggregate.
11
+
12
+ ## Per-task results
13
+
14
+ | task | category | correct | placeroot tokens | raw tokens | ratio |
15
+ |---|---|---|---:|---:|---:|
16
+ | austin_texas_capitol | point_in_admin | yes | 90 | 2349639 | 26107.1x |
17
+ | brooklyn_borough_hall | point_in_admin | yes | 113 | 1819534 | 16102.1x |
18
+ | la_city_hall | point_in_admin | yes | 94 | 1880981 | 20010.4x |
19
+ | chicago_willis_tower | point_in_admin | yes | 135 | 1806458 | 13381.2x |
20
+ | seattle_space_needle | point_in_admin | yes | 91 | 1807274 | 19860.2x |
21
+ | miami_downtown | point_in_admin | yes | 92 | 2000858 | 21748.5x |
22
+ | denver_state_capitol | point_in_admin | yes | 93 | 1752816 | 18847.5x |
23
+ | times_square_restaurant | within_distance | yes | 71 | 766290 | 10792.8x |
24
+ | chicago_loop_coffee | within_distance | yes | 75 | 42024 | 560.3x |
25
+ | hollywood_highland_restaurant | within_distance | yes | 74 | 167766 | 2267.1x |
26
+ | eiffel_tower_restaurant | within_distance | yes | 69 | 153342 | 2222.3x |
27
+ | yellowstone_lake_grocery | within_distance | yes | 13 | 0 | 0.0x |
28
+ | pacific_ocean_any_place | within_distance | yes | 13 | 0 | 0.0x |
29
+ | sahara_desert_any_place | within_distance | yes | 13 | 0 | 0.0x |
30
+ | grand_canyon_remote_restaurant | within_distance | yes | 13 | 0 | 0.0x |
31
+ | times_square_coffee | nearest_poi | **NO** | 181 | 40841 | 225.6x |
32
+ | chicago_loop_coffee | nearest_poi | yes | 185 | 20348 | 110.0x |
33
+ | seattle_downtown_coffee | nearest_poi | yes | 183 | 35743 | 195.3x |
34
+ | sf_union_square_coffee | nearest_poi | **NO** | 181 | 23338 | 128.9x |
35
+ | la_downtown_coffee | nearest_poi | **NO** | 190 | 20250 | 106.6x |
36
+ | boston_downtown_coffee | nearest_poi | yes | 183 | 28713 | 156.9x |
37
+ | manhattan_vs_rural_upstate_ny | area_comparison | yes | 526 | 17410408 | 33099.6x |
38
+ | chicago_loop_vs_rural_il | area_comparison | yes | 526 | 5454757 | 10370.3x |
39
+ | sf_union_sq_vs_sierra_backcountry | area_comparison | yes | 514 | 5916508 | 11510.7x |
40
+ | austin_6th_st_vs_hill_country | area_comparison | yes | 491 | 2550036 | 5193.6x |
41
+ | times_square_walk_15min | isochrone | yes | 2044 | 2836060 | 1387.5x |
42
+ | times_square_drive_ge_walk_15min | isochrone | yes | 3325 | 239697321 | 72089.4x |
43
+ | chicago_loop_walk_10min_vs_20min | isochrone | yes | 3371 | 4687640 | 1390.6x |
44
+ | sf_union_sq_cycle_ge_walk_15min | isochrone | yes | 3770 | 10627117 | 2818.9x |
45
+ | austin_downtown_drive_15min | isochrone | yes | 2039 | 95251683 | 46714.9x |
46
+
47
+ ## Failure detail
48
+
49
+ - **times_square_coffee** (nearest_poi): expected one of ['Starbucks'] in top-3, got ['Joe Coffee Company', "Dunkin'", 'Central Perk']
50
+ - **sf_union_square_coffee** (nearest_poi): expected one of ['Starbucks'] in top-3, got ['Bancarella', 'Lux Cafe Club', 'Hyatt Coffee Bar']
51
+ - **la_downtown_coffee** (nearest_poi): expected one of ['Starbucks'] in top-3, got ['Barista Society Coffee Boutique', 'Aquarela Coffee', 'The Coffee Bean & Tea Leaf']