Rate shopping and label generation via Shippo/EasyPost test mode — agents ship things, label returned in-response
Server exposes 4 tools via HTTP with mostly complete schemas and descriptions. POST /rates and POST /label have well-structured input schemas with nested objects and proper field descriptions. However, GET /health and GET /.well-known/x402 lack input schemas entirely (GET endpoints with no parameters), and output schemas are not documented for any tool. Descriptions are present but brief (10-50 chars for some). Parameter descriptions are generally good but inconsistent across tools. No error handling guidance, no tool annotations (readOnlyHint/destructiveHint), and no idempotency hints despite POST /label being a destructive write operation.
x402 manifest endpoint for payment protocol discovery
Health check endpoint returning service status, active provider, test mode status, and payment rails
Purchase a shipping label — PDF returned base64 in the response body
Carrier rate table for a shipment: every service, price and transit time
GET /health and GET /.well-known/x402 have no input schemas. GET endpoints should declare empty object schemas or explicitly document that they accept no parameters.
No output schemas documented for any tool. LLMs cannot plan downstream calls or extract fields without knowing response structure. POST /rates and POST /label should document their response objects.
POST /label is a destructive write (purchases a label, charges money) but lacks destructiveHint annotation and no confirmation/dry-run pattern. Agents cannot distinguish safe reads from irreversible operations.
No error handling guidance. Errors return JSON with 'error' and 'message' fields, but LLMs receive no recovery hints (e.g., 'Invalid address format. Ensure street1, city, state, zip, country are provided.').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 47 | 2025-06-18+ | v2 |
POST /label description is vague ('Purchase a shipping label, PDF returned base64 in the response body'). Does not explain when to call it (after POST /rates), what rateId is, or that it charges money.