Customer Order MCP (Python)
A safe Python 3.11+ MCP server modeled on the Java/Spring projectweibxiao/internal-mcp-server.It uses the official MCP Python SDK and exposes Streamable HTTP athttp://localhost:8000/mcp.
Tools
create_customer(customer_id, name, email)creates an ACTIVE demo customer.search_customers(query, limit=10)searches ID, name, and email (1โ25 results).get_customer(customer_id)returns one minimal customer profile.create_pending_order(customer_id, sku, quantity, unit_price)creates an orderinPENDING_REVIEWonly. It never charges, submits, reserves, or fulfills.get_orders_by_customer(customer_id)lists the customer's orders.run_health_check()returns safe service status.- Resource:
internal://service-info.
Run locally
Using uv:
uv sync --extra test
uv run pytest
uv run customer-order-mcp
Or using standard Python:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install -e ".[test]"
pytest
customer-order-mcp
Connect an MCP client or the MCP Inspector to http://localhost:8000/mcp.
npx -y @modelcontextprotocol/inspector
Fixing 421 Misdirected Request
The MCP SDK protects local servers from DNS-rebinding attacks. A 421 responsemeans the request's Host header is not allowed. This commonly happens with anngrok URL, reverse proxy, LAN hostname, or when connecting to 0.0.0.0.
For a tunnel such as https://example-name.ngrok-free.app, start the server withthe exact public host and browser origin:
MCP_ALLOWED_HOSTS="example-name.ngrok-free.app" \
MCP_ALLOWED_ORIGINS="https://example-name.ngrok-free.app" \
uv run customer-order-mcp
Multiple values are comma-separated. Do not include https:// inMCP_ALLOWED_HOSTS; do include it in MCP_ALLOWED_ORIGINS. If a proxy sends aport in the Host header, add example-name.ngrok-free.app:* instead. Keep thisallowlist narrow rather than disabling the protection.
To listen on all network interfaces for local Docker or LAN testing:
MCP_HOST=0.0.0.0 uv run customer-order-mcp
Binding to 0.0.0.0 makes the process reachable beyond localhost and disablesthe SDK's automatic localhost-only allowlist unless explicit allowlists are set.
Container
docker build -t customer-order-mcp:0.1.0 .
docker run --rm -p 8000:8000 customer-order-mcp:0.1.0
Design and storage
The MCP layer (server.py) is deliberately thin. service.py owns validation andbusiness rules, while store.py is the storage boundary. InMemoryStore isthread-safe and contains two demo customers; all changes disappear on restart.Replace it with an authenticated database/API adapter for real use while preservingthe service-facing methods.
Validation includes unique customer IDs/emails, validated email format, nonblanksearch/SKU fields, search limits of 1โ25, quantities of 1โ100, positive finiteprices with at most two decimal places, and an existing customer requirement.
Production safety
Do not expose this demo publicly with real customer data. Add OAuth or an APIgateway, authorize every lookup and mutation, audit tool calls without loggingsecrets or sensitive payloads, use least-privilege integrations, and replacein-memory storage. Order creation intentionally stops at PENDING_REVIEW.
Note about the Java reference
The Python server includes the Java repository's current customer creation andcustomer-order lookup tools in addition to the tools listed in its README. Its MCPannotations correctly mark customer/order creation as state-changing andnon-idempotent.