Skip to main content
Glama
weibxiao

Customer Order MCP

by weibxiao
README.md
# Customer Order MCP (Python)

A safe Python 3.11+ MCP server modeled on the Java/Spring project
[`weibxiao/internal-mcp-server`](https://github.com/weibxiao/internal-mcp-server).
It uses the official MCP Python SDK and exposes Streamable HTTP at
`http://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 order
  in `PENDING_REVIEW` only. 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`:

```bash
uv sync --extra test
uv run pytest
uv run customer-order-mcp
```

Or using standard Python:

```bash
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`.

```bash
npx -y @modelcontextprotocol/inspector
```

### Fixing `421 Misdirected Request`

The MCP SDK protects local servers from DNS-rebinding attacks. A `421` response
means the request's `Host` header is not allowed. This commonly happens with an
ngrok 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 with
the exact public host and browser origin:

```bash
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://` in
`MCP_ALLOWED_HOSTS`; do include it in `MCP_ALLOWED_ORIGINS`. If a proxy sends a
port in the Host header, add `example-name.ngrok-free.app:*` instead. Keep this
allowlist narrow rather than disabling the protection.

To listen on all network interfaces for local Docker or LAN testing:

```bash
MCP_HOST=0.0.0.0 uv run customer-order-mcp
```

Binding to `0.0.0.0` makes the process reachable beyond localhost and disables
the SDK's automatic localhost-only allowlist unless explicit allowlists are set.

## Container

```bash
docker build -t customer-order-mcp:0.1.0 .
docker run --rm -p 8000:8000 customer-order-mcp:0.1.0
docker run --rm \
  --name customer-order-mcp \
  -p 8000:8000 \
  -e DATABASE_URL="mysql+pymysql://root:password@mysql:3306/customer_order" \
  customer-order-mcp:0.1.0
```
docker build -t your-registry/customer-order-mcp:latest .
docker push your-registry/customer-order-mcp:latest
kubectl apply -f k8s/customer-order-mcp.yaml
##Check status and logs:
kubectl get pods -n customer-order
kubectl get service -n customer-order
kubectl logs -n customer-order deployment/customer-order-mcp

##Access it locally:
kubectl port-forward \
  -n customer-order \
  service/customer-order-mcp \
  8000:8000
  
## Design and storage

The MCP layer (`server.py`) is deliberately thin. `service.py` owns validation and
business rules, while `store.py` is the storage boundary. `InMemoryStore` is
thread-safe and contains two demo customers; all changes disappear on restart.
Replace it with an authenticated database/API adapter for real use while preserving
the service-facing methods.

Validation includes unique customer IDs/emails, validated email format, nonblank
search/SKU fields, search limits of 1–25, quantities of 1–100, positive finite
prices 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 API
gateway, authorize every lookup and mutation, audit tool calls without logging
secrets or sensitive payloads, use least-privilege integrations, and replace
in-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 and
customer-order lookup tools in addition to the tools listed in its README. Its MCP
annotations correctly mark customer/order creation as state-changing and
non-idempotent.