gridpin-mcp
OfficialGridPin MCP server lets AI agents manage country sheets and perform offline geocoding.
List covered countries with sheet sizes and download status.
Download a country sheet from dl.gridpin.dev, verifying SHA-256 against the signed release (only network-using tool; idempotent).
Forward geocode one address/place to ranked coordinates, with
kcandidates 1–10.Reverse geocode coordinates to the nearest indexed addresses, approximately, with
distance_m.Batch geocode up to 1000 addresses of one country in input order.
Return candidate details such as lat/lon, precision, confidence, score, street, housenumber, commune, postcode, region, flags, weak, and distance where applicable.
GridPin
A country in a file — offline forward and reverse geocoding.

Quick start
$ pip install gridpin gridpin-mcp
$ gridpin-fetch fr # France sheet, 365 MB, checked against the signed release; prints its pathimport gridpin
from gridpin_mcp import sheets
g = gridpin.Geocoder(str(sheets.download("fr"))) # no-op once the sheet is downloaded
g.geocode("1 rue de la Paix, Paris", 1)No install: try it in Colab — the Serbia sheet, forward, reverse and batch geocoding in five minutes.
Countries: us ca de fr it es au mx nl br rs (gridpin-fetch --list). For AI agents there is an
MCP server — claude mcp add gridpin -- uvx gridpin-mcp, details in mcp/README.md.
Everything after the download runs offline.
If GridPin is useful to you, a star on GitHub helps other people find it.
Ways to use it — one engine and the same country files behind each: CLI · spreadsheets (CSV) · local Nominatim-compatible server · Docker · Python · GeoPandas · R · Node.js · DuckDB · QGIS · AI agents (MCP) · LangChain · Rust. The whole list with what each one needs: documentation.
GridPin is an offline geocoder: a country in a data file, memory-mapped and ready in milliseconds, with no server, no database import, and no network. It resolves addresses to coordinates (forward) and coordinates to the nearest indexed address (reverse — approximate: streets are indexed by centroid, so each result carries its distance_m). On a single arm64 laptop core, measured on the France sheet: exact address lookups run at ~3,000/s, reverse geocoding at ~1,800/s, and typo-tolerant fuzzy matching much slower — tens of milliseconds per query, so batch it across cores when you feed it messy input. The engine and every query run fully offline — not a byte of what you geocode leaves your machine; the only network step is you downloading a sheet yourself, and a downloaded sheet carries no query data. Because you compute results locally from openly licensed data, you may store them forever, which hosted geocoding APIs typically forbid. If you have considered self-hosting Nominatim (a beefy server, days of import) or an on-prem geocoder license, the alternative here is: download one file, open it on any laptop.
Related MCP server: Geo MCP Worker
Install
CLI — prebuilt binaries for Linux x86_64 (x86_64-unknown-linux-gnu),
macOS Apple Silicon (aarch64-apple-darwin) and Windows x86_64
(x86_64-pc-windows-msvc) are attached to every
GitHub release; other targets
(Intel macOS, Linux arm64/musl, Windows arm) build from source:
$ base=https://github.com/gridpin/gridpin/releases/download/v0.8.0
$ curl -fsSLO "$base/gridpin-aarch64-apple-darwin.tar.gz"
$ curl -fsSLO "$base/gridpin-release-signers"
$ curl -fsSLO https://dl.gridpin.dev/v0.8.0/attestation.json
$ curl -fsSLO https://dl.gridpin.dev/v0.8.0/attestation.json.sig
# the trust root comes from the OTHER channel — see "Verifying a release" for why
$ ssh-keygen -Y verify -f gridpin-release-signers -I gridpin-release \
-n gridpin-g02 -s attestation.json.sig < attestation.json \
|| { echo "signature does not verify — STOP"; exit 1; }
$ want=$(python3 -c 'import json;print([a["sha256"] for a in json.load(open("attestation.json"))["assets"] if a["name"]=="gridpin-aarch64-apple-darwin.tar.gz"][0])')
$ test "$want" = "$(shasum -a 256 gridpin-aarch64-apple-darwin.tar.gz | cut -d' ' -f1)" \
|| { echo "archive does not match the signed attestation — STOP"; exit 1; }
$ tar xzf gridpin-aarch64-apple-darwin.tar.gz && sudo mv gridpin /usr/local/bin/Yes, that is longer than curl | tar. Unpacking an archive runs its contents sooner or later, and
a checksum published beside the file it describes proves only that the download was not truncated —
whoever can replace one can replace both. The four extra lines are what makes it a check.
Or build from source with stable Rust: cargo build --release --manifest-path gridpin/Cargo.toml.
Python — pip install gridpin. This one
rests on PyPI's own integrity, not on the signature above: the attestation covers the artifacts we
publish ourselves. If you need the signed chain, take the wheel from the GitHub release and verify
it the same way as the CLI archive.
DuckDB — INSTALL gridpin_ext FROM community; then LOAD gridpin_ext;. The community
catalog builds and signs the extension itself, so that trust root is DuckDB's rather than ours.
It builds the current release for the current DuckDB lines: that takes DuckDB 1.5.6 or newer;
older DuckDB versions install an older build. Check what you got with
SELECT extension_version FROM duckdb_extensions() WHERE extension_name = 'gridpin_ext';.
When the catalog is unreachable, or for the current release on DuckDB 1.5.5, download
gridpin_ext-<platform>.zip from the releases page: it is built for DuckDB 1.5.5 only. It is a loadable
extension, so authenticate it before you load it — same three lines as the CLI archive above,
with gridpin_ext-<platform>.zip as the asset name:
$ ssh-keygen -Y verify -f gridpin-release-signers -I gridpin-release \
-n gridpin-g02 -s attestation.json.sig < attestation.json \
|| { echo "signature does not verify — STOP"; exit 1; }
$ want=$(python3 -c 'import json;print([a["sha256"] for a in json.load(open("attestation.json"))["assets"] if a["name"]=="gridpin_ext-osx_arm64.zip"][0])')
$ test "$want" = "$(shasum -a 256 gridpin_ext-osx_arm64.zip | cut -d' ' -f1)" \
|| { echo "extension does not match the signed attestation — STOP"; exit 1; }Then unzip it and LOAD the extracted gridpin_ext.duckdb_extension in a duckdb -unsigned
session — keep
that basename, DuckDB derives the entry symbol from it.
Quick start
CLI
# forward: address -> coordinates, top 3 candidates
$ gridpin query france.bin "1 rue de la Paix Paris" -k 3
# reverse: coordinates -> nearest indexed address (approximate; each hit carries distance_m)
$ gridpin reverse france.bin 48.8686 2.3305
# batch: JSONL in ({"q": "..."} per line), JSONL out
$ gridpin batch france.bin queries.jsonl results.jsonl -k 1
# a spreadsheet: CSV or TSV in, the same file with gp_lat, gp_lon, gp_precision, ... out
$ gridpin batch usa.bin addresses.csv geocoded.csv
$ gridpin batch usa.bin orders.csv geocoded.csv --column "street,city,zip" --delimiter ";"
# a local geocoding server: own JSON API plus Nominatim-compatible /search and /reverse
$ gridpin serve usa.bin --port 8080Python
$ pip install gridpinimport gridpin
g = gridpin.Geocoder("france.bin")
g.geocode("1 rue de la Paix Paris", 1)
g.reverse(48.8686, 2.3305, 1)With pandas: pip install "gridpin[pandas]", then gridpin.sheets.fetch("us"), gridpin.geocode_df(df, "address", "us") (or df.gridpin.geocode(...)) and gridpin.reverse_df(df, "us"); the gp_* columns are the same as in gridpin batch CSV and gp_geocode_df in R. gridpin.sheets.list / path / verify / fetch use the same cache and checks as gridpin sheets. Extras: [pandas], [geopy]; without them the wheel still has no dependencies.
The wheel wraps the Rust engine directly (pyo3/maturin); on a single core, exact lookups run at ~3,000/s, while typo-heavy input is fuzzy-matched at tens of milliseconds per query — use geocode_many() to batch across cores.
DuckDB
The gridpin_ext extension exposes five functions: gridpin_load, gridpin_load_poi (optional POI layer), gridpin_reset, gridpin_geocode, gridpin_reverse. One country per session — the loaded path may not change while it is loaded (a different path is an error, so a path column can never swap the index in the middle of a running query); to switch countries run SELECT gridpin_reset(); (a standalone statement — it refuses to run inside a multi-row query) and load the new sheet. The loaded sheet is shared by every connection to the same database process: coordinate switches externally when several connections query concurrently.
LOAD gridpin_ext;
SELECT gridpin_load('france.bin');
SELECT gridpin_geocode(address) FROM orders;
SELECT gridpin_reverse(lat, lon) FROM pings;Countries & data
A country in a file — most countries are a single file (a few regional datasets add a small companion file, and the optional POI layer is its own file). We call a country file a sheet, the full set an atlas, and a monthly data release fresh sheets.
Country | File size | Addresses | Source | Data license | POI layer |
Australia | 130 MB | 11.92M | Overture / G-NAF (Geoscape Australia) | Open G-NAF EULA | — |
Brazil | 677 MB | 67.19M | Overture / IBGE CNEFE 2022, nationwide | CC0 1.0 (IBGE public data) | — |
Canada | 183 MB | 16.94M | Overture / Statistics Canada NAR, Adresses Québec, Yukon | open data (per source) | — |
France | 365 MB | 26.1 M | BAN (national registry) | Licence Ouverte | ✓ (231 MB, optional) |
Germany | 213 MB | 19.27M | Overture / OpenAddresses, 15 Länder; BNetzA postcode witness | dl-de/by-2-0 · dl-de/zero-2-0 · CC BY 4.0 (per source) | — |
Italy | 247 MB | 25.9 M | Overture / ANNCSU | CC BY 4.0 | — |
Mexico | 156 MB | 14.14M | Overture / INEGI address layer, Correos de México postal codes | open data (per source) | — |
Netherlands | 103 MB | 9.9 M | Overture / NGR (BAG) | Public Domain Mark 1.0 | — |
Serbia | 32 MB | 2.6 M | Overture / RGZ | data.gov.rs Terms | — |
Spain | 185 MB | 15.27M | Overture / CartoCiudad (IGN/CNIG), all 52 postal provinces | CC BY 4.0 | — |
United States | 1.45 GB | 95.33M | Overture / NAD + Census TIGER/Line; 7 states by address ranges only | open data (per source) | — |
Germany covers 15 of 16 Länder; Bavaria is not covered. In local spot checks, Bavarian queries typically returned a distant same-named address with low confidence; less often, an empty answer. Neither outcome establishes coverage. These spot checks illustrate failure modes, not population rates. The release sheet contains 19,267,049 addresses, 213,315,143 bytes, and no POI layer. Per-Land providers and licenses are in ATTRIBUTIONS.md.
POI coverage is per-country (see the table; we ship a layer only where source data is rich
enough to help). For France, pass the layer and
the engine cascades automatically — address index first, POI only on weak results, exact
addresses never overridden: gridpin query france.bin "pharmacie gare de Lyon" --poi fr_poi.bin
or gridpin.Geocoder("france.bin", poi="fr_poi.bin").
The cascade precedence, identical for free-form and structured input:
Address answer | POI answer | Winner |
confident (e.g. exact house match) | — (not consulted) | address |
weak (city-level / low confidence / weak fuzzy) | more confident | POI, flagged |
weak | equal or less confident | address (ties keep the address) |
empty | any hit | POI, flagged |
empty | empty / no layer loaded | empty |
Structured input feeds EVERY provided token to the POI escalation, joined in the canonical
street number postcode city order — a number is often part of a place name ("Studio 54"). The
two input styles agree on the winner for address resolution and for POIs whose tokens are in that
canonical order. They can differ only when the fields you supply reorder the words of an
order-sensitive POI name (splitting "54 Studio" into street:"studio", number:"54" re-forms it as
"Studio 54"); pass such a query as free-form to keep the original word order.
Sizes are for v7 builds (August 2026; Germany repacked 2026-09-10; Spain added 2026-09-28; Brazil added 2026-10-11; the United States added 2026-10-24; Canada added 2026-10-31; Mexico added 2026-11-02; Australia added 2026-11-06); the exact byte sizes are checked against a pinned source of truth in CI. A sheet is smaller than its own raw source: France's registry download is an 885 MB gzip that unpacks to 4.9 GB of CSV, while the finished sheet — the same 26.1M addresses plus every index (typo automata, spatial cells, ranking) — is 365 MB, ready to query.
Download sheets. Free builds of all available countries are served as individual files; download only the file of the country your addresses are in:
File | What's inside | Size |
Australia. 11.92M addresses in all states and territories, including the external territories. | 130 MB | |
Brazil. 67.19M addresses in 26 states and the Federal District. | 677 MB | |
Canada. 16.94M addresses in all provinces and territories. | 183 MB | |
France. 26.1M addresses from the national registry (BAN). | 365 MB | |
Germany. 19.27M addresses in 15 of 16 Länder. Bavaria is not covered. | 213 MB | |
Italy. 25.9M addresses. | 247 MB | |
Mexico. 14.14M addresses in all 32 states, from an address layer surveyed in 2009–2010. | 156 MB | |
Netherlands. 9.9M addresses. | 103 MB | |
Serbia. 2.6M addresses. | 32 MB | |
Spain. 15.27M addresses in all 52 postal provinces, including Ceuta and Melilla. | 185 MB | |
United States. 95.33M point addresses plus 34.52M points from address ranges, in 50 states and the District of Columbia. 7 states have address ranges only. | 1.45 GB | |
France, places. Optional add-on to | 231 MB |
The matching SHA256SUMS manifest (covering the
engine binary and every sheet) is on the GitHub release.
Subscribers fetch fresh monthly builds from a keyed URL instead — see
gridpin.dev/docs.
Quality
GridPin is built for addresses as people type them — typos, shortened street types, missing commas, the town written first. Share of queries whose first answer lands within 150 m of an independent reference coordinate (empty answers count as misses):
Query set | Rows | GridPin | Photon | Nominatim |
United States · distorted addresses | 3,655 | 32.9% | 28.5% | 24.3% |
United States · historic-places register (as recorded) | 1,000 | 64.1% | 38.9% | 52.3% |
Canada · distorted addresses | 2,785 | 55.9% | 45.2% | 32.7% |
Canada · radio-station sites (as recorded) | 1,000 | 39.3% | 39.3% | 25.2% |
Germany · distorted addresses | 2,000 | 82.1% | 46.9% | 15.8% |
Germany · clean addresses | 2,000 | 82.8% | 83.2% | 81.2% |
Spain · distorted addresses | 1,590 | 33.8% | 29.6% | 20.8% |
Mexico · distorted addresses | 2,000 | 24.1% | 13.8% | 7.7% |
Mexico · cultural venues (as recorded) | 1,000 | 28.0% | 17.3% | 10.2% |
Australia · distorted addresses | 2,000 | 74.6% | 50.6% | 42.2% |
Australia · radio sites (as recorded) | 1,000 | 65.8% | 25.2% | 13.0% |
Brazil · distorted addresses | 2,376 | 20.7% | 19.8% | 9.6% |
Brazil · health-facility register (as typed) | 1,000 | 36.8% | 28.2% | 20.7% |
Distorted means each real address was changed by one fixed mechanical rule, with the reference coordinate kept byte for byte. On German and Spanish distorted addresses GridPin leads both services; on Brazilian distorted addresses GridPin and Photon are level, both well ahead of Nominatim; on clean German addresses GridPin and Photon are level (p=0.6691367 — not proof of parity). We show the sets where GridPin leads or is level; on the other Spanish sets Photon leads, and on clean Brazilian addresses Photon leads too; on clean Canadian addresses Photon leads (59.1% vs 57.5%; Nominatim 57.2%); on clean Mexican addresses GridPin leads (29.2% vs Photon 15.9%, Nominatim 10.1%). Counts, paired tests and data fingerprints for every set are in docs-public/BENCHMARK.md; the summary lives on the test bench page.
Compatibility. GridPin answers in the Photon API format, forward and reverse (the reverse endpoint is implemented since 2026-08-06). On the external geocoder-tester suite — a compatibility fixture set, not an accuracy oracle — the full official Alsace numbered-address file passes 230/285 (2026-08-07; 0 → 173 → 208 → 230 as the adapter matured).
Two more axes where the comparison is not close: speed — ~3,000 exact lookups per second on a single arm64 core, with no network round-trip — and cost and privacy: no per-query fee at any volume, and not one query leaves your machine.
CI runs cargo test on three operating systems, plus a public smoke test that builds a tiny Monaco
sheet from an OSM extract and runs live queries against it.
How it works
A country sheet is a single self-contained index file designed to be memory-mapped: opening it is effectively instant, there is no import into a database and no daemon to keep warm, and the first query returns in milliseconds from a cold start. Inside the file, sections are laid out for lazy access: an FST over the normalized lexicon for exact and prefix matching, spatial cells for reverse lookup and proximity ranking, typo automata for bounded-edit-distance fuzzy matching, and optional ML sections for query parsing and candidate ranking. The engine touches only the pages a query needs, which is why lookups stay in the millisecond range even on modest hardware.
Country-specific behavior lives in the data, not the binary. Normalization and parsing rule tables ship inside the sheet itself as a dedicated section, so a monthly data release can fix a country's quirks — abbreviations, renamed streets, administrative reshuffles — without an engine upgrade; engine and data are versioned independently. This repository contains the public parts: the Rust engine and CLI, the Python bindings, the DuckDB extension, the data pipeline (DuckDB SQL under prep/), and the docs. The rule tables (shipped inside paid data files), the evaluation corpora, and the ML training code are private.
Verifying a release
Every release is covered by one signed attestation. The signature spans the asset graph — the
sheets, the binaries and this checker, each by size and two hashes. It does not cover
SHA256SUMS itself: that file lists the proofs, so it cannot be inside them. The checksums are
still pinned, because every line in them must match the signed graph — but the authority is the
attestation, not the checksum file. Two properties make the check worth anything, and
both are about order and source, not about running more commands.
The trust root never comes from the channel it checks. gridpin-release-signers (the public
half of the signing key) and verify_release.py come from the GitHub release; the data files
and the proofs come from dl.gridpin.dev. Whoever controls one channel cannot hand you both a
forged file and the key that blesses it.
Nothing you downloaded runs until it has been authenticated. verify_release.py is an ordinary
Python program: running it first and checking signatures afterwards would mean executing an
untrusted download. Steps 3–5 below use only ssh-keygen and shasum, which you already trust,
and only step 6 runs our code.
mkdir gridpin-v0.8.0 && cd gridpin-v0.8.0
mkdir trust data # two directories on purpose: the checker never sits in what it checks
gh_base=https://github.com/gridpin/gridpin/releases/download/v0.8.0
# 1. TRUST ROOT — from GitHub, never from the download host
curl -fsSL -o trust/gridpin-release-signers "$gh_base/gridpin-release-signers"
curl -fsSL -o trust/verify_release.py "$gh_base/verify_release.py"
# 2. Data and proofs — from the download host
for f in france.bin germany.bin italy.bin netherlands.bin serbia.bin fr_poi.bin spain.bin brazil.bin usa.bin canada.bin mexico.bin \
SHA256SUMS attestation.json attestation.json.sig; do
curl -fsSL -o "data/$f" "https://dl.gridpin.dev/v0.8.0/$f"
done
# 3. The key is the one published below — compare by eye, before anything else
ssh-keygen -lf trust/gridpin-release-signers
# expected: SHA256:Y1Q/c9oMgST5VkyU78Py/XM5QiQP8KXwe0fopZA+YXA
# 4. The attestation really is signed by that key (system ssh-keygen, not our code)
ssh-keygen -Y verify -f trust/gridpin-release-signers -I gridpin-release \
-n gridpin-g02 -s data/attestation.json.sig < data/attestation.json \
|| { echo "signature does not verify — STOP"; exit 1; }
# 5. The checker you downloaded is the one that signature covers.
# Its expected hash comes out of the attestation you just authenticated.
want=$(python3 -c 'import json;print([a["sha256"] for a in json.load(open("data/attestation.json"))["assets"] if a["name"]=="verify_release.py"][0])')
test "$want" = "$(shasum -a 256 trust/verify_release.py | cut -d" " -f1)" \
|| { echo "checker does not match the signed attestation — STOP"; exit 1; }
# 6. Only now run it, from outside the directory it judges.
python3 trust/verify_release.py --dir data --channel r2 \
--allowed-signers trust/gridpin-release-signers \
--expected-public-sha <commit from release notes>Expected output: OK: owner signature verified; N assets, checksums and attestation agree.
Any other exit code means do not use the files.
Step 3 is the one thing a machine cannot do for you. The published fingerprint is
SHA256:Y1Q/c9oMgST5VkyU78Py/XM5QiQP8KXwe0fopZA+YXAIf it ever differs and no signed announcement explains the change, stop and write to contact@gridpin.dev.
Data licensing & attribution
The engine is Apache-2.0. Each sheet carries the license of its source data:
United States — National Address Database (USDOT) and 19 state and county open datasets via Overture/OpenAddresses, each under its own permissive licence (U.S. Government work, CC0, PDDL, CC BY 4.0, open-data terms — listed per source in the sheet manifest); address ranges from U.S. Census Bureau TIGER/Line 2025 (U.S. Government work); postal codes from GeoNames (CC BY 4.0).
Canada — Statistics Canada National Address Register under the Statistics Canada Open Licence (required notice: "Adapted from Statistics Canada, National Address Register, 2024. This does not constitute an endorsement by Statistics Canada of this product."); Adresses Québec (MRNF) under CC BY 4.0; Yukon civic addresses under the Open Government Licence – Yukon ("Contains information licensed under the Open Government Licence – Yukon"); all via Overture/OpenAddresses; settlement names from GeoNames (CC BY 4.0).
Mexico — "Fuente: INEGI, Vectorial de localidades amanzanadas y números exteriores (actualización 2009-2010), via AddressForAll"; postal codes: Correos de México (Libre Uso MX). GridPin's sheet is a derived work, not an INEGI product, and INEGI does not endorse it.
Australia — "Incorporates or developed using G-NAF © Geoscape Australia licensed by the Commonwealth of Australia under the Open Geo-coded National Address File (G-NAF) End User Licence Agreement." Adapted by GridPin. That licence does not allow using this data to compile addresses for sending mail unless each address is verified against a second source.
France — BAN, under Licence Ouverte 2.0.
Italy — ANNCSU via Overture/OpenAddresses, under CC BY 4.0.
Netherlands — Nationaal Georegister (BAG) via Overture, under Public Domain Mark 1.0.
Serbia — RGZ via Overture, under data.gov.rs Terms of use; settlement-name aliases from GeoNames (CC BY 4.0).
POI layer — Overture places, mixed permissive: CDLA-Permissive-2.0 (Meta, Microsoft, …), Apache-2.0 (Foursquare), CC0-1.0 (AllThePlaces).
Per the Overture attribution page, addresses are not one blanket license — each national source carries its own. Consult your own counsel before commercial redistribution.
All of these licenses let you store geocoding results indefinitely. This is a real difference from hosted APIs: Google's terms forbid storing results and allow caching coordinates for at most 30 days, and Mapbox charges roughly 7.5× for permanent storage — at list prices of about $3,550 (Google) to $4,500 (Mapbox) per million requests per month before you even ask to keep the output (List prices as of 2026-08, 1M requests/month; see the linked provider terms). With GridPin the computation happens on your hardware, so the results are yours from the start. The grid is ours. The pin is yours.
This also matters for GDPR: the addresses you geocode are often customer data, and with GridPin they never leave your infrastructure.
Status & roadmap
v0.8.0: Australia added. Client update required. One nationwide sheet (australia.bin, 130 MB): 11.92M addresses from G-NAF, August 2026 edition, including the external territories. Engine rules for Australian addresses (see docs). Also: a long dash in a house range reads like a hyphen, and a place name before an address is read past, in every country.
v0.7.1: the engine knows the sheets of its own release. In 0.7.0, gridpin sheets, gridpin.sheets.fetch, the npm sheets.fetch and the Docker images listed the previous release and had no Mexico; in 0.7.1 they list all ten countries, and the Mexico image ghcr.io/gridpin/gridpin:mx is published. The sheets are byte-for-byte the same as in 0.7.0; the new sheets-receipt.json (with its .sig) is the signed list of sheets, signed before the engine is built, and the engine pins it by SHA-256.
v0.7.0: Mexico added, plus spreadsheets, a local server and Docker. Client update required. One nationwide sheet (mexico.bin, 156 MB): 14.14M point addresses from the INEGI address layer (surveyed 2009–2010) with Correos de México postal codes. Engine rules for Mexican addresses (see docs). Also new: CSV/TSV in gridpin batch; gridpin serve with its own API and Nominatim-compatible /search and /reverse; Docker images per country; the weak field; gridpin sheets; gridpin parse and gridpin same; the npm package; gridpin[pandas] and gridpin[geopy].
v0.6.0: Canada added. Client update required. One nationwide sheet (canada.bin, 183 MB): 16.94M point addresses in all provinces and territories from the Statistics Canada National Address Register, Adresses Québec and the Government of Yukon (via Overture Maps, release 2026-10-21.0); answers carry the Canadian postal code. Older engines have no Canadian rules (place written before the address, Edmonton and Calgary quadrants, French street types in front, "A9A 9A9" postal codes). Use the 0.6.0 client with this release. All nine country sheets and the France POI layer use the new immutable v0.6.0/ path and a new attestation; v0.1.0/ to v0.5.0/ are unchanged.
v0.5.0: the United States added. Client update required. One nationwide sheet (usa.bin, 1.45 GB): 95.33M point addresses from sources with an explicit right to redistribute (National Address Database, New York City, Los Angeles, Oakland County, MassGIS, MnGeo, Oregon and other state and county open data) plus 34.52M address-range points from Census TIGER/Line 2025 where no point addresses exist. Point addresses cover 35 states; in CO, FL, HI, MS, NH, NV and SC answers are interpolated from address ranges (tens to hundreds of metres). Older engines have no US rules (trailing street types, directions, ZIP+4, state tails). Use the 0.5.0 client with this release. All eight country sheets and the France POI layer use the new immutable v0.5.0/ path and a new attestation; v0.1.0/ to v0.4.0/ are unchanged.
v0.4.0: Brazil added. Client update required. Brazil is one nationwide sheet covering 26 states and the Federal District; the source counts describe source presence, not address completeness. Spain covers all 52 postal provinces (incl. Ceuta and Melilla). Germany covers 15 of 16 Länder; Bavaria is not covered. Use the 0.4.0 client with this release: older engines do not contain the Brazilian rules. All seven country sheets and the France POI layer use the new immutable v0.4.0/ path and a new attestation; v0.1.0/, v0.2.0/ and v0.3.0/ are unchanged.
For an existing Python installation, use pip install --upgrade gridpin==0.8.0, not a plain install that may keep an older version.
Interfaces and file formats may still change before v1.0. Sheets are tied to the engine's format major version — keep the engine build that shipped alongside your sheets; before v1.0 a newer engine may require newer sheets.
Distribution:
Free — static builds of all available countries, with no update schedule. Free builds carry the same curated rules section subscribers get — you trade freshness, not quality; fresh monthly sheets are what subscriptions are for.
Starter, $59/mo (in preparation) — a subscription: one country of your choice, a fresh sheet every month.
Pro, $149/mo — a subscription: the full atlas (all available countries), fresh sheets monthly, provenance journal per build, invoice billing, priority email support.
Snapshot, $199 one-time — this month's fresh build of all available countries (the same sheets subscribers get), with an invoice, without a subscription. No updates afterwards. It costs more than one month of Pro on purpose: it is the no-subscription option — one invoice for procurement, no card on file, nothing to cancel. If a subscription works for you, a month of Pro is cheaper.
OEM / embedding — from $1,500/yr.
Pro and Snapshot can be bought in USDC: see pricing and how buying works.
License
The code in this repository is licensed under Apache-2.0. Data files are licensed separately by source — see Data licensing & attribution.
Available Tools
5 toolsbatchARead-only
Geocode up to 1000 addresses of one country in a single call, in input order.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | ||
| country | Yes | ||
| queries | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| country | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare that this is a read-only, non-destructive, closed-world geocoding call; the description adds genuinely new behavior with the maximum batch size of 1000 and the guarantee that results are returned in input order. These extra details, along with the fact that results preserve input order, help the agent predict and trust the returned results, and because an output schema exists, the return format need not be described. The description does not disclose what happens when individual addresses fail to geocode, which is the main remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence that gives the action, the scale limit, the scoping constraint, and the ordering guarantee, with no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the annotations that already cover the safety profile and the output schema that defines the return values, the definition covers the essentials: batch size, single-country scope, and order preservation. The gaps concern what the 'k' parameter means and how per-address failures are handled, which matter for a batch tool where partial failures are likely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must carry the parameter meaning. It does add meaning for two parameters: 'addresses' identifies the queries array, and 'of one country' explains that country is a single-country constraint that applies to the whole batch. However, the parameter 'k' is completely unexplained here and in the schema, and the description gives no format or syntax guidance for the query strings.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It gives a specific verb (geocode), a specific resource (addresses), and concrete limits: up to 1000 per call and one country per call. That makes the tool easy to tell apart from the sibling tool geocode, which is not batched, and from reverse. It does not explicitly name those siblings, so the score is 4 rather than 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'up to 1000 addresses ... in a single call' implies when to batch instead of calling geocode one address at a time, and 'of one country' states a constraint on use. However, the description never says when not to use this tool, never names geocode or reverse as alternatives, and does not state whether calls with more than 1000 queries are rejected or truncated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
download_sheetAIdempotent
Download one country sheet from dl.gridpin.dev and verify its SHA-256 against the signed release. Needed once per country; the only tool that uses the network.
country: ISO code or English name, e.g. "fr" or "France".
| Name | Required | Description | Default |
|---|---|---|---|
| country | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | |
| country | Yes | |
| size_mb | Yes | |
| already_downloaded | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare openWorldHint=true, idempotentHint=true, destructiveHint=false and readOnlyHint=false, so the safety profile is partly covered. The description adds genuinely new behavioral facts: the download is integrity-checked against a signed release via SHA-256, and it is the sole networked tool. It does not say where the sheet lands on disk or what happens on checksum mismatch, which is the main remaining gap for a non-read-only tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core action and its scope are front-loaded in the first sentence, and the parameter note is short and separated. The multi-line indented formatting is slightly awkward but no sentence is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, the description covers what is needed to invoke it: source, integrity verification, network requirement, and accepted input formats. The missing piece is what the tool returns or writes locally on success/failure, which the output schema may partially cover.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the only property is an untyped-in-practice string named country, so the description must compensate and largely does: it accepts an ISO code or an English name and gives concrete examples ("fr" or "France"). It could note case/format handling but the essential meaning is supplied.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ("Download one country sheet") and pins the source host (dl.gridpin.dev). It also differentiates itself from siblings by declaring it is "the only tool that uses the network", so an agent can separate it from list_countries, geocode, reverse and batch without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Needed once per country" gives a clear usage cadence and implies the fetch is a prerequisite step rather than a repeated call. It stops short of naming an explicit alternative or a when-not condition (e.g. do not call if the sheet is already cached), so it is strong but not fully routing-grade guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
geocodeARead-only
Geocode one address or place: text -> coordinates, offline.
query: the address as a person would write it, e.g. "1 rue de la Paix, Paris".
country: ISO code or English name of the country the address is in.
k: number of ranked candidates, 1-10.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | ||
| query | Yes | ||
| country | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| query | Yes | |
| country | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The 'offline' note adds real behavioral context (results come from a local dataset, so coverage and currency are bounded), though openWorldHint=false already signals no external calls. The description does not disclose what happens with unmatched addresses or whether candidates carry confidence scores, both of which matter for a geocoder.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One front-loaded purpose line followed by a compact, indented parameter list – no wasted prose. The trailing blank/indented layout is slightly untidy but costs nothing in comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and all three parameters are documented despite empty schema descriptions. The remaining gap is routing guidance against the reverse and batch siblings, which an agent would need in order to pick correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description carries the full burden and does so for all three parameters: query format with a concrete example, country as ISO code or English name, and k as ranked candidates bounded 1-10 (a range the schema omits). It stops short of explaining what distinguishes the ranked candidates.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Geocode one address or place') plus the transformation direction ('text -> coordinates') and a key property ('offline'). The directionality implicitly separates it from the sibling 'reverse', but no sibling is named explicitly, so an agent must infer the contrast rather than read it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'One address' implies single-item use, which hints that 'batch' handles many, but that alternative is never named and no when-not guidance is given. Nothing tells the agent to prefer 'reverse' for coordinates-to-text or to handle ambiguous multi-candidate cases with k>1.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_countriesARead-only
List covered countries, their sheet sizes and whether each sheet is downloaded.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| home | Yes | |
| release | Yes | |
| countries | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds that results include sheet sizes and whether each sheet is already downloaded, which hints at state awareness, but goes no further (no refresh behavior, no ordering).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence that names the resource first and then the useful payload fields. Nothing superfluous.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-argument read tool with an output schema, this is nearly complete; the schema covers return values and annotations cover safety. It only lacks a pointer that this is the entry point for the download_sheet workflow.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4 and no compensating parameter explanation is needed. The description does not mislead about any inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb ("List") plus resource ("covered countries") and it also states what the response contains (sheet sizes and download status). This clearly separates it from siblings like download_sheet and geocode, though it never names those siblings explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Use is only implied: it is obviously the discovery step before download_sheet, but the description never says when to call it versus the other tools or whether it is a prerequisite. No exclusions or alternatives are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reverseCRead-only
Reverse geocode: coordinates -> nearest indexed addresses, offline. Approximate: each result carries distance_m from the given point.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | ||
| lat | Yes | ||
| lon | Yes | ||
| country | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| lat | Yes | |
| lon | Yes | |
| country | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/openWorld=false annotations, the description adds two useful traits: the lookup is 'offline' (no network dependency) and results are 'approximate'. However, it says nothing about result ordering, what happens when the index has no nearby address, or coverage limits per country, which matter for a closed-world lookup.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short, front-loaded sentences with no filler; the direction of the mapping comes first and the caveat second. It is arguably too terse rather than too long, but nothing written is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value explanation is not required, but the definition leaves critical invocation details missing: what 'k' controls, why 'country' is mandatory, and what an empty result looks like. For a four-parameter tool with zero schema descriptions, this is under-specified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the description adds almost nothing: 'given point' vaguely covers lat/lon, but the 'k' parameter (default 1, presumably result count) and the required 'country' filter are completely unexplained. With four bare parameters, the description needed to compensate and does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb+resource ('reverse geocode') and spells out the input→output mapping ('coordinates -> nearest indexed addresses'), which cleanly separates it from the forward 'geocode' sibling without naming it. It stops short of the 5 mark because it never explicitly contrasts itself with geocode or batch.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use or when-not-to-use guidance, and no mention of the obvious alternatives (geocode for address→coordinates, batch for many points). The agent must infer routing from the word 'reverse' alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.6.1- Changed
batch1 field changed- added
Output schema / $defs / Hit / properties / weakAdded value: +{ + "title": "Weak", + "type": "boolean" +}
- Changed
geocode1 field changed- added
Output schema / $defs / Hit / properties / weakAdded value: +{ + "title": "Weak", + "type": "boolean" +}
- Changed
reverse1 field changed- added
Output schema / $defs / Hit / properties / weakAdded value: +{ + "title": "Weak", + "type": "boolean" +}
5 tool updates
v0.1.0- First observed
batch - First observed
download_sheet - First observed
geocode - First observed
list_countries - First observed
reverse
TDQS
Scored across 5 tools
Each tool targets a distinct stage of the workflow: listing datasets, downloading a sheet, single forward geocode, reverse geocode, and bulk forward geocode. The only soft overlap is geocode vs batch, but the descriptions clearly separate one-address from up-to-1000-address usage.
list_countries and download_sheet follow a clean verb_noun pattern, but geocode and reverse are bare verbs and batch is a bare noun. The set is still readable, but conventions are mixed rather than uniform.
Five tools is well-scoped for an offline geocoding-dataset server: one setup tool, one metadata tool, and three query tools covering single, reverse, and bulk lookup. Nothing feels padded or missing at the count level.
Core lifecycle is covered: discover countries, fetch and verify sheets, then geocode forward, reverse, and in bulk. Minor gaps exist around managing already-downloaded data (no removal/refresh/version-check tool) and no multi-country batch, but agents can work around these.
Maintenance
Related MCP Connectors
Free GeoNames MCP: countries, cities, POIs, distance & nearby. Remote HTTP + agent token signup.
Address to coordinates and back, postal codes, worldwide (OpenStreetMap). Free, no key.
Address validation & geocoding for AI agents: 240+ countries, UK PAF, free US/CA enrichment
Maps, geocoding and time zones for AI coding agents, on OpenStreetMap, with hard prepaid spend caps.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceAn experimental MCP server providing spatial context for LLMs by interfacing with French Geoplateforme services. It enables tasks such as geocoding, altitude lookups, and querying administrative, cadastral, or urban planning data.18 npm4MIT
- AlicenseNot gradedqualityBmaintenanceFree geospatial MCP server for AI agents, providing geocoding, reverse geocoding, POI search, and route planning using OpenStreetMap data via Nominatim, Overpass, and OSRM.GPL 3.0
- AlicenseNot gradedqualityBmaintenanceA hosted MCP server for French address data quality, geocoding, and routing, allowing LLMs to verify, normalize, search addresses, and compute routes using official open data sources.3MIT
- AlicenseAqualityCmaintenanceEnables LLMs to geocode and reverse geocode places, find nearby points of interest, search categories within an area, get turn-by-turn routing, suggest meeting points, and analyze neighborhoods, commutes, schools, EV charging, and parking through OpenStreetMap data services. It also serves place and map tile resources over stdio, SSE, or Streamable HTTP transports.12MIT