Skip to main content
Glama
README.md
# 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

A4/5.0

Scored across 4 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues