Skip to main content
Glama
clarencez1011

MCP Connection Test Server

README.md
# 兴先达订单与库存 MCP Server

这是一个只使用 Streamable HTTP 的云端 MCP Server。它把下单系统与库存系统
封装成 Agent 可以发现和调用的 MCP 工具。公网入口为:

```text
https://你的域名/mcp
```

MCP Server 本身不调用模型,Agent 负责决定何时调用工具。服务保留
`mcp_connection_test` 作为链路检查,并提供 3 个业务工具:

- `get_latest_inventory`:获取全部库存,或查询一个 SKU 的最新库存。
- `update_inventory_from_order`:读取一张已存在订单,并把订单中的
  `inventoryDelta` 幂等地应用到库存系统。
- `create_order`:在订单系统中创建订单,但不会隐式调用库存更新。

## 部署结构

```text
上游 MCP 客户端 -> 已有 HTTPS 服务 443 -> 127.0.0.1:18080 -> MCP 容器 8000
                                                              -> 订单系统 API
                                                              -> 库存系统 API
```

项目不再启动 Caddy,也不占用 80、443。MCP 只绑定服务器本机回环地址,
由现有 Nginx、Apache 或 Caddy 使用已有证书反向代理。

## 一、准备服务器和域名

1. 云服务器安装 Docker Engine 和 Docker Compose 插件。
2. 准备已有证书覆盖的域名,例如 `mcp.example.com`。
3. 确认现有 HTTPS 服务能够修改反向代理配置。

`MCP_DOMAIN` 只填写域名,不要包含 `https://` 或路径。

## 二、上传项目

在本地 Mac 执行,将 `SERVER_USER` 和 `SERVER_IP` 换成真实值。这里明确排除
本地 `.env` 和虚拟环境,避免把旧 API Key 或无关文件上传到服务器:

```bash
rsync -av \
  --exclude='.env' \
  --exclude='.venv' \
  --exclude='__pycache__' \
  "/Users/clarence/Desktop/项目/兴先达/MCPtest/" \
  SERVER_USER@SERVER_IP:~/mcp-connection-test/
```

登录服务器:

```bash
ssh SERVER_USER@SERVER_IP
cd ~/mcp-connection-test
```

## 三、填写域名并启动

```bash
cp deploy.env.example deploy.env
```

编辑 `deploy.env`:

```text
MCP_DOMAIN=mcp.example.com
MCP_LOCAL_PORT=18080
PYTHON_IMAGE=public.ecr.aws/docker/library/python:3.12-slim
ORDER_API_BASE_URL=http://host.docker.internal:32769
INVENTORY_API_BASE_URL=http://host.docker.internal:32768
```

`PYTHON_IMAGE` 默认使用 AWS Public ECR 中的 Python 官方镜像,构建时不会再访问
Docker Hub。Amazon ECR Public 的公开镜像无需登录即可拉取;若服务器曾保存过
错误的 ECR 登录状态,可先执行 `docker logout public.ecr.aws`。

如果订单或库存 API 不在 MCP 宿主机,把对应地址换成 Agent 网络能够访问的
`http://` 或 `https://` 地址。如果业务 API 使用 Bearer Token,再填写
`ORDER_API_TOKEN` 和 `INVENTORY_API_TOKEN`;令牌只放在 `deploy.env`,不要提交
到仓库。

配套库存系统默认固定映射为宿主机端口 `32768`。不要再使用随机端口;如果因
端口冲突确实需要修改,库存系统的 `INVENTORY_PORT` 与这里的
`INVENTORY_API_BASE_URL` 必须同步调整。

如果 18080 已被占用,可以改成其他未使用的高位端口,例如 18081。检查端口:

```bash
sudo ss -ltnp | grep ':18080'
```

构建并后台启动:

```bash
docker compose --env-file deploy.env up -d --build
```

如果之前启动过旧版 Caddy 编排,使用下面的命令同时清理已经移除的 Caddy
容器;不会影响服务器上其他项目的容器:

```bash
docker compose --env-file deploy.env up -d --build --remove-orphans
```

查看运行状态和日志:

```bash
docker compose --env-file deploy.env ps
docker compose --env-file deploy.env logs --tail=100
```

## 四、接入现有 HTTPS 服务

如果使用 Nginx,将 `nginx-mcp-location.conf.example` 中的 `location = /mcp`
配置加入已有证书域名对应的 `server { ... }` 中。确认反向代理端口与
`MCP_LOCAL_PORT` 一致,然后执行:

```bash
sudo nginx -t
sudo systemctl reload nginx
```

Nginx 的 `proxy_pass` 应指向:

