Skip to main content

parcelapp-mcp

Python PyPI CI Docker License

An MCP server for Parcel, the iOS/macOS delivery tracking app. It wraps Parcel's external API (a premium feature of the app) so an LLM client can list your deliveries, add new ones to track, and look up carrier codes, over either stdio or streamable HTTP.

This project is not affiliated with or endorsed by Parcel.

🚀 Features

Feature Notes
📦 List deliveries Recent or active, with filtering and sorting, served from a 3-minute cache
➕ Add a delivery Validated locally first, so a typo never spends part of the daily quota
🔍 Search carriers Free, unrated lookup from code or name to the internal carrier code
📡 Resource template parcel://deliveries/{filter_mode} for passive reads alongside the tools
🔐 Two auth modes Environment variable over stdio, per-request header over HTTP
🚦 Rate-limit awareness Every result reports an honest, locally-tracked budget estimate
🐳 Container-ready Ships a Dockerfile for the HTTP transport, no key baked in

⚡ Quickstart

Run it directly with uv, no install step:

PARCEL_TOKEN=your-parcel-api-key uvx parcelapp-mcp

Get a key at https://web.parcelapp.net (requires a Parcel premium subscription). Pin a version with uvx parcelapp-mcp@0.2.0, or track the latest release with uvx parcelapp-mcp@latest.

To wire it into an MCP client such as Claude Desktop, add:

{
  "mcpServers": {
    "parcel": {
      "command": "uvx",
      "args": ["parcelapp-mcp"],
      "env": { "PARCEL_TOKEN": "your-parcel-api-key" }
    }
  }
}

📦 Installation

Local

# Persistent install, on your PATH
uv tool install parcelapp-mcp

# Or, inside a virtualenv (a bare `pip install` fails outside one on
# externally-managed pythons such as Homebrew's or Debian's)
python -m venv .venv
source .venv/bin/activate
pip install parcelapp-mcp

🐳 Docker

The image serves the streamable-http transport only; it holds no credential, since the key travels with each request instead.

docker pull ghcr.io/obeone/parcelapp-mcp
docker run -p 8000:8000 ghcr.io/obeone/parcelapp-mcp

Images are published on every v* tag, for linux/amd64 and linux/arm64, to both ghcr.io/obeone/parcelapp-mcp and docker.io/obeoneorg/parcelapp-mcp, with an SBOM and a provenance attestation attached.

Every image is signed with cosign, keylessly: there is no public key to distribute, the signature is bound to the workflow that produced it and recorded in Sigstore's public transparency log. Check one before you run it:

cosign verify ghcr.io/obeone/parcelapp-mcp:0.2.0 \
  --certificate-identity-regexp '^https://github.com/obeone/parcelapp-mcp/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

To build it yourself instead:

docker build -t parcelapp-mcp \
  --build-arg VERSION="$(git describe --tags --abbrev=0 | sed 's/^v//')" .
docker run -p 8000:8000 parcelapp-mcp

The VERSION build argument matters: .dockerignore excludes .git, and the package version is derived from the git tag, so without it the build still succeeds but reports version 0.0.0.

Call it with your key in a header (use 127.0.0.1, not localhost: on macOS localhost resolves to ::1 first, and -p 8000:8000 only publishes IPv4):

curl http://127.0.0.1:8000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "X-Parcel-Token: your-parcel-api-key" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

The HTTP transport does not authenticate callers: it partitions its cache and rate-limit counters by a fingerprint of the key so two callers never see each other's parcels, but anyone who can reach the port can relay a request using their own key. Put it behind a network boundary you control, or add your own auth in front of it.

⚙️ Configuration

Variable Default Meaning
PARCEL_TOKEN none API key, stdio transport. Falls back to PARCEL_API_KEY
PARCEL_API_KEY none Alternate name for the key above
PARCEL_TRANSPORT stdio stdio or streamable-http
PARCEL_HOST 127.0.0.1 Bind address for streamable-http
PARCEL_PORT 8000 Port for streamable-http
PARCEL_PATH /mcp URL path for streamable-http
PARCEL_LOG_LEVEL INFO Python logging level

