Nigeria Postcode

Official registry Other

Validate, look up and resolve addresses to Nigeria's NIPOST digital postcodes (NDAPS).

Install

Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).

PYPI package ng-postcode-mcp
Claude Code
claude mcp add nigeria-postcode -e 'NG_POSTCODE_API_KEY=<your ng_postcode_api_key>' -e 'NG_POSTCODE_MAX_LEVEL=1' -e 'NG_POSTCODE_MAX_PAID_CALLS=25' -e 'NG_POSTCODE_BASE_URL=https://api.postcode.gov.ng' -e 'NG_GEOCODER_URL=<NG_GEOCODER_URL>' -e 'NG_GEOCODER_CONTACT=<NG_GEOCODER_CONTACT>' -e 'NG_POSTCODE_TRANSPORT=stdio' -e 'NG_POSTCODE_HOST=127.0.0.1' -e 'NG_POSTCODE_PORT=8000' -e 'NG_POSTCODE_ALLOW_SHARED_KEY=<NG_POSTCODE_ALLOW_SHARED_KEY>' -e 'NG_POSTCODE_ALLOWED_HOSTS=<NG_POSTCODE_ALLOWED_HOSTS>' -- uvx ng-postcode-mcp==0.6.3
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "nigeria-postcode": {
            "command": "uvx",
            "args": [
                "ng-postcode-mcp==0.6.3"
            ],
            "env": {
                "NG_POSTCODE_API_KEY": "<your ng_postcode_api_key>",
                "NG_POSTCODE_MAX_LEVEL": "1",
                "NG_POSTCODE_MAX_PAID_CALLS": "25",
                "NG_POSTCODE_BASE_URL": "https://api.postcode.gov.ng",
                "NG_GEOCODER_URL": "<NG_GEOCODER_URL>",
                "NG_GEOCODER_CONTACT": "<NG_GEOCODER_CONTACT>",
                "NG_POSTCODE_TRANSPORT": "stdio",
                "NG_POSTCODE_HOST": "127.0.0.1",
                "NG_POSTCODE_PORT": "8000",
                "NG_POSTCODE_ALLOW_SHARED_KEY": "<NG_POSTCODE_ALLOW_SHARED_KEY>",
                "NG_POSTCODE_ALLOWED_HOSTS": "<NG_POSTCODE_ALLOWED_HOSTS>"
            }
        }
    }
}

Environment variables

  • NG_POSTCODE_API_KEY secret NIPOST Postcode API key from dashboard.postcode.gov.ng. Validation works without it.
  • NG_POSTCODE_MAX_LEVEL Highest lookup level tools may request. Levels 2 and up consume NIPOST credits.
  • NG_POSTCODE_MAX_PAID_CALLS How many paid lookups one run of the server may make with its own key.
  • NG_POSTCODE_BASE_URL Alternative API host, such as a staging stack.
  • NG_GEOCODER_URL Nominatim server used by resolve_address to place described addresses. Ideally your own instance.
  • NG_GEOCODER_CONTACT URL or email identifying you to the geocoder. Required for the public Nominatim.
  • NG_POSTCODE_TRANSPORT stdio (default), or http to serve streamable HTTP at /mcp on NG_POSTCODE_HOST and NG_POSTCODE_PORT.
  • NG_POSTCODE_HOST Address the HTTP transport listens on.
  • NG_POSTCODE_PORT Port the HTTP transport listens on.
  • NG_POSTCODE_ALLOW_SHARED_KEY Set to 1 to let the HTTP transport start with a server key on a public address.
  • NG_POSTCODE_ALLOWED_HOSTS Host names the HTTP transport answers to, separated by commas. Required with a server key on a public address.
NPM package ng-postcode-mcp
Claude Code
claude mcp add nigeria-postcode -e 'NG_POSTCODE_API_KEY=<your ng_postcode_api_key>' -e 'NG_POSTCODE_MAX_LEVEL=1' -e 'NG_POSTCODE_MAX_PAID_CALLS=25' -e 'NG_POSTCODE_BASE_URL=https://api.postcode.gov.ng' -- npx -y ng-postcode-mcp@0.4.2
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "nigeria-postcode": {
            "command": "npx",
            "args": [
                "-y",
                "ng-postcode-mcp@0.4.2"
            ],
            "env": {
                "NG_POSTCODE_API_KEY": "<your ng_postcode_api_key>",
                "NG_POSTCODE_MAX_LEVEL": "1",
                "NG_POSTCODE_MAX_PAID_CALLS": "25",
                "NG_POSTCODE_BASE_URL": "https://api.postcode.gov.ng"
            }
        }
    }
}

