Local Model Context Protocol (MCP) server that lets Claude Desktop call the AfterShip API via two tools:
track-shipment– track a package by tracking number (with optional courier slug; falls back to detection)list-couriers– list available couriers
- Node.js 18+ (tested on Node 22)
 - Claude Desktop (MCP enabled)
 - AfterShip API key
 
npm iThe project uses ESM (
"type": "module") and TypeScript (tsxrunner).
Open Claude Desktop → Settings → Developer → Edit Config, and add an MCP server entry:
{
  "mcpServers": {
    "aftership-tracking": {
      "command": "npx",
      "args": ["-y", "tsx", "/absolute/path/to/repo/src/index.ts"],
      "env": {
        "AFTERSHIP_API_KEY": "YOUR_AFTERSHIP_API_KEY"
      }
    }
  }
}🔐 Do not commit secrets. If this repo is shared, prefer:
"AFTERSHIP_API_KEY": "$AFTERSHIP_API_KEY"and set the variable in your shell / system keychain.
Now quit & reopen Claude Desktop (Cmd–Q on macOS) so it picks up the config.
In any chat (below or use natural language):
- 
List couriers
Run the
list-courierstool (aftership-tracking). - 
Track a shipment (known courier)
Run "track-shipment" (aftership-tracking) with: { "trackingNumber": "1Z999AA10123456784", "slug": "ups" } - 
Track a shipment (detect courier automatically)
Run "track-shipment" (aftership-tracking) with: { "trackingNumber": "1Z999AA10123456784" } 
Claude will invoke the MCP tool and show results.
src/
  index.ts                     # MCP stdio server (tools/list & tools/call)
  tools/
    list-couriers.ts           # Tool wrapper (calls utils.listCouriers)
    track-shipment.ts          # Tool: detect → fetch → create → return
  utils/
    aftership-api.ts           # axios client + helpers (detect/get/create/list)
package.json
tsconfig.json
- 
MCP transport: stdio (stdin/stdout). The server uses
@modelcontextprotocol/sdk1.18.x.- Tools are advertised via 
ListToolsRequestSchema, invoked viaCallToolRequestSchema(imported from@modelcontextprotocol/sdk/types.js). - Server capabilities include 
{ tools: { list: true, call: true } }. 
 - Tools are advertised via 
 - 
Do not log to stdout.
stdoutmust carry only JSON-RPC.- Use 
console.error(...)(stderr) for diagnostics; otherwise Claude will show “not valid JSON” errors. 
 - 
Schemas (JSON Schema):
list-couriers:{ "type": "object", "properties": {}, "additionalProperties": false }track-shipment:{ "type": "object", "properties": { "trackingNumber": { "type": "string" }, "slug": { "type": "string", "nullable": true } }, "required": ["trackingNumber"], "additionalProperties": false }
 - 
Tracking logic (tool
track-shipment):- If 
slugis not provided → call/couriers/detect. - Try 
GET /trackings/{slug}/{tracking_number}for top candidates. - If none exists, 
POST /trackingsto create one. - Return a clear status (
ok,created,no_courier_detected,error) and surface helpful messages from AfterShip. 
 - If 
 - 
TypeScript / ESM:
- Use explicit 
.tsextensions in relative imports (ESM rule). tsconfig.jsonsets"module": "ESNext","moduleResolution": "NodeNext".
 - Use explicit 
 
You can run the server without Claude (it will wait on stdio):
export AFTERSHIP_API_KEY=YOUR_AFTERSHIP_API_KEY  # local only
npx tsx src/index.tsClaude Desktop logs (macOS): ~/Library/Logs/Claude/
mcp-server-aftership-tracking.log– your server’s stderr/stdout (stderr recommended)mcp.log– client/handshake logs
- 
No wrench / tools not visible
- Restart Claude Desktop after editing config.
 - Confirm 
tools/listreturns both tools withinputSchema. - Capabilities must include 
{ tools: { list: true, call: true } }. 
 - 
“not valid JSON” banner
- You printed to stdout. Switch to 
console.error(stderr). 
 - You printed to stdout. Switch to 
 - 
400s from AfterShip
- Often caused by missing/invalid slug or an unrecognized number.
 - Use detection; errors are bubbled back so Claude shows the meaningful message.
 
 - 
Missing API key
- Ensure 
AFTERSHIP_API_KEYis present in the MCP server env (Claude config) or exported in your shell for local dev. 
 - Ensure 
 
- Keep your API key out of source control.
 - Use 
.gitignoreto excludenode_modules/, logs,.env, and your localclaude_desktop_config.json. - Provide a 
claude_desktop_config.example.jsonwithout secrets. 
MIT (or update to your preference).