```text
http://127.0.0.1:18080
```

最终公网 MCP 地址仍然是:

```text
https://mcp.example.com/mcp
```

## 五、验证工具发现和调用

在本地 Mac 执行,验证公网链路:

```bash
/opt/homebrew/Caskroom/miniconda/base/bin/python \
  "/Users/clarence/Desktop/项目/兴先达/MCPtest/verify_remote.py" \
  https://mcp.example.com/mcp
```

成功时会显示:

```text
发现工具:create_order, get_latest_inventory, mcp_connection_test, update_inventory_from_order
调用返回:MCP连接成功
```

这一步会真实执行 MCP 初始化、`tools/list` 和 `tools/call`,比普通的端口或
HTTP 检查更准确。

## 六、上游配置

将上游 MCP 客户端的服务器 URL 配置为:

```text
https://mcp.example.com/mcp
```

### 获取最新库存

查询全部库存:

```json
{}
```

查询单个库存:

```json
{
  "sku": "003"
}
```

`sku` 同时接受 `3`、`003` 或 `X-003`。返回的 `meta.version` 可用于后续
需要并发保护的更新。

### 创建订单

```json
{
  "product_id": 3,
  "quantity": 5
}
```

成功后记住返回的 `order.id`。订单系统当前是补货单语义,因此新订单包含
`inventoryDelta: 5`;创建订单本身不会自动修改独立库存系统。

### 根据订单更新库存

```json
{
  "order_id": "ORD-20260821153000-ABCD"
}
```

工具会读取订单,将 `X-003` 映射为库存 SKU `003`,再把
`inventoryDelta` 应用到库存。库存请求使用订单号生成幂等键,同一订单重复
调用不会重复增加库存。

如果 Agent 刚读取过库存,也可以带版本号进行乐观并发保护:

```json
{
  "order_id": "ORD-20260821153000-ABCD",
  "expected_version": 7
}
```

如果返回 `VERSION_CONFLICT`,Agent 应重新调用 `get_latest_inventory`,确认新
库存后再决定是否重试。普通订单同步不强制填写 `expected_version`。

### 推荐的 Agent 调用顺序

1. 需要确认现货时调用 `get_latest_inventory`。
2. 用户确认下单后调用 `create_order`。
3. 使用返回的 `order.id` 调用 `update_inventory_from_order`。
4. 如需向用户确认最终库存,再调用一次 `get_latest_inventory`。

订单创建与库存更新是两个独立动作。这样即使第二步之后网络中断,Agent 仍可
用同一订单号安全重试库存同步,也不会重复下单。

## 本地测试

业务适配层单元测试不需要启动服务:

```bash
python -m unittest -v test_mcp_tools.py
```

三系统联调测试会使用临时数据文件和随机本机端口,不会修改现有测试数据:

```bash
python integration_test.py
```

### 连接测试

工具 `mcp_connection_test` 可传空对象 `{}`,也可传:

```json
{
  "probe": "upstream-health-check"
}
```

## 更新与停止

更新代码后重新部署:

```bash
docker compose --env-file deploy.env up -d --build
```

停止服务:

```bash
docker compose --env-file deploy.env down
```

## 常见问题

- 返回 `421 Invalid Host header`:`deploy.env` 中的域名与访问域名不一致。
- MCP 连接出现 `Connection refused`:检查 `MCP_LOCAL_PORT`、容器状态及反向代理端口。
- 工具返回“无法连接订单系统/库存系统”:从 MCP 容器内检查
  `ORDER_API_BASE_URL`、`INVENTORY_API_BASE_URL` 与实际端口。配套库存系统的
  默认地址应为 `http://host.docker.internal:32768`。
- 库存返回 `SKU_NOT_FOUND`:检查订单 SKU 与库存系统的 `001`–`010` 是否对应。
- 库存返回数量不能小于 0:该订单的 `inventoryDelta` 会使库存越界,需要先核对订单方向和当前库存。
- 查看 MCP 服务日志:`docker compose --env-file deploy.env logs mcp-server`。
- HTTPS 相关问题继续查看服务器现有 Nginx、Apache 或 Caddy 日志。

## 安全要求

这个 MCP 现在包含可创建订单和修改库存的写工具,不能把无鉴权入口直接暴露到
公网。正式使用时必须在现有 HTTPS 网关增加访问控制,例如 OAuth、受管 API
网关或固定来源 IP 白名单;业务系统自身也建议启用 Bearer Token。日志中不要
记录 `ORDER_API_TOKEN`、`INVENTORY_API_TOKEN` 或完整的 Authorization 请求头。