Environment variables

  • NG_POSTCODE_API_KEY secret NIPOST Postcode API key from dashboard.postcode.gov.ng. Validation works without it.
  • NG_POSTCODE_MAX_LEVEL Highest lookup level tools may request. Levels 2 and up consume NIPOST credits.
  • NG_POSTCODE_MAX_PAID_CALLS How many paid lookups one run of the server may make with its own key.
  • NG_POSTCODE_BASE_URL Alternative API host, such as a staging stack.

Documentation

From the project's README on GitHub, refreshed weekly. Links open on GitHub.

Contents
  1. ng-postcode
  2. Packages
  3. Quick start
  4. For coding agents
  5. The format
  6. Passing a postcode between systems
  7. Design
  8. Status
  9. Development
  10. License

ng-postcode

Developer tools for Nigeria's National Digital Alphanumeric Postcode System (NDAPS), the building-level postcode NIPOST launched in October 2026: libraries for Rust and Python (ng-postcode), JavaScript (ng-postcode-js on npm), and Java (ng-postcode-api), MCP servers for AI assistants on PyPI and npm, and a resolver that turns described addresses into postcodes.

A postcode has 11 characters in five segments, written EK-01-A03-FK-01: state, LGA, district, area and building unit. NIPOST's own example, FC 02 A09 DB 09, is the same shape with spaces.

Packages

PackageWhat it doesInstallSource
ng-postcode (Rust)Parse, validate and format codes offline; client for the postcode.gov.ng APIcargo add ng-postcodesrc/, docs
ng-postcode (Python)The same behaviour, with sync and async clientspip install ng-postcodepython/
ng-postcode-jsThe same behaviour for JavaScript and TypeScript, with a fetch clientnpm install ng-postcode-jsjs/packages/ng-postcode-js
ng-postcode and ng-postcode-api (Java)The same behaviour for Java 17 and later; the core has no dependenciesio.github.adeniyikayodee:ng-postcode-apijava/
ng-postcode-mcpMCP server: validate, look up, autocomplete, find by location, resolve addressesuvx ng-postcode-mcpmcp/
ng-postcode-mcp (npm)The same server for Node, without address resolutionnpx ng-postcode-mcpjs/packages/ng-postcode-mcp
ng-postcode-polarsPolars expressions that parse, format and group codes by place, compiled from the Rust corepip install ng-postcode-polarsnative/polars
ng_postcode (PostgreSQL)The postcode as native 8-byte types with index support, compiled from the Rust corePackage from Releases, then CREATE EXTENSION ng_postcodenative/pg
ng_postcode (DuckDB)The same functions as a DuckDB extension, compiled from the Rust coreINSTALL ng_postcode FROM community (DuckDB 1.5.6)ng-postcode-duckdb
ng-address-resolverResolve free-text addresses to postcodes, only as precisely as the evidence allows (pre-alpha)pip install ng-address-resolveragent/

Each package has its own README with full usage.

For databases, sql/ holds the postcode as a PostgreSQL domain and as DuckDB macros, in plain SQL that runs the same shared cases.

Two names are easy to get wrong. On npm the library is ng-postcode-js, because ng-postcode there is another project. ng-address-resolver is imported as ng_address.

For online stores, ng-postcode-woocommerce is a WooCommerce plugin built on the same rules and the same shared test cases: it checks and tidies the postcode at checkout, finds it from the customer's location, and makes shipping zones match on postcode prefixes.

For React and Next.js, templates/nigerian-address-form is a checkout address form built with shadcn/ui and ng-postcode-js. Add it with npx shadcn@latest add https://adeniyikayodee.github.io/ng-postcode/r/nigerian-address-form.json, or deploy the folder as an app.

Quick start

Parsing and validation are offline and need no key.

Python

from ng_postcode import Postcode, parse

match parse("ek 01 a03 fk 01"):
    case Postcode() as code:
        print(code, code.compact)  # EK-01-A03-FK-01 EK01A03FK01
    case error:
        print(error)               # e.g. "invalid lga segment"

JavaScript and TypeScript

import { Postcode, parse } from "ng-postcode-js";

const code = parse("ek 01 a03 fk 01");
if (code instanceof Postcode) {
  console.log(String(code), code.compact); // EK-01-A03-FK-01 EK01A03FK01
} else {
  console.log(String(code));               // e.g. "invalid lga segment"
}

