ShipSmart-MCP
Provides shipping tools for DHL integration, including address validation and rate preview capabilities through the DHL shipping provider.
Provides shipping tools for FedEx integration, including address validation and rate preview capabilities through the FedEx shipping provider.
Provides shipping tools for UPS integration, including address validation and rate preview capabilities through the UPS shipping provider.
Provides shipping tools for USPS integration, including address validation and rate preview capabilities through the USPS shipping provider.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ShipSmart-MCPvalidate address 123 Main St San Francisco CA 94105"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ShipSmart — MCP Tool Server (mcp)
Standalone MCP (Model Context Protocol) server exposing ShipSmart's
shipping tools (validate_address, get_quote_preview, …) over a small
HTTP contract.
It is the single source of truth for tool behavior across the platform.
Both ShipSmart-API (Python /
FastAPI — RAG & advisors) and
ShipSmart-Orchestrator
(Java / Spring Boot — upcoming AI features) call this server instead of
implementing tools in-process.
Stack: FastAPI 0.135.3 · Python 3.13 · uv · pydantic-settings · httpx · MCP-compatible HTTP contract
Table of contents
Related MCP server: one-mcp
The ShipSmart ecosystem
This service is one of six sibling repositories. Clone them as siblings of this directory when working on the full system. All six are also mirrored together in ShipSmart — the umbrella repository that snapshots each component at its latest stable milestone.
Repo | Role | Stack |
React SPA — user-facing UI | React 19, Vite, TypeScript | |
Java transactional API — single writer to Supabase Postgres; quotes, bookings, saved options, carrier integration | Spring Boot 3.4, Java 17 | |
Python AI/orchestration service — RAG, advisors, recommendations, compliance (UC2), multi-agent workflow (UC3/UC4) | FastAPI, Python 3.13 | |
ShipSmart-MCP (this repo) | MCP tool server — | FastAPI + MCP |
Supabase migrations + edge functions, deployment configs, docs | Supabase, Render blueprints | |
Cross-repo integration harness — contract + live e2e suites, cross-service Postman collection | Python 3.13, pytest |
┌──────────────────────────────┐
│ ShipSmart-Web │
│ React SPA · Vite │
└──────────────┬───────────────┘
│ Authorization: Bearer <Supabase JWT>
┌────────────┴────────────┐
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ ShipSmart-Orchestrator │◀──│ ShipSmart-API │
│ Java / Spring Boot │ │ Python / FastAPI │
│ Sole writer to Postgres │ │ RAG · advisors · recs │
│ Carrier integration (FedEx) │ │ │
└──────────────┬───────────────┘ └──────────────┬───────────────┘
│ │
│ X-MCP-Api-Key │ X-MCP-Api-Key
│ (reserved — upcoming │
│ AI-assist flows) │
▼ ▼
┌─────────────────────────────────────────┐
│ ShipSmart-MCP (this repo) │
│ FastAPI · MCP HTTP contract │
│ validate_address · get_quote_preview │
└────────────────────┬────────────────────┘
│
▼
┌──────────────────────────────┐
│ ShippingProvider (pluggable)│
│ mock · ups · fedex · dhl · usps │
└──────────────────────────────┘The tool layer is centralized here — add a tool once, every service
gets it. The Java orchestrator's wiring (SHIPSMART_MCP_URL /
SHIPSMART_MCP_API_KEY) is in place but no Java call sites exist yet;
Python's RemoteToolRegistry hydrates from this server's /tools/list
on boot and routes every advisor/orchestration tool call through here.
What this service does
Capability | Endpoint | Notes |
Service discovery |
| Name, version, tool count, endpoint map. Unauthenticated. |
Liveness |
| Probe used by Render's health check. Unauthenticated. |
Tool catalog |
| Returns JSON Schemas for every registered tool. |
Tool execution |
| Executes a tool by name with the provided arguments. |
Interactive docs |
| Swagger UI / ReDoc. Mounted only when |
Wire-compatible with the
MCP tools/list / tools/call
semantics: every call returns { success, content: [...], error? },
where content is a list of {type, text} blocks suitable for LLM
consumption.
HTTP contract
Method | Path | Purpose |
GET |
| Service discovery (name, version, tool count, endpoints). |
GET |
| Liveness probe used by Render. |
POST |
| Return schemas for all registered tools. |
POST |
| Execute a tool by name with the provided arguments. |
GET |
| Swagger UI (non-production only). |
GET |
| ReDoc (non-production only). |
Auth
If MCP_API_KEY is set on the server, every POST /tools/* request
must send the matching value in X-MCP-Api-Key. If MCP_API_KEY is
empty, auth is disabled (local dev only). GET / and GET /health are
always unauthenticated so health checks and service discovery work
without the shared secret.
Error responses
Condition | HTTP | Body |
Missing or invalid | 401 |
|
Unknown tool name | 404 |
|
Input validation failure or tool exception | 200 |
|
Validation and execution errors deliberately return HTTP 200 with
success=false so consumers can distinguish protocol-level failures
(4xx) from tool-level failures (200 + success=false).
Tools
Name | Description |
| Validate + normalize a shipping address through the configured carrier. |
| Non-binding rate preview for a package. Final rates come from the Java API. |
Tools delegate to pluggable ShippingProvider implementations selected
by SHIPPING_PROVIDER.
Provider | Status |
| Fully working. Returns deterministic fake data for local dev and tests. |
| Stub — class exists but is not yet production-ready. |
| Stub — class exists but is not yet production-ready. |
| Stub — class exists but is not yet production-ready. |
| Stub — class exists but is not yet production-ready. |
Adding a tool is a matter of dropping a new class into app/tools/ and
registering it in app/main.py.
Provider startup behavior
SHIPPING_PROVIDER=mock(default) emits a loudWARNINGat startup so operators are not surprised by fake data.Selecting a real carrier (
ups/fedex/dhl/usps) without all required credentials raisesValueErrorat startup. There is no silent fallback to mock — misconfiguration fails fast and visibly.
Architecture inside this service
app/
├── main.py FastAPI app, tool registry wiring, lifespan, provider selection
├── core/
│ ├── config.py pydantic-settings — env-driven configuration
│ └── middleware.py RequestLoggingMiddleware (X-Request-Id + W3C traceparent)
├── tools/
│ ├── registry.py ToolRegistry — `tools/list` + `tools/call` dispatch
│ ├── base.py Tool ABC + JSON Schema helpers
│ ├── address_tools.py validate_address
│ └── quote_tools.py get_quote_preview
└── providers/
├── base.py ShippingProvider ABC
├── shipping_provider.py Provider factory keyed by SHIPPING_PROVIDER
├── mock_provider.py Deterministic fake data — local dev + tests
├── ups_provider.py Stub
├── fedex_provider.py Stub
├── dhl_provider.py Stub
└── usps_provider.py StubThe tool layer is decoupled from carrier implementations: each tool
calls into a ShippingProvider chosen at startup. New tools land in
app/tools/ and register themselves with the ToolRegistry; new
carriers land in app/providers/ and slot into the
SHIPPING_PROVIDER switch.
Read-only least-privilege invariant
This server is strictly read-only: every tool it serves is a pure
read/preview operation (address validation, non-binding rate preview).
No tool or provider writes to a database, mutates persistent state,
moves money, or books anything — those actions belong exclusively to the
Java Orchestrator, the single writer of record. The invariant is
enforced, not just documented: app/main.py constrains the registry
to a READ_ONLY_TOOL_ALLOWLIST (validate_address, get_quote_preview)
and _build_registry() raises at startup if any tool outside that
allowlist is ever registered. Every tool input is also fully validated
against its JSON Schema before execution.
Configuration
All settings are loaded from environment variables (or .env for local
dev). See .env.example for the full list and defaults.
Variable | Purpose |
|
|
| Bind address. Defaults |
| Standard logging level (default |
| Comma-separated origins allowed by the CORS middleware. |
| Shared secret enforced on |
|
|
| ISO-3166 alpha-2 home country enforced when scope is |
| One of |
| Per-carrier credentials and base URLs. |
Running locally
Prerequisites
Python 3.13+
uv0.6.5+
Install & configure
cp .env.example .env
# fill in credentials if you want a real carrier integration;
# default is SHIPPING_PROVIDER=mock
uv syncRun
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8001Service comes up on http://localhost:8001. Browse the live OpenAPI
spec at http://localhost:8001/docs (development only).
Smoke test
curl -s http://localhost:8001/health
curl -s -X POST http://localhost:8001/tools/list
curl -s -X POST http://localhost:8001/tools/call \
-H 'Content-Type: application/json' \
-d '{
"name": "validate_address",
"arguments": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip_code": "94105"
}
}'If MCP_API_KEY is set, add -H "X-MCP-Api-Key: $MCP_API_KEY" to the
/tools/* calls.
Postman collection
postman/ShipSmart-MCP.postman_collection.json
walks the same contract with assertions on every request: discovery + liveness,
tools/list, a happy-path tools/call for both tools, and the error semantics
this README documents (unknown tool → 404; schema-invalid input → 200 with
success:false, never a 4xx/5xx). Every request pins an X-Request-Id, and the
/tools/* requests already carry the X-MCP-Api-Key header — filling the
environment's MCP_API_KEY variable (empty by default for a no-auth local boot)
is all that auth needs. Import it with
postman/environments/local.postman_environment.json
(base_url defaults to http://127.0.0.1:8001), or run it headless:
npx newman run postman/ShipSmart-MCP.postman_collection.json \
-e postman/environments/local.postman_environment.jsonTests
uv run pytest # 95 tests, ~0.5s, no networkTests live under tests/ and use pytest-asyncio (async mode = auto). What they cover:
File | Focus |
| The HTTP contract: |
| Accepted/rejected JSON-Schema boundaries (ZIP+4, weight/dimension maxima, type coercion). |
| Tool execution + registry (dedup, sorting, schema shape). |
| Mock provider behavior, the carrier factory (credential gating, case-insensitivity), and per-carrier |
The tool schemas here are also asserted from ShipSmart-Test/contract/ so a rename can't silently break ShipSmart-API or the Web client.
Lint & formatting
uv run ruff check . # lint (line length, imports, pyflakes)A .pre-commit-config.yaml wires ruff plus hygiene hooks (end-of-file fixer, trailing
whitespace, YAML, merge-conflict) — install once with uvx pre-commit install. CI
(.github/workflows/ci.yml) runs ruff check . then pytest -q on every push / PR.
Observability
RequestLoggingMiddleware (app/core/middleware.py) handles
correlation IDs for every request:
Reads
X-Request-Idfrom the inbound request, or mints a UUID hex if absent.Reads W3C
traceparent, or mints a fresh one if absent or malformed.Echoes both headers on the response so callers can
grepby ID across services.Emits one log line per request on the
shipsmart_mcp.requestslogger:GET /health → 200 (1.4ms) [a1b2c3...]
Pass X-Request-Id from upstream services to stitch a single request
across ShipSmart-API → MCP → carrier APIs. The Python service's
outbound_headers() helper already forwards both X-Request-Id and
traceparent on every MCP hop.
Deployment (Render)
render.yaml is a Render Blueprint defining the
deployed service:
Python web service, build via
pip install uv && uv sync, start viauvicorn app.main:app --host 0.0.0.0 --port $PORT.Health check at
/health.MCP_API_KEYissync: false— set it once in the Render dashboard and use the same value for every consumer'sSHIPSMART_MCP_API_KEY.Default
SHIPPING_PROVIDER=fedexpointing athttps://apis-sandbox.fedex.com(FedEx sandbox, not production). Override the base URL when promoting to live carrier traffic.CORS origins are pinned in the blueprint to the deployed consumer URLs (
shipsmart-api-python,shipsmart-api-java,shipsmart-web).
Provision by pointing Render at this repo; all sync: false env vars
must be filled before the first deploy succeeds.
The companion blueprints for the other services live alongside their repos and in ShipSmart-Infra; deploy them together when promoting a release.
Consumers & cross-service contract
Caller | Endpoint | Used by |
Python → MCP |
| Every advisor and orchestration tool call. See |
Java → MCP |
| Reserved for upcoming AI-assist features. Wired via |
Ops / health |
| Render health probe + service discovery. Always unauthenticated. |
ShipSmart-API (Python / FastAPI; deployed as
shipsmart-api-pythonon Render): pointsSHIPSMART_MCP_URLat this server and calls/tools/list+/tools/callfrom its orchestration and advisor services. Tool catalog is hydrated at boot viaRemoteToolRegistry.ShipSmart-Orchestrator (Java / Spring Boot; deployed as
shipsmart-api-javaon Render): will call the same HTTP contract from its upcoming AI-assist flows. No tool logic lives in the Java codebase.
When changing the tool surface, this repo is the source of truth. Consumers should:
Python: nothing to update for catalog changes — the registry re-hydrates from
/tools/liston boot. For schema changes, update any callers inapp/services/orchestration_service.py/shipping_advisor_service.py/tracking_advisor_service.py.Java: mirror the contract in whichever client lands when the AI-assist flows ship.
Operational notes
SHIPPING_PROVIDER=mockwarning on boot — expected. The mock provider returns deterministic fake data; do not promote that configuration to production.Server refuses to start with
ValueErrorafter carrier switch — required credentials for the selected carrier are missing. There is no silent fallback to mock; fill in the matchingUPS_*/FEDEX_*/DHL_*/USPS_*envs.401 Invalid or missing X-MCP-Api-Key—MCP_API_KEYis set on the server but the client did not send the matching header (or sent the wrong value). ConfirmSHIPSMART_MCP_API_KEYon the consumer matchesMCP_API_KEYhere.404 Tool not found— the tool name in/tools/calldoes not match anything registered inapp/main.py. HitPOST /tools/listto see the current catalog.200 { success: false }from/tools/call— protocol succeeded but the tool itself raised (validation failure, provider error). Theerrorfield has the detail; HTTP stays 200 by design so consumers can distinguish transport vs. tool failures./docsreturns 404 in production — expected. Swagger UI is mounted only whenAPP_ENV != production.CORS blocked from a consumer — add the calling origin to
CORS_ALLOWED_ORIGINS(comma-separated). On Render, the blueprint already pins the three deployed consumers; override in the dashboard for additional origins.
License
See LICENSE for the full text.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nia194/ShipSmart-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server