Skip to main content
Glama
gridpin-mcp

gridpin-mcp

Official

GridPin

PyPI crates.io DuckDB community extension MCP server License: Apache-2.0 DOI CI Open in Colab Live demo

A country in a file — offline forward and reverse geocoding.

GridPin demo: typo-tolerant forward and reverse geocoding in the terminal

Quick start

$ pip install gridpin gridpin-mcp
$ gridpin-fetch fr    # France sheet, 365 MB, checked against the signed release; prints its path
import 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 8080

Python

$ pip install gridpin
import 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 poi_layer

weak

equal or less confident

address (ties keep the address)

empty

any hit

POI, flagged poi_layer

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.bin

Australia. 11.92M addresses in all states and territories, including the external territories.

130 MB

brazil.bin

Brazil. 67.19M addresses in 26 states and the Federal District.

677 MB

canada.bin

Canada. 16.94M addresses in all provinces and territories.

183 MB

france.bin

France. 26.1M addresses from the national registry (BAN).

365 MB

germany.bin

Germany. 19.27M addresses in 15 of 16 Länder. Bavaria is not covered.

213 MB

italy.bin

Italy. 25.9M addresses.

247 MB

mexico.bin

Mexico. 14.14M addresses in all 32 states, from an address layer surveyed in 2009–2010.

156 MB

netherlands.bin

Netherlands. 9.9M addresses.

103 MB

serbia.bin

Serbia. 2.6M addresses.

32 MB

spain.bin

Spain. 15.27M addresses in all 52 postal provinces, including Ceuta and Melilla.

185 MB

usa.bin

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

fr_poi.bin

France, places. Optional add-on to france.bin: places by name, such as "pharmacie gare de Lyon". Not needed to geocode addresses.

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+YXA

If 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 tools
batchA
Read-only

Geocode up to 1000 addresses of one country in a single call, in input order.

NameRequiredDescriptionDefault
kNo
countryYes
queriesYes

Output Schema

NameRequiredDescription
countryYes
resultsYes

TDQS

A3.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_sheetA
Idempotent

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".
    
NameRequiredDescriptionDefault
countryYes

Output Schema

NameRequiredDescription
pathYes
countryYes
size_mbYes
already_downloadedYes

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

geocodeA
Read-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.
    
NameRequiredDescriptionDefault
kNo
queryYes
countryYes

Output Schema

NameRequiredDescription
queryYes
countryYes
resultsYes

TDQS

A3.6/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_countriesA
Read-only

List covered countries, their sheet sizes and whether each sheet is downloaded.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
homeYes
releaseYes
countriesYes

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

reverseC
Read-only

Reverse geocode: coordinates -> nearest indexed addresses, offline. Approximate: each result carries distance_m from the given point.

NameRequiredDescriptionDefault
kNo
latYes
lonYes
countryYes

Output Schema

NameRequiredDescription
latYes
lonYes
countryYes
resultsYes

TDQS

C2.9/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

  1. 3 tool updatesv0.6.1
    • Changedbatch1 field changed
      • addedOutput schema / $defs / Hit / properties / weak
        Added value: +{
        +  "title": "Weak",
        +  "type": "boolean"
        +}
    • Changedgeocode1 field changed
      • addedOutput schema / $defs / Hit / properties / weak
        Added value: +{
        +  "title": "Weak",
        +  "type": "boolean"
        +}
    • Changedreverse1 field changed
      • addedOutput schema / $defs / Hit / properties / weak
        Added value: +{
        +  "title": "Weak",
        +  "type": "boolean"
        +}
  2. 5 tool updatesv0.1.0
    • First observedbatch
    • First observeddownload_sheet
    • First observedgeocode
    • First observedlist_countries
    • First observedreverse

TDQS

A3.5/5.0

Scored across 5 tools

Disambiguation4/5

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.

Naming Consistency3/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityActive
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    An 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 npm
    4
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Free 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
  • A
    license
    A
    quality
    C
    maintenance
    Enables 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.
    12
    MIT