postnord-mcp
# postnord-mcp
An [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) server for [PostNord](https://www.postnord.com/)'s REST APIs. Lets AI assistants like Claude track shipments, find pickup points, and estimate transit times across the Nordics (Sweden, Norway, Denmark, Finland).
```
You: Where is my package 84971563697SE?
Claude: → track_shipment("84971563697SE")
Your package left the Oslo terminal this morning and is expected
at your pickup point tomorrow between 08:00 and 16:00.
```
## Tools
| Tool | Description |
|---|---|
| `track_shipment` | Track a shipment by shipment ID, item ID (parcel number), or customer reference. Returns full event history and estimated delivery. |
| `find_service_points` | Find the nearest pickup/drop-off points by address or postal code, with opening hours. |
| `find_service_points_by_coordinates` | Same, but by latitude/longitude. |
| `get_transit_time` | Estimate delivery time between two postal codes for a given PostNord service code. |
## Prerequisites
- **Node.js 18+**
- **A PostNord API key** — free. All PostNord API plans are free of charge; the key is emailed to you after you register for a plan. Which registration to use depends on whether you have a company/organization number (see below).
### Getting an API key
PostNord's developer portal is business-oriented. There is no consumer plan — every option authenticates with an API key sent as the `apikey` query parameter, but the registration paths differ:
| Path | Where | Needs org number? | Real shipment data? | Environment |
|---|---|---|---|---|
| **Sandbox** | [postnord-test.3scale.net/signup](https://postnord-test.3scale.net/signup) | No — "Company Name" is free text | No (test data only) | `atapi2.postnord.com` |
| **Customer / Partner Plan** | [Developer Portal → Plans & Pricing](https://developer.postnord.com/apis/plansAndPricing) | Yes | Yes | `api2.postnord.com` |
- **Sandbox** is the quickest way to verify the server works end to end. The signup form only asks for a name, email, username, and password; set `POSTNORD_SANDBOX=true` (see [Configuration](#configuration)) to point the server at the test host.
- **Customer Plan vs Partner Plan** — both are free. "Customer" is for using the APIs for your *own* business; "Partner" is for building a platform that serves *other* PostNord customers. Both require a company name and organization number at signup. If you run a sole proprietorship you already have an org number and can use the Customer Plan.
- **No company?** For real (non-sandbox) tracking without an organization number, email api.support@postnord.com and ask whether the free Customer Plan can be granted to an individual/hobby project.
The Booking API requires a separate business agreement and OAuth2 client credentials, and is not included in this server.
## Installation
```bash
git clone https://github.com/fredrsat/postnord-mcp.git
cd postnord-mcp
npm install
npm run build
```
## Usage
### Claude Desktop
Add to your `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"postnord": {
"command": "node",
"args": ["/path/to/postnord-mcp/dist/index.js"],
"env": {
"POSTNORD_API_KEY": "your-api-key"
}
}
}
}
```
### Claude Code
```bash
claude mcp add postnord --env POSTNORD_API_KEY=your-api-key -- node /path/to/postnord-mcp/dist/index.js
```
### Any other MCP client
The server speaks MCP over stdio. Start it with `POSTNORD_API_KEY` set:
```bash
POSTNORD_API_KEY=your-api-key node dist/index.js
```
## Configuration
All configuration is via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
| `POSTNORD_API_KEY` | ✅ | — | Your API key from the developer portal |
| `POSTNORD_SANDBOX` | | `false` | Set to `true` to use the sandbox environment (`atapi2.postnord.com`) |
| `POSTNORD_BASE_URL` | | `https://api2.postnord.com` | Override the API base URL entirely |
| `POSTNORD_LOCALE` | | `en` | Language for human-readable strings: `en`, `sv`, `no`, `da`, `fi` |
## Example prompts
Once connected, try asking your assistant:
- *"Track package 84971563697SE"*
- *"Where's the nearest PostNord pickup point to Karl Johans gate 1, Oslo?"*
- *"Find service points near postal code 111 20 in Sweden"*
- *"How long does MyPack Collect take from 0150 Oslo to 5003 Bergen?"*
## API notes
- PostNord authenticates with the API key as an `apikey` query parameter on every request.
- Tracking uses **Track & Trace v5** (`/rest/shipment/v5/trackandtrace/findByIdentifier.json`); service points use **Business Location v5**.
- The transit time endpoint varies by plan and agreement — if `get_transit_time` returns a 404, check the exact path in the swagger spec under your account on the [developer portal](https://developer.postnord.com/) and adjust `src/postnord.ts`.
- Rate limits depend on your plan (Sandbox / Free / Premium). API errors are returned to the model as tool errors with the response body included, so the assistant can explain what went wrong.
## Development
```bash
npm run dev # tsc --watch
npm run build # compile to dist/
```
The code is small on purpose: `src/postnord.ts` is a thin API client, `src/index.ts` registers the MCP tools. Adding a new PostNord endpoint is a method on the client plus a `registerTool` call.
You can test the server manually with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
```bash
POSTNORD_API_KEY=your-api-key npx @modelcontextprotocol/inspector node dist/index.js
```
## Roadmap
- [ ] Booking API support (requires a PostNord business agreement + OAuth2 client credentials)
- [ ] Publish to npm for `npx postnord-mcp` usage
- [ ] Response trimming — return curated fields instead of raw API JSON to save tokens
Contributions welcome — open an issue or PR.
## Disclaimer
This is an unofficial, community-built integration. It is not affiliated with or endorsed by PostNord AB. Use of the PostNord APIs is subject to their [terms of service](https://developer.postnord.com/).
## License
[MIT](LICENSE)
TDQS
Scored across 4 tools
track_shipment and get_transit_time are clearly distinct, but find_service_points and find_service_points_by_coordinates perform the same lookup with different input types. The suffix and explicit descriptions prevent serious confusion.
All tool names follow a lowercase verb_noun pattern with consistent snake_case. find_service_points_by_coordinates is longer but still fits the same convention.
Four tools is well within the ideal range and each covers a meaningful PostNord workflow: tracking, service point lookup, and transit time estimation. There is no obvious redundancy.
The set covers the core read-only PostNord use cases well, especially tracking and pickup locations. It lacks a way to enumerate valid service codes or create shipments, but those are minor gaps for typical tracking and collection queries.