CrossBorder Copilot MCP
by cang-ge
README.md
# CrossBorder Copilot MCP
语言:**English** · [简体中文](README.zh.md)
CrossBorder Copilot is a local, offline, reproducible **review-gated MCP Server demo** for cross-border ecommerce customer-service operations.
> **One-line pitch**: turn a logistics exception into a traceable, bilingual and review-gated support action — without letting a draft silently become a refund, a promise, or a duplicate ticket.
## Why this project matters
This is not another chat window wrapped around an LLM. It models the operational boundary that a real customer-service copilot must respect:
- **Evidence before explanation** — order facts, tracking events and policy references remain visible in the result.
- **Draft before side effect** — a customer reply is only a draft; ticket creation is a separate confirmed operation.
- **Stable contract before integration** — local JSON/SQLite adapters can later be replaced by permissioned ERP and carrier clients without changing the MCP tool contract.
- **Bilingual by call, not by global state** — operators can switch `locale` between `zh` and `en` in the same running server while machine fields remain stable.
The result is a compact portfolio project that demonstrates product judgment, MCP protocol design, deterministic workflow engineering and safety-aware AI product thinking in one concrete scenario.
## The problem and the boundary
When a shipment goes wrong, a support operator has to switch between orders, carrier tracking, marketplace policy pages, and a ticketing system. A fast answer is not enough: the reply must be grounded in evidence and must not promise a refund, compensation, delivery date, or other action that has not been approved.
This project demonstrates one controlled workflow: **retrieve evidence -> classify the exception -> retrieve a citable policy reference -> draft a bilingual reply -> ask for human confirmation -> create an idempotent local ticket**.
The data is local simulation data. Amazon and Shopify are represented by JSON records through replaceable adapters; this repository does not call Amazon, Shopify, DHL, UPS, Shoplazza, 店小秘, or 马帮 production APIs. It does not send customer messages, issue real refunds, or create real platform tickets.
## The core workflow in one view
```text
Order + tracking facts
|
v
Exception classification ──> Citable marketplace policy
| |
+──────────────┬───────────────+
v
Bilingual reply draft
|
Human confirmation
|
v
Idempotent local ticket
```
The design deliberately separates **recommendation** from **execution**. A support operator can inspect the evidence and policy before confirming the only write-like step.
## What is implemented
The server exposes six structured MCP tools:
| Tool | Input | Output and safety boundary |
| --- | --- | --- |
| `get_order_detail` | `order_id`, optional `locale` (`zh`/`en`) | A privacy-safe `order` plus ordered tracking `events`; customer name, email, phone, and address are masked. |
| `analyze_logistics_exception` | `order_id`, optional timezone-aware `now`, optional `locale` | `exception_type`, `severity`, `evidence`, `recommended_actions`, and `requires_human_review`. |
| `search_marketplace_policy` | `platform`, `exception_type`, `country`, optional `top_k` and `locale` | Ranked local policy records with source title, URL, conditions, score, score components, and verification flag. |
| `draft_customer_reply` | `order_id`, `language` (`en`/`zh`), optional `now` and `locale` | A deterministic `subject`/`body`, source facts, prohibited commitments, and a review flag. It creates a draft only; it never sends. |
| `create_support_ticket` | `order_id`, `confirmed`, `idempotency_key`, optional `now` and `locale` | An open local SQLite ticket. `confirmed` must be `true`; a repeated key returns the same ticket with `idempotent_replay: true`. |
| `get_ticket_status` | `ticket_id`, optional `locale` | The persisted local ticket status, timestamps, assignee/update fields, and replay marker. |
The human confirmation point is `create_support_ticket`. A false confirmation fails with the stable `confirmation_required` error before the idempotency lookup or database creation, even when that key already exists. A blank key fails with `idempotency_key_required`.
## A concrete run: `SHP-1001`
The fixed demo order makes the product behavior easy to inspect:
| Stage | Result | Why it matters |
| --- | --- | --- |
| Order lookup | Masked Shopify order with UPS events | Protects customer data while preserving operational context. |
| Exception analysis | `stalled` / `high` with 96-hour same-node evidence | Returns the reason, not only a label. |
| Policy search | Ranked Shopify policy reference with source URL and score components | Separates a citable reference from an unverified entitlement. |
| Reply drafting | Chinese or English subject/body with source facts and prohibited commitments | Drafting cannot send a message or promise a refund/date. |
| Ticket create | Requires `confirmed=true` and a unique idempotency key | Makes the human approval boundary explicit. |
| Retry | Same ticket plus `idempotent_replay=true` | Prevents duplicate operational work. |
The complete call sequence, expected outputs and interview narration are in [`docs/demo-script.md`](docs/demo-script.md).
## What the implementation demonstrates
| Capability | Implementation evidence |
| --- | --- |
| MCP server design | Six discoverable tools with typed input schemas and structured outputs. |
| Domain modeling | Frozen order, tracking, policy, analysis, reply and ticket models with stable enums and error codes. |
| Replaceable integrations | `OrderAdapter` composes local repositories behind the seam where real ERP/carrier clients would later connect. |
| Deterministic decisioning | Ordered exception rules and explicit policy scoring make the workflow reproducible and debuggable. |
| AI product safety | Allowlisted reply facts/actions, prohibited commitments, masked public views and mandatory human review. |
| Reliability | SQLite transaction plus a unique idempotency key makes retries safe across process-local memory loss. |
| Internationalization | Stateless per-call `locale` for operator-facing content; independent `language` for customer drafts. |
| Evaluation discipline | Fixed 41-record offline suite covers normal, delayed, stalled, returned, dispute, missing-data and replay boundaries. |
The project intentionally uses deterministic templates instead of hiding the core behavior behind an LLM. An LLM can be added later as a constrained drafting layer, but evidence validation, structured output checks and human review remain the control points.
## Bilingual MCP contract
All six tools accept an optional `locale` of `zh` or `en`; the default is `zh`. This is a stateless per-call presentation choice, so the same running server can serve Chinese and English calls concurrently. For example:
```json
{"order_id":"SHP-1001","now":"2026-08-29T12:00:00Z","locale":"zh"}
```
The tool names, field names, error codes, enum values, IDs, URLs, and other machine contract fields remain English and stable in either locale. Only human-readable descriptions, evidence, actions, policy text, status labels, and safe messages are localized. `draft_customer_reply` keeps its `language` input independent: `language` controls the customer-facing subject/body, while `locale` controls the operator-facing metadata and messages.
换句话说,工具名、字段名、错误码、ID 和 URL 等机器契约保持英文不变;`locale` 只切换人类可读内容。默认语言为中文(default `zh`),也可以按次选择英文(`en`)。
The MCP server localizes server metadata and tool results, not the third-party MCP Inspector application. The Inspector's own menus and UI language are not translated by this MCP. Node.js/npm/npx are only dependencies for launching Inspector; the offline MCP server and an MCP Host configuration do not require them. See the Inspector section below for the direct launch command when those tools are available in the environment.
## Architecture at a glance