Over HTTP, a request's own key always wins: send it as X-Parcel-Token or Authorization: Bearer <key>. The environment variables are only the stdio path and the HTTP fallback.

Each CLI flag mirrors one of these: --transport, --host, --port, --path.

🛠️ Development

Command Purpose
uv sync Install dependencies, including the dev group
uv run pytest Run the 80-test suite (mocked, no network)
uv run ruff check Lint
uv run ruff format Format
uv run mypy Strict type-check
envchain parcel uv run parcelapp-mcp Run over stdio with a local key
envchain parcel uv run scripts/smoke_test.py Live, read-only in-process check
envchain parcel uv run scripts/stdio_test.py Live end-to-end check over stdio
envchain parcel uv run scripts/http_test.py Live check against a running HTTP server

CI runs lint, type-check and the test matrix (Python 3.10 to 3.13) on every push and pull request against main.

📡 API

Three MCP tools, all documented in detail through their own docstrings (the text a model actually reads at call time):

Tool Upstream limit Summary
list_deliveries 20 / hour Recent or active deliveries, with status, carrier name and events resolved
add_delivery 20 / day Adds a tracking number; rejects an unknown carrier or a missing required field before spending a request
search_carriers none Looks up a carrier code by name or code, including whether it needs a postcode or email

Plus one resource template, parcel://deliveries/{filter_mode} (active or recent), for reading the same listing without a tool call.

Every result carries a rate_limit block. Parcel publishes no rate-limit headers, so the figures are counted locally by this server and reported as remaining_at_most: the Parcel app itself, or another client sharing the same key, can spend from the same budget invisibly.

Upstream quirks

Three behaviours that look like bugs here and are not:

  • A listing is never fresh. It returns Parcel's own cached view of your deliveries. Nothing in this API asks a carrier for an update on demand.
  • A newly added delivery looks empty. It shows "No data available" until Parcel's next update cycle reaches it, and nothing can force that.
  • A tracking number must match a format a carrier recognises. For a placeholder with no real number behind it, pass carrier_code: "pholder".

🚀 Releases

Pushing a v* tag drives the whole pipeline: build and test, publish to PyPI via Trusted Publishing, create the GitHub release, and regenerate CHANGELOG.md with git-cliff from the Conventional Commit history, then build and push the multi-architecture image to both registries. Nothing here is versioned by hand.

🏗️ Architecture

flowchart TB
    Client[MCP client]

    subgraph Transports
        Stdio[stdio<br/>key from env]
        Http[streamable-http<br/>key per request]
    end

    subgraph parcelapp-mcp
        Server[server.py<br/>tools, resource, docstring contracts]
        Client_mod[client.py<br/>HTTP, cache, rate limits]
    end

    Parcel[Parcel API<br/>api.parcel.app]

    Client --> Stdio --> Server
    Client --> Http --> Server
    Server --> Client_mod --> Parcel

📝 License

MIT, see LICENSE.

Made by Grégoire Compagnon (obeone)

Metadata

Release files for parcelapp-mcp 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for parcelapp-mcp 0.2.1
File Size Uploaded
parcelapp_mcp-0.2.1.tar.gz 128.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for parcelapp-mcp 0.2.1
File Interpreter ABI Platform
parcelapp_mcp-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 146.2 kB

Release files / parcelapp_mcp-0.2.1.tar.gz

Download URL parcelapp_mcp-0.2.1.tar.gz
Size 128.8 kB
Tags Source
SHA-256 checksum
How to use checksums
f689f2dad83e35dfd9d41faa18a0dc2e0922814ba870c7c932bde4f5757b43be
BLAKE2b-256 checksum
How to use checksums
1bd2020476abf0196b08471aa1ab9ef7ea5a7de4082c286e91640df2ad8f8db4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / parcelapp_mcp-0.2.1-py3-none-any.whl

Download URL parcelapp_mcp-0.2.1-py3-none-any.whl
Size 17.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b4d6fd9d52f9d35cfc255c631ecd94d8b1746d65e67cd1ee0140a2778569b142
BLAKE2b-256 checksum
How to use checksums
4fe6797e4a87e6a720c832a4da7f3ec8ac51729e7fddb9341253406d3edbc537
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page