envia-mcp
v0.2.0
Published
MCP server and client library for Envia.com shipping API — quote, label, track, and cancel shipments across 170+ carriers in 18 countries
Maintainers
Readme
What's Inside
Two exports, one package:
| Export | Import | Use Case |
|--------|--------|----------|
| MCP Server | envia-mcp | AI agents (Claude Code, Claude Desktop, Cursor) interact with Envia via natural language |
| Client Library | envia-mcp/client | Node.js/TypeScript apps call the Envia API directly with full type safety |
| Type Definitions | envia-mcp/types | Zod schemas and TypeScript types for all API entities |
Supported Countries
Americas
| Country | Code | Carriers | Notable Carriers | |---------|:----:|:--------:|-----------------| | Mexico | MX | 34 | DHL, FedEx, Estafeta, Paquetexpress, UPS | | United States | US | 33 | FedEx, UPS, USPS, DHL, Sendle, LSO | | Colombia | CO | 16 | FedEx, DHL, Coordinadora, Servientrega, TCC | | Argentina | AR | 12 | Andreani, Correo Argentino, FedEx, DHL, OCA | | Brazil | BR | 11 | Correios, FedEx, DHL, Jadlog, Loggi | | Chile | CL | 10 | Chilexpress, Correos Chile, FedEx, DHL, Starken | | Guatemala | GT | 7 | Cargo Expreso, DHL, Telomando | | Canada | CA | 4 | Canada Post, Canpar, DHL, Purolator | | Uruguay | UY | 3 | DHL, Treggo | | Peru | PE | 1 | Olva |
Europe, Asia & Oceania
| Country | Code | Carriers | Notable Carriers | |---------|:----:|:--------:|-----------------| | Spain | ES | 16 | Correos, DHL, FedEx, GLS, SEUR, UPS | | India | IN | 9 | BlueDart, Delhivery, FedEx, Aramex, Xpressbees | | France | FR | 4 | Chronopost, Mondial Relay, UPS | | Italy | IT | 4 | Poste Italiane, BRT, InPost, UPS | | Australia | AU | 3 | Aramex, FedEx, Sendle | | Hong Kong | HK | 1 | FedEx | | Japan | JP | 1 | — | | China | CN | 1 | — |
Envia adds carriers and countries over time. Use
envia_get_carriersto get the current list.
Quick Start
As an MCP Server
The server connects to Envia's sandbox by default — no real charges, safe to experiment.
Add to your Claude Code config (.mcp.json):
{
"mcpServers": {
"envia": {
"command": "node",
"args": ["/path/to/envia-mcp/dist/index.js"],
"env": {
"ENVIA_API_KEY": "your-sandbox-api-key"
}
}
}
}Or, if installed globally via npm:
{
"mcpServers": {
"envia": {
"command": "npx",
"args": ["envia-mcp"],
"env": {
"ENVIA_API_KEY": "your-sandbox-api-key"
}
}
}
}As a Client Library
import { EnviaClient } from 'envia-mcp/client';
// Sandbox (default — safe for testing)
const client = new EnviaClient({
apiKey: process.env.ENVIA_API_KEY!,
shippingUrl: 'https://api-test.envia.com',
queriesUrl: 'https://queries-test.envia.com',
geocodesUrl: 'https://geocodes.envia.com',
});
// Get rates from all carriers, sorted by price
const quotes = await client.getQuotesAllCarriers(origin, destination, packages);
// Purchase a label (charges your prepaid balance in USD)
const label = await client.createLabel(origin, destination, packages, 'fedex', 'ground');
// Track a shipment
const tracking = await client.trackShipments(['TRACK123']);MCP Tools
The server exposes 11 tools that AI agents can call:
| Tool | Description | Destructive |
|------|-------------|:-----------:|
| envia_quote | Get shipping rates from all carriers for a route | |
| envia_create_label | Purchase a shipping label (charges USD balance) | Yes |
| envia_track | Track one or more shipments by tracking number | |
| envia_cancel | Cancel a shipment and request refund | Yes |
| envia_validate_zipcode | Validate a postal code and get address info | |
| envia_get_carriers | List available carriers for a country | |
| envia_get_services | List services for a specific carrier | |
| envia_shipment_history | Get shipment history for a given month/year | |
| envia_schedule_pickup | Schedule a carrier pickup | Yes |
| envia_classify_hscode | Classify product description into HS code for customs | |
| envia_lookup_city | Look up cities by name, get postal codes (no auth) | |
All tools return both Markdown (for display) and structured data (for programmatic use).
MCP Resources
7 documentation resources provide AI agents with context about the Envia API:
| URI | Content |
|-----|---------|
| envia://docs/overview | API hosts, auth model, sandbox vs production |
| envia://docs/address-format-mx | Mexican address fields, state codes, colonia mapping |
| envia://docs/carriers | 34 carriers, service counts, weight limits |
| envia://docs/rate-response | Price breakdown, MXN currency, additional charges |
| envia://docs/label-response | USD currency, permanent label URLs, no idempotency |
| envia://docs/errors | Error codes and how to handle them |
| envia://docs/international | International shipping guide: HS codes, commercial invoices, currency, duties |
MCP Prompts
4 workflow prompts guide agents through multi-step tasks:
| Prompt | What It Does |
|--------|-------------|
| diagnose-shipment | Investigate tracking status, identify stuck/failed shipments |
| compare-rates | Quote all carriers for a route, compare price vs speed |
| verify-address | Validate a postal code, return neighborhoods and coordinates |
| prepare-international-shipment | Step-by-step international shipment workflow |
Client API
The EnviaClient class provides typed methods for every Envia operation:
import { EnviaClient } from 'envia-mcp/client';
const client = new EnviaClient({ apiKey, shippingUrl, queriesUrl, geocodesUrl });| Method | Returns | Description |
|--------|---------|-------------|
| getQuotes(origin, dest, packages, carrier) | RateQuoteItem[] | Rates from one carrier |
| getQuotesAllCarriers(origin, dest, packages) | RateQuoteItem[] | Fan-out to all carriers, sorted by price |
| createLabel(origin, dest, packages, carrier, service) | LabelItem | Purchase a shipping label |
| trackShipments(trackingNumbers) | TrackingItem[] | Track one or more shipments |
| cancelShipment(carrier, trackingNumber) | CancellationItem | Cancel and request refund |
| validateZipCode(postalCode, countryCode?) | PostalCodeItem[] | Validate postal code (no auth needed) |
| getCarriers(countryCode?) | Carrier[] | List available carriers |
| getServices(carrier, countryCode?) | CarrierService[] | List services for a carrier |
| getShipmentHistory(month, year) | ShipmentHistoryItem[] | Get shipment history for a month |
| schedulePickup(request) | PickupResult | Schedule a carrier pickup |
| classifyHsCode(description, options?) | HsCodeClassification | Classify product into HS code |
| generateCommercialInvoice(request) | CommercialInvoiceResult | Generate commercial invoice for customs |
| lookupCity(city, countryCode?) | CityLookupItem[] | Look up cities by name, get postal codes |
| getAvailableCarriers(countryCode?, international?) | AvailableCarrier[] | List carriers with availability details |
Envia API Gotchas
Things that will bite you if you're not careful:
| Gotcha | Details |
|--------|---------|
| phone_code differs | Country code string for quotes (e.g. "MX"), dialing code for labels (e.g. "52") |
| Quotes are MXN, labels are USD | Prepaid balance is denominated in USD |
| Labels are NOT idempotent | Duplicate call = double charge, different tracking number |
| Sandbox geocodes is DOWN | Always use production geocodes.envia.com |
| Carrier is required per quote | The client fans out automatically, but the raw API needs one carrier per call |
| Track by tracking number | shipmentId does NOT work as a tracking key |
Configuration
The MCP server reads configuration from environment variables:
| Variable | Required | Default | Description |
|----------|:--------:|---------|-------------|
| ENVIA_API_KEY | Yes | — | Your Envia.com API key |
| ENVIA_SHIPPING_URL | | https://api-test.envia.com | Shipping API base URL |
| ENVIA_QUERIES_URL | | https://queries-test.envia.com | Queries API base URL |
| ENVIA_GEOCODES_URL | | https://geocodes.envia.com | Geocodes API base URL |
Sandbox vs Production
The server defaults to Envia's sandbox environment. This is intentional.
The envia_create_label tool purchases real shipping labels — it costs money and is not idempotent (calling it twice creates two labels with different tracking numbers, charged twice). AI agents can trigger this tool multiple times during a conversation, and there is no undo. Defaulting to sandbox prevents accidental charges during development, testing, and experimentation.
This follows industry best practice: Supabase MCP recommends "never connect to production"; Stripe separates test/live keys; Slack MCP disables destructive operations by default.
Switching to production
To use the live Envia API, set the URL environment variables to production hosts:
{
"mcpServers": {
"envia": {
"command": "node",
"args": ["/path/to/envia-mcp/dist/index.js"],
"env": {
"ENVIA_API_KEY": "your-production-api-key",
"ENVIA_SHIPPING_URL": "https://api.envia.com",
"ENVIA_QUERIES_URL": "https://queries.envia.com"
}
}
}
}Important notes:
- Sandbox and production use separate API keys — a sandbox key won't work on production and vice versa
- Geocodes (
geocodes.envia.com) always uses production — the sandbox geocodes endpoint is down (503) - The server logs
(sandbox)or(PRODUCTION)on startup so you always know which environment you're hitting
Currency
The Envia API defaults to USD when the currency field is omitted from requests. This is a common source of confusion for Latin American users who expect MXN (or their local currency). The official @envia/envia-mcp server inherits this behavior, silently quoting and charging in USD.
Our implementation defaults to MXN. You can override this globally or per call:
// Default: MXN
const client = new EnviaClient({ apiKey, shippingUrl, queriesUrl, geocodesUrl });
// Override globally (e.g., for Colombia)
const client = new EnviaClient({
apiKey,
shippingUrl,
queriesUrl,
geocodesUrl,
defaultCurrency: 'COP',
});
// Override per call via the currency parameter
const quotes = await client.getQuotesAllCarriers(origin, destination, packages, {
currency: 'USD',
});The MCP server also defaults to MXN. Set ENVIA_DEFAULT_CURRENCY to change it.
HTTP Features
The client includes production-grade HTTP handling:
- SSRF protection — hostname allowlist restricts requests to known Envia API domains only
- Retry with exponential backoff — failed requests retry up to 3 times with increasing delays
Retry-Afterheader support — respects server-requested cooldown periods before retrying
Comparison with @envia/envia-mcp
Envia maintains an official MCP server at @envia/envia-mcp. Here is a factual comparison:
| Feature | envia-mcp (this project) | @envia/envia-mcp (official) | |---------|--------------------------|----------------------------| | Typed TypeScript client library | Yes | No | | Configurable currency default | Yes (MXN default) | No (USD default) | | Zod response validation | Yes | No | | Structured MCP output (data + Markdown) | Yes | No | | Sandbox geocodes fallback | Yes | No | | SSRF protection (hostname allowlist) | Yes | No | | Retry with exponential backoff | Yes | No | | Official Envia branding | No | Yes | | Sandbox by default | Yes | Yes | | 10+ MCP tools | Yes (11) | Yes |
Known issues in the official server:
- Missing
settingsobject on label creation causes HTTP 400 errors - Defaults to USD instead of MXN, which is unexpected for the primary market (Mexico)
Development
# Clone and install
git clone https://github.com/amak07/envia-mcp.git
cd envia-mcp
npm.cmd install
# Build
npm.cmd run build
# Type check
npm.cmd run typecheck
# Run tests (30 unit tests, mocked API)
npm.cmd run test:run
# Dev mode (watch + restart)
npm.cmd run devProject Structure
src/
index.ts # MCP server entry point (shebang, stdio transport)
client.ts # EnviaClient class (standalone, no MCP dependency)
types.ts # Zod schemas + TypeScript types for all API entities
utils.ts # HTTP helpers, error handling, formatting
constants.ts # API URLs, character limits, response format
tools/
quote.ts # envia_quote — fan-out rate quoting
create-label.ts # envia_create_label — label purchase
track.ts # envia_track — shipment tracking
cancel.ts # envia_cancel — shipment cancellation
validate-zipcode.ts # envia_validate_zipcode — postal code lookup
get-carriers.ts # envia_get_carriers — carrier directory
get-services.ts # envia_get_services — service catalog
index.ts # Barrel — registerAllTools()
resources/
index.ts # 6 inline documentation resources
prompts/
index.ts # 3 workflow prompts
client.test.ts # 30 unit tests (mocked fetch)
tests/
fixtures/ # Real API response snapshots (JSON)Tech Stack
- TypeScript with strict mode
- MCP SDK
@modelcontextprotocol/sdk1.27+ - Zod for runtime schema validation
- Native
fetch(Node.js 18+ built-in, no axios) - Vitest for unit testing
- ESM (
"type": "module")