The diagram shows the end-to-end path from an MCP client through validation, domain services, local adapters, repositories, and the explicit confirmation boundary before ticket persistence.
```text
MCP Host / Inspector / CLI client
|
stdio JSON-RPC
v
MCPServer + six tools (validation and serialization)
|
OperationsService / domain services
| |
Local Adapter layer -> JSON repositories TicketService -> SQLite ticket store
```
The detailed boundaries and data flow are in [`docs/architecture.md`](docs/architecture.md). The product decisions are in [`docs/product-brief.md`](docs/product-brief.md), and the fixed interview walkthrough is [`docs/demo-script.md`](docs/demo-script.md).
## Repository map
```text
src/crossborder_mcp/
├── server.py # MCP entry point and dependency wiring
├── tools.py # public tool contract, validation, serialization
├── operations_service.py # end-to-end application composition
├── exception_service.py # deterministic logistics classification
├── policy_service.py # local policy filtering and scoring
├── reply_service.py # safe bilingual draft generation
├── ticket_service.py # confirmation, SQLite write and replay handling
├── adapters.py # replaceable order/tracking integration seam
├── repositories.py # local JSON and SQLite persistence helpers
├── localization.py # operator-facing zh/en presentation layer
└── models.py / errors.py # domain contract and stable failures
data/ # local ERP, tracking and policy fixtures
tests/ # unit, protocol, integration and evaluation tests
docs/ # product brief, architecture, demo and evaluation report
examples/ # MCP Host configuration example
```
The most useful interview path is `server.py → tools.py → operations_service.py → domain service → adapter/repository`, then `ticket_service.py` at the explicit write boundary.
## Windows quick start
Run these commands from the project root (`D:\Study\crossborder-copilot-mcp`):
```powershell
python -m pip install -e ".[test]"
python -m pytest -v
python scripts\run_evaluation.py
python -m json.tool examples\mcp-client-config.json
python -m compileall -q src tests scripts
```
To run the stdio server directly for a client:
```powershell
$env:PYTHONPATH = "$PWD\src"
python -m crossborder_mcp.server
```
The process waits for MCP JSON-RPC messages on stdin. It writes protocol messages to stdout and logs to stderr. Stop it with `Ctrl+C` when running it manually. The default runtime ticket database is `data\support_tickets.sqlite3`; tests and evaluation use temporary databases.
## Connect an MCP Host on Windows
[`examples/mcp-client-config.json`](examples/mcp-client-config.json) is a local-machine example. It uses the verified Python executable and absolute `PYTHONPATH`, `-m crossborder_mcp.server`, and contains no secret. Replace both the Python path and project path before using it on another computer; do not copy this example as if those paths were portable.
The important shape is:
```json
{
"command": "C:\\path\\to\\python.exe",
"args": ["-m", "crossborder_mcp.server"],
"env": {
"PYTHONPATH": "D:\\path\\to\\crossborder-copilot-mcp\\src"
}
}
```
## MCP Inspector and the mcp 2.1.1 detail
This machine was checked with Python 3.14.2 and `mcp version`, which returned `MCP version 2.1.1`. In that version:
- `mcp dev FILE_SPEC` is the official development command and launches `@modelcontextprotocol/inspector` through `npx`.
- `mcp run FILE_SPEC` runs an imported `MCPServer` object or a file that calls its own `server.run()`.
- This repository's `server.py` is a package module with relative imports and creates the server inside `main()`. Therefore `mcp dev src\crossborder_mcp\server.py` is not a valid command for this entry point: the verified result is `ImportError: attempted relative import with no known parent package`. Do not document it as a working shortcut.
- The direct Inspector form for a machine with Node.js/npm installed is:
```powershell
$env:PYTHONPATH = "$PWD\src"
npx.cmd @modelcontextprotocol/inspector python -m crossborder_mcp.server
```
In the Inspector UI, select the spawned stdio server, connect, open Tools, and confirm these six names: `get_order_detail`, `analyze_logistics_exception`, `search_marketplace_policy`, `draft_customer_reply`, `create_support_ticket`, and `get_ticket_status`. The Inspector process is a development dependency and is not required by the offline server or the Host config.
If `node`/`npx` are not on `PATH`, that affects only the optional Inspector launcher. Use the standalone stdio command or the existing [`examples/mcp-client-config.json`](examples/mcp-client-config.json) configuration with an MCP Host. The Inspector UI language is controlled by Inspector itself; changing a tool's `locale` changes server responses, not Inspector menus. Once connected, call the same tool twice with `locale: "zh"` and `locale: "en"` to compare localized human-readable fields while machine fields remain stable.
## Real evaluation results
The checked-in [`docs/evaluation-report.md`](docs/evaluation-report.md) was generated by `python scripts/run_evaluation.py` from 41 fixed records: 40 evaluable workflow cases plus one separately counted `tracking_not_found` input failure. The four core exception classes have eight records each; repeated records are explicit time-slice variants of the same local orders, not 40 independent production orders. Four `empty_snapshot` records exercise the domain-level `insufficient_data` branch.
| Metric | Measured result |
| --- | ---: |
| Exception type accuracy | 40/40 (100.0%) |
| Severity accuracy | 40/40 (100.0%) |
| Policy Recall@3 | 32/32 (100.0%) |
| Reply language match | 40/40 (100.0%) |
| Unsupported content | 0 |
| Idempotency interception | 40/40 (100.0%) |
The linked evaluation report records detailed timing for its local run. Those figures vary by run; their boundary starts at local order lookup/snapshot setup and ends after the second idempotent ticket create, excluding case loading, temporary-directory lifecycle, and report rendering. They are offline fixture measurements, not production SLA, platform latency, availability, or customer-outcome claims.
The report also records two important limits: at exactly `SHP-1002`'s ETA, the strict `now > ETA` rule returns `normal`; and `case-041` is an adapter-level `tracking_not_found` input failure, distinct from `insufficient_data`.
## Security and non-goals
- Public order views mask name, email, phone, and address; error responses redact tracking details where appropriate.
- Domain errors use stable codes such as `order_not_found`, `tracking_not_found`, `confirmation_required`, `idempotency_key_required`, and `ticket_not_found`.
- Replies are deterministic drafts built from allowlisted facts/actions. They never send messages and explicitly list prohibited commitments: `refund_issued`, `compensation_approved`, and `guaranteed_delivery_date`.
- Ticket creation is a confirmed side effect guarded by a unique SQLite idempotency key and replay detection.
- There is no network access, real platform authentication, real ERP integration, refund execution, outbound messaging, LLM call, vector database, queue, or web UI.
Next steps would be to replace one Adapter at a time with authenticated platform clients, preserve the tool contract, add audit/permission controls, and introduce an LLM only behind the same evidence whitelist and human-review gate. None of those integrations are present in this demo.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues