{
  "markdown": "# GeoWire\n\n> **Give any AI agent real-world location intelligence in 5 minutes — no API key required.**\n>\n> One interface for place search, directions, and area/market analysis across every map provider.\n\n<p align=\"center\">\n  <img src=\"docs/media/geowire-mcp.gif\" alt=\"geowire MCP server — tools/list and a geocode_address call over stdio\" width=\"760\">\n</p>\n\nGeoWire is an open-source geo intelligence gateway that sits between AI agents and\nmap/place data providers (OpenStreetMap, Google, Yelp, Foursquare, US Census, your\nown data) and exposes them through a single **MCP server**, **REST API**, and\n**SDK**. One interface for **place search, geocoding, directions & distance\nmatrices, and commercial-area analysis (density, ratings, demographics)** — with\nprovider fallback, multi-provider merge + dedup, cost budgets, and a policy engine\nthat enforces each provider's caching/attribution terms.\n\n**Published on npm — MCP · REST · CLI · SDK all functional. 10 providers, 8 operations.**\n\n> **Honest by design:** OpenStreetMap (the zero-key default) is a great\n> *geocoder* — strong on place names, addresses, and landmarks — but thin on\n> category words (\"coffee\", \"pharmacy\"), ratings, and hours. For US/Western\n> commercial data, add a Google, Yelp, or Foursquare key (BYOK) — GeoWire merges\n> them and tells you which source every field came from. Directions run key-free\n> on OpenStreetMap routing (OSRM); US demographics come from the Census (free key).\n\n**Contents:** [Why](#why-geowire) · [Quickstart](#quickstart) · [MCP tools](#mcp-tools) · [REST](#rest-endpoints) · [Anatomy of a response](#anatomy-of-a-response) · [Config](#configuration-optional--everything-works-without-it) · [Providers](#providers) · [Recipes & examples](#recipes--examples) · [Roadmap](#roadmap) · [Architecture](#architecture)\n\n## Why GeoWire?\n\n|  | Direct integration | Single-provider MCP | **GeoWire** |\n|---|---|---|---|\n| Unified place schema | ❌ per-provider code | ❌ | ✅ |\n| Provider fallback on failure | ❌ | ❌ | ✅ |\n| Multi-provider merge + dedup | ❌ | ❌ | ✅ |\n| Cost budgets & routing | ❌ | ❌ | ✅ |\n| Works without any API key | ❌ | depends | ✅ (OSM by default) |\n| Self-hosted | — | depends | ✅ |\n| Your own place data as a provider | ❌ | ❌ | ✅ |\n| Transparent provenance (which source, what cost) | ❌ | ❌ | ✅ (every response) |\n\n**Not a Google replacement — it *uses* Google.** The thing no single provider can\ndo: merge **your own store data + Google + OSM** into one deduped record, with\nper-field provenance (your name is authoritative, Google adds ratings, OSM adds\ncoordinates). Real run below:\n\n<p align=\"center\">\n  <img src=\"docs/media/geowire-merge.gif\" alt=\"GeoWire merging a private store DB, Google, and OpenStreetMap into one record with per-field provenance\" width=\"760\">\n</p>\n\n### More than search — location intelligence for agents\n\nOnce your agent can find places, it can reason about *areas*. One `analyze_area`\ncall turns a point + radius into a commercial-district read — category density,\ncompetition, the rating landscape, and (in the US) demographics:\n\n```jsonc\n// \"Is this a good block for a new café?\" — SF, 1 km radius\n{ \"center\": { \"latitude\": 37.7749, \"longitude\": -122.4194 }, \"radiusMeters\": 1000,\n  \"categories\": [\"cafe\", \"restaurant\", \"bar\"] }\n// → per-category counts + density/km², avg rating (Google/Yelp), price mix,\n//   an activity proxy (Yelp review volume), and Census demographics for the tract.\n```\n\nSame gateway also does **directions & distance matrices** and **travel-time\nisochrones** — key-free via OSRM — so an agent can rank candidates by drive time and\nanswer catchment questions like *\"what's within a 15-minute drive of this address?\"*\nSee [Recipes](./docs/recipes.md).\n\n## Quickstart\n\n### 1. MCP (Claude Desktop / Cursor) — 30 seconds\n\nAdd this to your MCP client config (e.g. Claude Desktop `claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"geowire\": { \"command\": \"npx\", \"args\": [\"-y\", \"@geowirehq/mcp\"] }\n  }\n}\n```\n\nThen ask: *\"Where is the Golden Gate Bridge?\"*, *\"Find coffee within 2 km of\n37.7749, -122.4194 (San Francisco).\"*, or *\"How do I drive from downtown SF to\nFisherman's Wharf?\"* Works with **zero API keys** — OpenStreetMap + OSRM routing\nare the defaults. Add `\"env\": { \"GOOGLE_MAPS_API_KEY\": \"...\" }` (or `YELP_API_KEY`)\nfor ratings, reviews, and hours, and `\"CENSUS_API_KEY\"` for area demographics.\nSee [more MCP client configs](./examples/mcp-clients.md).\n\n### 2. CLI — one-shot search & server\n\n<p align=\"center\">\n  <img src=\"docs/media/geowire-search.gif\" alt=\"geowire search in the terminal, with source attribution and response time\" width=\"720\">\n</p>\n\n```bash\nnpx @geowirehq/cli search \"Golden Gate Bridge\"    # terminal search with a results table\nnpx @geowirehq/cli search \"coffee\" --near 37.7749,-122.4194 --radius 2000   # near a coordinate\nnpx @geowirehq/cli reverse 37.8199,-122.4783      # coordinate → nearest place\nnpx @geowirehq/cli route 37.7749,-122.4194 37.8083,-122.4156   # driving directions (no key, OSRM)\nnpx @geowirehq/cli get google:ChIJ...             # one place by reference (getPlace-capable provider)\nnpx @geowirehq/cli                                # start the REST + MCP server (zero-config)\nnpx @geowirehq/cli init                           # interactive setup wizard (.env + config)\nnpx @geowirehq/cli test                           # check provider connections\n```\n\nAdd `--json` to any command for the full response (results + provenance `meta`).\n\n### 3. Docker — self-hosted server\n\n```bash\ndocker run -p 4980:4980 geowire/geowire\n# then:\ncurl -X POST http://localhost:4980/v1/places/search \\\n  -H 'content-type: application/json' \\\n  -d '{\"query\":\"coffee\",\"near\":{\"latitude\":37.7749,\"longitude\":-122.4194},\"radiusMeters\":2000}'\n```\n\nOr with `docker compose up` (see `docker-compose.yml`). API docs at `/docs`.\n\n### 4. SDK (embedded)\n\n```ts\nimport { createGeoWire } from \"@geowirehq/core\";\nimport { createNominatimProvider } from \"@geowirehq/provider-nominatim\";\n\nconst geo = createGeoWire({ providers: [createNominatimProvider()] });\nconst { results, meta } = await geo.searchPlaces({\n  query: \"coffee\",\n  near: { latitude: 37.7749, longitude: -122.4194 },\n  radiusMeters: 2000,\n});\n```\n\nFull embedded-SDK guide: [`examples/typescript-sdk.md`](./examples/typescript-sdk.md).\n\n## MCP tools\n\n| Tool | Description |\n|---|---|\n| `search_places` | Natural-language + coordinate/region place search |\n| `get_place` | Details by `provider:providerPlaceId` reference |\n| `geocode_address` | Address → coordinates (+ normalized address) |\n| `reverse_geocode` | Coordinates → nearest address |\n| `get_directions` | Route between waypoints (distance, time, legs) — no key (OSRM) |\n| `distance_matrix` | N×M travel distances/times — rank candidates by drive time — no key |\n| `analyze_area` | Commercial-area analysis: category density, competition, rating landscape, demographics |\n| `get_isochrone` | Travel-time reachability polygon (\"what's within a 15-min drive\") — no key (OSRM) |\n| `get_demographics` | Population / age / income for a coordinate's area (US Census, free key) |\n| `list_geo_providers` | Active providers, capabilities, status (agent self-awareness) |\n\nEvery response includes both a human-readable summary and `structuredContent`\n(schema-valid JSON).\n\n## REST endpoints\n\n| Method | Path | |\n|---|---|---|\n| POST | `/v1/places/search` | search |\n| GET | `/v1/places/{ref}` | place details (`provider:id`) |\n| GET | `/v1/geocode?address=` | geocode |\n| GET | `/v1/reverse-geocode?lat=&lon=` | reverse geocode |\n| POST | `/v1/directions` | directions between waypoints (no key) |\n| POST | `/v1/distance-matrix` | N×M travel distance/time matrix (no key) |\n| POST | `/v1/analyze-area` | commercial-area analysis (density, competition, ratings, demographics) |\n| POST | `/v1/isochrone` | travel-time reachability polygon (no key) |\n| GET | `/v1/demographics?lat=&lon=` | area demographics (US Census, free key) |\n| GET | `/v1/providers` | list providers |\n| GET | `/v1/health` | health check |\n| GET | `/metrics` | Prometheus metrics |\n| GET | `/docs` | Swagger UI (OpenAPI 3.1) |\n| POST | `/mcp` | MCP over Streamable HTTP |\n\nOptional Bearer auth: set `GEOWIRE_API_KEYS=key1,key2`.\n\n## Anatomy of a response\n\nNo black box. Every response carries a `meta` block: which providers were\n**used / skipped / failed** (and why), dedup counts, cache status, estimated\ncost, and per-field sourcing — so you always know where each value came from.\n\n```jsonc\n{\n  \"results\": [{\n    \"id\": \"gwp_CvWvRZrFtegkJPxP9CW0\",\n    \"name\": \"Golden Gate Bridge\",\n    \"location\": { \"latitude\": 37.8199286, \"longitude\": -122.4782551 },\n    \"sources\": [{\n      \"provider\": \"nominatim\",\n      \"providerPlaceId\": \"way/27385590\",\n      \"fields\": [\"name\", \"location\", \"categories\", \"address\"]   // ← what this source contributed\n    }],\n    \"attributions\": [\"© OpenStreetMap contributors\"]\n  }],\n  \"meta\": {\n    \"providersUsed\":   [{ \"provider\": \"nominatim\", \"resultCount\": 1, \"latencyMs\": 2449 }],\n    \"providersSkipped\": [],   // e.g. { provider: \"google\", reason: \"MISSING_CREDENTIALS\" | \"QUOTA_EXCEEDED\" }\n    \"providersFailed\":  [],   // e.g. { provider: \"google\", reason: \"TIMEOUT\" }\n    \"strategy\": \"first-success\",\n    \"cache\": { \"hit\": false }\n    // merging adds:  \"dedup\": { \"before\": 3, \"after\": 1 }\n    // paid provider: \"estimatedCostUSD\": 0.032\n  }\n}\n```\n\nAfter a merge, `sources[].fields` shows (say) the phone came from Google while\nthe coordinates came from OSM. Walkthrough: [docs/recipes.md](./docs/recipes.md#4-read-a-response-provenance--transparency).\n\n## Configuration (optional — everything works without it)\n\n`geowire.config.yaml`:\n\n```yaml\nproviders:\n  nominatim: { enabled: true }                       # default ON, no key (search/geocode)\n  osrm:      { enabled: true }                        # default ON, no key (directions)\n  google:    { enabled: true, apiKey: ${GOOGLE_MAPS_API_KEY} }\n  yelp:      { enabled: true }                        # env YELP_API_KEY (US/Western business & reviews)\n  census:    { enabled: true }                        # env CENSUS_API_KEY (US demographics, free)\n  kakao:     { enabled: true }                        # env KAKAO_REST_API_KEY (KR)\n  internal:  { enabled: true, source: ./my-places.csv, priority: 100 }\nrouting:\n  defaultStrategy: merge          # first-success | merge | cost-aware | weighted | fastest\n  providerWeights:                # for `weighted`: order by priority·cost·coverage\n    priority: 0.5\n    cost: 0.3\n    coverage: 0.2\nbudget:\n  perRequestMaxUSD: 0.10          # over-budget paid providers are skipped, free ones used\n```\n\nKeys come from the environment (`${VAR}`), never committed in plaintext.\n\n## Providers\n\n| Provider | Key? | Capabilities |\n|---|---|---|\n| `@geowirehq/provider-nominatim` (OpenStreetMap) | none | search, geocode, reverseGeocode |\n| `@geowirehq/provider-osrm` (OpenStreetMap routing) | none | route, distanceMatrix |\n| `@geowirehq/provider-google` (Maps Platform) | BYOK | search, geocode, reverseGeocode, getPlace, route, distanceMatrix |\n| `@geowirehq/provider-kakao` (카카오맵, KR) | BYOK `KAKAO_REST_API_KEY` | search, geocode, reverseGeocode |\n| `@geowirehq/provider-naver` (네이버 지역검색, KR) | BYOK `NAVER_CLIENT_ID`+`NAVER_CLIENT_SECRET` | search, geocode |\n| `@geowirehq/provider-baidu` (百度地图, CN) | BYOK `BAIDU_MAP_AK` | search, geocode, reverseGeocode |\n| `@geowirehq/provider-foursquare` (global POI) | BYOK `FOURSQUARE_API_KEY` | search, getPlace |\n| `@geowirehq/provider-yelp` (US/Western business, ratings & reviews) | BYOK `YELP_API_KEY` | search, getPlace |\n| `@geowirehq/provider-census` (US demographics) | BYOK `CENSUS_API_KEY` (free) | demographics |\n| `@geowirehq/provider-internal` (your CSV) | none | search |\n\nRegional providers make Korea (Kakao/Naver) and China (Baidu) coverage\nfirst-class where OSM is thin and Google has gaps — Baidu returns BD-09\ncoordinates, which GeoWire converts to WGS84 automatically. Merge them all +\nyour own store data into one deduped record.\n\n### Provider roles — each provider does what it's best at\n\nProviders aren't interchangeable; they're **complementary**. When `merge` combines\nduplicates, GeoWire doesn't just pick the highest-priority provider's whole record —\nit sources **each field from the provider that's authoritative for it**. Every\nprovider declares its strengths in its manifest (`fieldAuthority`), so one merged\nplace can carry OSM's coordinates, Google's reviews, and Kakao's local name at once:\n\n| Provider | Authoritative for | Role |\n|---|---|---|\n| Nominatim / OSM | `location`, `address` | base map geometry & addresses |\n| Google | `business` (rating, hours, **reviews**), `contact` | rich business data |\n| Foursquare | `business` (**photos**, price), categories | global POI specialist |\n| Kakao / Naver / Baidu | `name`, `address` | country-specific local names |\n| Internal (your CSV) | `name`, `contact`, `business` | your own data is the source of truth |\n\n`sources[].fields` in every response records which provider contributed which field.\nThis is the \"Stripe for Maps\" idea in code: you get one clean place record, and each\npart of it comes from whoever knows it best. (Reviews/photos are provider originals —\nthe policy engine enforces each provider's storage terms; Google originals aren't cached.)\n\nWant another provider? See [CONTRIBUTING.md](./CONTRIBUTING.md) —\n*\"Write a provider in 30 minutes\"*.\n\n## Recipes & examples\n\n- **[docs/recipes.md](./docs/recipes.md)** — end-to-end recipes: near+radius\n  search, merge + dedup, cost budgets, country routing, your own CSV, self-host.\n- **[examples/mcp-clients.md](./examples/mcp-clients.md)** — configs for Claude\n  Desktop/Code, Cursor, Cline, VS Code, Windsurf.\n- **[examples/typescript-sdk.md](./examples/typescript-sdk.md)** — embed the SDK.\n- **[examples/llm-tool-use.md](./examples/llm-tool-use.md)** — raw OpenAI /\n  Anthropic function calling. Also [LangChain](./examples/langchain.md) ·\n  [Vercel AI SDK](./examples/vercel-ai-sdk.md).\n\n## Roadmap\n\nv0.1 is deliberately \"It works\" scope. Honest about what's **not** in it yet:\n\n| Area | Shipped | Planned |\n|---|---|---|\n| Operations | search, geocode, reverse-geocode, get-place, directions, distance-matrix, area analysis, **isochrones** | **autocomplete** (typed, not wired) |\n| Strategies | `first-success`, `merge`, `cost-aware`, `weighted`, `fastest` | — (all 5 shipped) |\n| Field sourcing | **role-based merge** (each provider's authoritative fields) | per-field config overrides |\n| Routing providers | **OSRM** (no key), **Google Routes** (BYOK) | Mapbox, Valhalla, HERE |\n| Routing | explicit `country`, free-first cost ordering | country **inference** from coordinates |\n| Analysis | category density / competition / rating landscape, US demographics, activity proxy, **isochrones (travel-time catchment)** | real foot-traffic (paid data), exact isochrones (ORS/Valhalla), Korea demographics (SGIS) |\n| Cache | in-memory (LRU) | **Redis** adapter |\n| Providers | OSM, OSRM, Google, **Yelp**, Foursquare, Census, Kakao, Naver, Baidu, your CSV | Mapbox, HERE, TomTom, … (community PRs welcome) |\n| Rate limiting | per-provider (OSM 1 req/s) | global / per-endpoint |\n\n## Architecture\n\n```\nAI agent / app\n   │  MCP · REST · SDK\n   ▼\nGeoWire core  ── pipeline: plan → execute → normalize → dedup → rank → policy → cache\n   │  GeoProvider contract\n   ▼\nproviders: nominatim · osrm · google · yelp · foursquare · census · kakao · naver · baidu · internal · (community)\n```\n\nMonorepo packages: `schema` · `provider-sdk` · `provider-testkit` · `core` ·\n`providers/*` · `mcp` · `apps/server` · `cli`.\n\n## Documentation\n\n- [Recipes / cookbook](./docs/recipes.md) — task-oriented, copy-pasteable\n- [Examples](./examples/) — MCP clients, SDK, LangChain, AI SDK, tool use\n- [Contributing + write a provider](./CONTRIBUTING.md)\n- [System design](./GeoWire_system_design.md)\n\n## License\n\n[Apache-2.0](./LICENSE). GeoWire's code license is separate from the terms of\nthird-party map/place data providers — usage of Google, Mapbox, HERE, Kakao,\nNaver, etc. is governed by each provider's own terms. OSM data is under ODbL;\nGeoWire's policy engine enforces attribution and caching limits per provider.\n",
  "bytes": 16206,
  "sha": "9c7fe6055c3bb3edb6cf2e56136bba1a9591f0b575a620cc72e264aadbb34cd1",
  "repo_slug": "geowire/geowire",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_geowire_geowire_6a61170d/readme"
}