Skip to main content
Glama
shop-agent

shop-agent

by shengtong255
README.md
# shop-agent · 把电商业务系统接给 AI 的 MCP 服务

让 AI 用自然语言查订单、算 GMV/ROI、改价格;改价必须过安全护栏,所有写操作留审计。

一个 30 秒能跑起来、可以现场演示、也能讲 20 分钟的最小 MCP(Model Context Protocol)项目。

![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white)
![MCP](https://img.shields.io/badge/MCP-1.x%20%2F%202.x-6E56CF)
![License](https://img.shields.io/badge/License-MIT-green)

<img src="docs/demo.png" width="880" alt="改价被护栏拦截,确认后执行并写审计">

---

## 它解决什么问题

跨境电商团队每天在做这种事:

- "帮我查一下 SO-20261001-0007 这单到哪了"
- "上周 Meta 的 ROI 是多少,哪个国家最差"
- "把 SKU-1001 降到 19.9 清库存"

以前这些要在后台点七八个页面,现在交给 AI:模型通过 MCP 调用本项目的工具,读业务库、算指标、执行改价。

**但它不是"让 AI 随便改数据库"。** 改价超过 30% 会被拦截,要求模型带 `confirm=true` 和原因二次调用;每次写入都记 `audit_log`,谁、什么时候、把什么改成了什么,可追溯。

## 架构

```
┌──────────────────────────┐
│ Claude Desktop / Cursor  │
│ (或任何 MCP 客户端)      │
└────────────┬─────────────┘
             │ MCP over stdio(JSON-RPC,不开放端口)
             ▼
┌──────────────────────────┐
│ server.py  (FastMCP)     │  注册 5 个 tool,自动生成 JSON Schema
└────────────┬─────────────┘
             ▼
┌──────────────────────────┐
│ app/tools.py             │  参数校验 / 统一错误码 / 改价护栏
└────────────┬─────────────┘
             ▼
┌──────────────────────────┐
│ app/repo.py              │  查询聚合 / 写操作 / 审计日志
└────────────┬─────────────┘
             ▼
┌──────────────────────────┐
│ SQLAlchemy Core          │
│ SQLite(演示)→ MySQL/PG  │  切换只改 DATABASE_URL
└──────────────────────────┘
```

## 快速开始

环境:Python 3.10+,Windows / macOS / Linux 均可。兼容 `mcp` 1.x(FastMCP)与 2.x(MCPServer)。

```bash
git clone <your-repo> && cd mcp-shop-agent
python -m venv .venv

# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt
python scripts/seed.py          # 生成 30 天演示数据
python scripts/demo_cli.py      # 不装任何客户端,直接看 7 步效果
```

`demo_cli.py` 会依次演示:查最近订单 → 查订单详情 → 按渠道看 GMV/ROI → 查广告花费 → **改价被护栏拦截** → 带确认重试成功 → 审计日志。完整输出样例见 `docs/demo-output.txt`。

> ⚠️ **不要手动运行 `python server.py`。**
> 它是 stdio 服务,启动后会阻塞在标准输入上等客户端发消息——不打印任何东西,也不会自己退出,看起来像卡死,其实是在待命。正确用法是让 Claude Desktop 之类的客户端拉起它;想手动验证效果请跑 `demo_cli.py`;误开了就按 `Ctrl+C` 退出。

## 目录结构

```
mcp-shop-agent/
├── server.py            # MCP 入口:注册工具、启动 stdio 服务
├── app/
│   ├── config.py        # 环境变量与 .env 读取
│   ├── models.py        # 表结构(SQLAlchemy Core,跨数据库)
│   ├── db.py            # 连接池、事务边界、建表
│   ├── repo.py          # 查询聚合、改价、审计
│   ├── tools.py         # 参数校验、错误码、改价护栏
│   └── seed.py          # 固定随机种子的演示数据
├── scripts/
│   ├── seed.py          # 生成演示数据
│   ├── demo_cli.py      # 不装客户端也能跑的 7 步演示
│   └── _console.py      # Windows 控制台 UTF-8 兼容
├── tests/               # 19 条用例
└── docs/
    ├── demo.png         # README 首图
    └── demo-output.txt  # 演示输出样例
```

## 接入 Claude Desktop

编辑 `%APPDATA%\Claude\claude_desktop_config.json`(macOS 是 `~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "shop-agent": {
      "command": "D:\\path\\to\\mcp-shop-agent\\.venv\\Scripts\\python.exe",
      "args": ["D:\\path\\to\\mcp-shop-agent\\server.py"],
      "env": {
        "DATABASE_URL": "sqlite:///D:/path/to/mcp-shop-agent/data/shop.db",
        "AGENT_ACTOR": "claude-desktop"
      }
    }
  }
}
```

保存后完全退出并重启 Claude Desktop,对话框右下角出现工具图标即可。试着输入:

> 帮我看看最近三笔订单,再算一下本周各渠道的 ROI,哪个最差?

> 把 SKU-1001 降到 19.9 清库存

第二句会先被拦截,模型会把需要确认的信息转述给你;你确认后它再带 `confirm=true` 调用。

## 工具清单

| 工具 | 作用 | 关键参数 | 类型 |
|---|---|---|---|
| `query_order` | 按订单号查详情(商品、金额、国家、渠道、状态) | `order_no` | 只读 |
| `list_recent_orders` | 最近订单列表,用于不确定订单号时先探数据 | `limit` | 只读 |
| `query_ad_spend` | 区间广告花费与效果:spend / revenue / ROI / CPA / CTR / CVR | `start_date`、`end_date`、`channel` | 只读 |
| `gmv_roi_summary` | 按渠道 / 国家 / 日期聚合 GMV、订单数、客单价、ROI | `group_by` | 只读 |
| `update_price` | 改价,带幅度护栏与审计 | `sku`、`new_price`、`confirm`、`reason` | **写入** |

## 三个设计决策

### 1. 写操作要"二次确认",不能一句话改全店价格

`update_price` 先算改价幅度。超过 `MAX_PRICE_CHANGE_RATIO`(默认 30%)时不执行,返回:

```json
{
  "ok": false,
  "error": {
    "code": "NEED_CONFIRM",
    "message": "改价幅度 -96.7% 超过 30% 阈值,已拦截",
    "hint": "如确认执行,请带 confirm=true 和 reason 重新调用:update_price(sku='SKU-1001', new_price=1.0, confirm=True, reason='...')。当前价格 30.0,目标价格 1.0。"
  }
}
```

`hint` 是写给模型看的——它知道下一步该传什么,不需要人来翻译报错。**这是 MCP 工具设计和普通 REST API 最大的区别:你的调用方是一个会自己纠错的模型。**

### 2. 所有写操作落审计

`audit_log` 记录 `ts / actor / action / target / before / after / reason`。出了问题能回答三个问题:谁改的、改成什么、为什么。

### 3. 不抛异常,返回结构化错误

工具永远返回 `{"ok": true|false, ...}`。理由是:模型拿到 traceback 只会道歉,拿到 `code + hint` 才会修正参数重试。

已定义的错误码:

| code | 场景 |
|---|---|
| `INVALID_ARGUMENT` | 参数为空、价格非数字、价格 ≤ 0 |
| `INVALID_DATE` / `INVALID_RANGE` / `RANGE_TOO_LARGE` | 日期格式错、起止颠倒、区间超 180 天 |
| `ORDER_NOT_FOUND` / `PRODUCT_NOT_FOUND` | 单号或 SKU 不存在 |
| `PRODUCT_INACTIVE` | 商品已下架,不允许改价 |
| `NEED_CONFIRM` / `MISSING_REASON` | 改价幅度过大,需二次确认并说明原因 |
| `INVALID_GROUP_BY` | 聚合维度不在 channel / country / day 内 |

## 测试

```bash
pytest
```

当前 19 条用例全部通过(约 3 秒)。覆盖:订单查询与大小写、日期校验、区间校验、渠道过滤、三种聚合维度、改价四条路径(小额通过 / 大额拦截 / 无原因拦截 / 确认后通过)、MCP 工具注册与 Schema 生成。

## 换成 MySQL / PostgreSQL

```bash
# MySQL
pip install pymysql
set DATABASE_URL=mysql+pymysql://root:pass@127.0.0.1:3306/shop?charset=utf8mb4

# PostgreSQL
pip install "psycopg[binary]"
set DATABASE_URL=postgresql+psycopg://postgres:pass@127.0.0.1:5432/shop
```

表结构由 SQLAlchemy Core 定义,三种数据库共用一套 `create_all`,不需要改代码。

## 演示脚本(3 分钟版)

1. **30 秒**:讲问题——运营查一单要点五个页面,AI 能不能直接查?
2. **40 秒**:跑 `demo_cli.py` 第 1-4 步,展示自然语言背后的工具调用与汇总指标。
3. **60 秒**:跑第 5 步,改价被拦。讲"为什么不能让模型直接写库"——这是整个项目最值钱的部分。
4. **30 秒**:跑第 6-7 步,确认后执行 + 审计日志。
5. **20 秒**:打开 `server.py`,说明工具注册只用了 5 行,schema 由类型注解和 docstring 自动生成。

## Roadmap

- [ ] 接真实广告平台:Meta Marketing API / Google Ads API,替换 seed 数据
- [ ] 增加 MCP Resource,把表结构和字段含义暴露给模型(减少瞎猜字段)
- [ ] Redis 缓存热点查询 + 按调用方限流
- [ ] 写操作接入审批流(企业微信/飞书卡片确认)
- [ ] 多店铺多币种

## License

MIT