Java

import io.github.adeniyikayodee.ngpostcode.Postcode;

if (Postcode.parse("ek 01 a03 fk 01") instanceof Postcode code) {
    System.out.println(code + " " + code.compact()); // EK-01-A03-FK-01 EK01A03FK01
}

Rust

use ng_postcode::{Postcode, Segment};

let code: Postcode = "ek 01 a03 fk 01".parse()?;
assert_eq!(code.to_string(), "EK-01-A03-FK-01");
assert_eq!(code.prefix(Segment::Area), "EK-01-A03-FK");

AI assistants

The MCP server works with any MCP client. It runs over stdio as uvx ng-postcode-mcp, with the API key in the environment. Most clients take this entry in their MCP settings:

{
  "mcpServers": {
    "ng-postcode": {
      "command": "uvx",
      "args": ["ng-postcode-mcp"],
      "env": { "NG_POSTCODE_API_KEY": "nipost_live_..." }
    }
  }
}

To run it with Node, use "command": "npx" and "args": ["-y", "ng-postcode-mcp"]; that edition has every tool except address resolution. Per-client steps for Cursor, VS Code, Codex, Claude and others are in the MCP server README. Validation works without a key; the other tools need one from the NIPOST developer dashboard. The server is listed in the MCP Registry as io.github.Adeniyikayodee/ng-postcode.

PostgreSQL

Load the plain SQL file with psql -f sql/postgres.sql. It needs no extension, so it runs on managed services.

CREATE TABLE customers (
    id       bigint PRIMARY KEY,
    postcode ng_postcode            -- refuses anything that is not a compact code
);

INSERT INTO customers VALUES (1, ng_postcode_parse('ek 01 a03 fk 01'));

SELECT ng_postcode_format(ng_postcode_truncate(postcode, 'district')) AS district, count(*)
FROM customers
GROUP BY 1;
-- EK-01-A03 | 1

On a server where you can install an extension, the native extension has the same functions and stores a code in 8 bytes. Unpack the package for your PostgreSQL version from Releases, then run CREATE EXTENSION ng_postcode.

DuckDB

Load the same functions with duckdb my.db < sql/duckdb.sql.

SELECT ng_postcode_parse('ek 01 a03 fk 01');
-- EK01A03FK01

The compiled extension, ng-postcode-duckdb, has the same functions: on DuckDB 1.5.6, run INSTALL ng_postcode FROM community, then LOAD ng_postcode.

Polars

import polars as pl
import ng_postcode_polars  # pip install ng-postcode-polars

frame = pl.DataFrame({"postcode": ["ek 01 a03 fk 01", "EK-01-A03-ZZ-14", "100001"]})

print(frame.with_columns(code=pl.col("postcode").ng.parse(), ok=pl.col("postcode").ng.is_valid()))
# ek 01 a03 fk 01  EK01A03FK01  true
# EK-01-A03-ZZ-14  EK01A03ZZ14  true
# 100001           null         false

A CSV file

pip install ng-postcode
ng-postcode --column postcode customers.csv --out checked.csv

The output keeps every row and adds the compact code, a status of valid, suggested, invalid, or empty, a suggested correction, and the reason for each invalid row.

For coding agents

Language models trained before October 2026 expect a six-digit Nigerian postcode. To point a coding agent at the current format, paste this into your project's AGENTS.md, CLAUDE.md, or editor rules:

Nigerian postcodes: since 1 October 2026 a building's postcode is NIPOST's 11-character
digital postcode (NDAPS), such as EK-01-A03-FK-01, and a six-digit pattern does not match it.
Validate and format it with ng-postcode (pip, cargo), ng-postcode-js (npm), or
io.github.adeniyikayodee:ng-postcode (Maven). On npm, ng-postcode is a different project.
Reference: https://adeniyikayodee.github.io/ng-postcode/llms.txt

The format

SegmentExampleShape
StateEK2 letters
LGA012 digits, 01 to 99
DistrictA033 letters or digits
AreaFK2 letters
Building unit012 digits, 01 to 99
FormExampleUse it to
CanonicalEK-01-A03-FK-01Write a code and pass it between systems
SpacedEK 01 A03 FK 01Show a code to people
CompactEK01A03FK01Store and compare codes

Input may be hyphenated, spaced or compact, in either case. The compact form matches ^[A-Z]{2}(0[1-9]|[1-9][0-9])[A-Z0-9]{3}[A-Z]{2}(0[1-9]|[1-9][0-9])$. A well-formed code is not necessarily assigned to a building; only the NIPOST API can confirm that.

Passing a postcode between systems

docs/schemas/postcode-reference.schema.json defines a small JSON object for handing a location from one system or AI agent to another: the code, its level (state to building), and optionally a confidence and whether NIPOST confirmed it. A resolved or partial resolve_address answer already fits it.

For a whole address, docs/address-record.md wraps the reference in an address record and maps it onto ISO 20022, FHIR, schema.org, vCard, and UPU's address layout for Nigeria.

Design

  • One behaviour, four languages: Rust, Python, JavaScript and Java all run the cases in spec/, so they cannot drift apart. The Node MCP server registers its tools from spec/mcp.json, which the Python server generates.
  • Offline first: parsing and validation never touch the network. API access is a separate, optional layer.
  • Errors are values: expected failures, such as a malformed code or a rejected API key, come back as return values.
  • Careful with money and guesses: the MCP server caps lookups at the free level unless told otherwise, and never corrects a mistyped code into a paid call. The resolver gives an area or district code when that is all the evidence supports.

Status

  • The NIPOST API needs a key for every endpoint, from the developer dashboard. Offline validation needs nothing.
  • The API layer is tested against responses captured from the live API with a level 1 key, kept in spec/responses.json. Run scripts/live_check.py with your own key to repeat the comparison. Lookup levels 2 and up need a higher-access key and are tested only against NIPOST's documented examples.
  • The resolver and the resolve_address tool are pre-release. They work against the live API, but their accuracy on real addresses is unmeasured. Described addresses need a geocoder you run or pay for; text alone rarely identifies a building, so ask users for a location pin when the exact building matters.
  • Notes for integrators on how the live API behaves, each with the date it was observed, are in docs/api-differences.md.

Development

See CONTRIBUTING.md for the shared spec, how to add a language, and the commit standards.

cargo test --all-features                              # Rust
cd python && uv run --group dev pytest                 # Python library
cd mcp && uv run --group dev pytest                    # MCP server
cd agent && uv run --group dev pytest                  # resolver
cd js && npm ci && npm test                            # JavaScript library and Node MCP server
cd java && ./mvnw verify                               # Java libraries

CI runs formatting, linting, type checks and tests for every package. Releases publish from tags (v* to crates.io; py-v*, mcp-v* and agent-v* to PyPI and the MCP Registry; js-v* and js-mcp-v* to npm) through trusted publishing, so no tokens are stored. Maven Central has no trusted publishing, so java-v* uses a token and a signing key held as environment secrets, and the upload is published by hand. An mcp-v* release also creates a GitHub release, which Glama rebuilds its listing from, and republishes the Smithery bundle with a key held the same way.

License

MIT

Frequently asked questions

What is Nigeria Postcode?

Validate, look up and resolve addresses to Nigeria's NIPOST digital postcodes (NDAPS).

How do I install Nigeria Postcode?

It runs on your machine. In Claude Code: claude mcp add nigeria-postcode -e 'NG_POSTCODE_API_KEY=<your ng_postcode_api_key>' -e 'NG_POSTCODE_MAX_LEVEL=1' -e 'NG_POSTCODE_MAX_PAID_CALLS=25' -e 'NG_POSTCODE_BASE_URL=https://api.postcode.gov.ng' -e 'NG_GEOCODER_URL=<NG_GEOCODER_URL>' -e 'NG_GEOCODER_CONTACT=<NG_GEOCODER_CONTACT>' -e 'NG_POSTCODE_TRANSPORT=stdio' -e 'NG_POSTCODE_HOST=127.0.0.1' -e 'NG_POSTCODE_PORT=8000' -e 'NG_POSTCODE_ALLOW_SHARED_KEY=<NG_POSTCODE_ALLOW_SHARED_KEY>' -e 'NG_POSTCODE_ALLOWED_HOSTS=<NG_POSTCODE_ALLOWED_HOSTS>' -- uvx ng-postcode-mcp==0.6.3. For other clients, paste the JSON configuration from this page into their MCP settings.

Does Nigeria Postcode need an API key?

Yes. It expects NG_POSTCODE_API_KEY, which you set in its configuration.

Is Nigeria Postcode open source?

Yes. Its code is on GitHub under the MIT license.

Who maintains Nigeria Postcode?

It is published by Adeniyikayodee. AI.tn lists it but does not maintain or endorse it.

From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.