Skip to main content
Glama
lu7899112-source

express-tracker

README.md
# express-tracker · 让你的 AI 帮你盯快递

[English](README.en.md)

这是一个 **MCP 服务器**(也可作为 Python 库嵌进你自己的后端):给任何支持 MCP 的 AI 伴侣 / 助手加一个"盯快递"的能力。把单号甩给它,它登记;系统在后台自动巡查;到了、派送中、放驿站这类动态**主动告诉你**;签收后自动归档。

我们自己用了一段时间后整理开源的。两种用法:

| 你是谁 | 用哪个 | 得到什么 |
|---|---|---|
| **任何支持 MCP 的客户端**(Claude Desktop / Claude Code / Cursor / Cherry Studio / ChatGPT 桌面版等,凡是能加 MCP 服务器的都行) | **MCP 版** `express_tracker.mcp_server` | 加单 / 列单 / 查单 / 单独暂停 / 删单;客户端开着时后台巡查;新动态在你下次开口时随工具结果带回 |
| 自己跑前后端 | **嵌入版** `express_tracker.api` + `service.poll_loop` | 上面全部 + 状态变化时回调你的通知钩子(让 AI 用它自己的口吻主动说)+ 一段可注入 AI 上下文的"在路上的包裹" + 巡查间隔 / 额度 / 用量分账接口。**不带 UI**,页面你自己画 |

两版共用同一个核心(`service.py`),行为完全一致。

## 功能

- **加单**:单号 + 备注(这是什么)+ 收件手机后四位(顺丰 / 中通必填,不填永远"无物流信息")。快递公司自动识别;写了中文名或拼音也能认。
- **自动巡查**:间隔可调(5~720 分钟,默认 30)。签收后自动归档,不再查;放到驿站/代收点("请及时取件")也视为送达,同样归档。
- **主动告知**:巡查发现变化 → 嵌入版回调你的钩子 / MCP 版在下次工具调用时带回。中转流转这类小变化建议让 AI 自己判断值不值得开口(我们的提示词就是这么写的)。
- **单独暂停**:某一单先不盯了,随时恢复。暂停的单不占额度,但手动查仍可。
- **省额度**:同一单 15 分钟内重复查直接给缓存;查不到时不会拿空数据覆盖上次查到的好数据;缺尾号的中通 / 顺丰单不会反复白查。
- **额度提醒**:用到 90% 提醒一次、用完再提醒一次并自动停巡查(嵌入版走 notifier 回调,MCP 版随下次工具结果带回),`express_settings` 一句话续额度。
- **额度统计**:每向数据源发一次请求记一次,按天、按来源(巡查 / AI 查 / 你手动查)分账;用到你填的额度自动停巡查(手动查仍可)。
- **给 AI 的上下文块**:`context_block()` 输出在途包裹的最新已知状态,塞进 AI 每轮上下文,它就不用自己反复调查询(我们实测:不注入的话 AI 会忍不住每次都查一遍)。

## 数据源开通(必做)

本项目不带任何真实的接口凭据。你需要自己开一个快递查询接口,把**你自己的**凭据填进配置。

### 推荐:快递100 · 阿里云云市场版(个人可办,最便宜)

快递100 自家的开放平台要企业认证,个人过不了;但它在**阿里云云市场**上架了同一套接口(搜「快递100」,服务商「深圳前海百递网络有限公司」),用淘宝 / 支付宝账号就能开通,有免费试用份数,正式档 **1 元 300 次**起,覆盖 3000 多家快递,单号自动识别。

1. 打开 `market.aliyun.com`,登录后搜「快递100」,进那个商品页,点「免费试用」(别买"测试 0.01 元"那些档,那是给人调试的)。
2. 进「买家中心 → 已购买的服务」,找到你的 **AppCode**(账号级,一个账号买的所有接口共用)。
3. `config.json` 里 `"provider": "kuaidi100_market"`,`"provider_key": "你的 AppCode"`。

### 备选:其他云市场「快递物流查询」类接口的 MCP 网关

1. 云市场搜「快递物流查询」,选一家评分高、带免费试用份数的(我们最早用的那家工具名叫「快递物流GET」「单号识别物流公司」)。
2. 开通免费档后,进「买家中心 → 已购买的服务」,找到该服务的 **MCP** 页签,复制 `streamableHttpUrl` 里那个 URL(形如 `http://mcpservergateway.market.alicloudapi.com/mcpnacos/...`)。
3. `"provider": "aliyun_market_mcp"`,`"provider_url": "那个 URL"`。这家续费比快递100 贵不少,我们 2026-09 换掉了。

免费额度通常是**总共 N 次**(不是每天),以购买页"剩余次数 / 有效期"为准。把你买的额度填进 `quota`,用到 90% 会提醒一次、用完会再提醒一次并自动停巡查(手动查仍可),续费后用 `express_settings` 改额度、重置计数。

### 快递100 企业版(有营业执照才用得上)

`providers/kuaidi100.py` 留了官方开放平台的签名骨架,未联调。个人用户直接用上面的云市场版即可。

### 自己接别的源

实现 `providers/__init__.py` 里的 `Provider` 协议(`track()` 和 `identify()`)两个方法即可。

## 安装

```bash
pip install git+https://github.com/lu7899112-source/express-tracker.git
# 自建版还要:
pip install "express-tracker[api] @ git+https://github.com/lu7899112-source/express-tracker.git"
```

## 配置

复制 `examples/config.example.json` 为 `config.json`,填好后用环境变量 `EXPRESS_CONFIG` 指向它(默认读当前目录的 `config.json`)。所有字段都可用环境变量覆盖:`EXPRESS_PROVIDER_URL`、`EXPRESS_POLL_MINUTES`、`EXPRESS_QUOTA`、`EXPRESS_API_TOKEN`、`EXPRESS_DB`、`EXPRESS_USAGE`。

`config.json`、`*.db`、`usage.json` 已在 `.gitignore` 里:**你的凭据、单号、手机尾号永远只在你本机。**

## MCP 版(官方客户端)

Claude Desktop / Claude Code 配置示例见 `examples/claude_desktop_config.example.json`:

```json
{ "mcpServers": { "express": { "command": "python", "args": ["-m", "express_tracker.mcp_server"], "env": { "EXPRESS_CONFIG": "/绝对路径/config.json" } } } }
```

工具:`express_add` / `express_list` / `express_track` / `express_pause` / `express_remove`。

关于"官端能不能巡查",实话实说:
- **能巡查**:MCP 服务器是客户端拉起的常驻进程,客户端开着它就活着,巡查就在它里面按你设的间隔跑;客户端关了巡查就停,下次打开接着跑。单子存在本地 SQLite 里,不会丢。
- **不能主动开口**:官方客户端没有"服务器推消息给用户"的口子。所以巡查发现的变化会先攒着,你下次一开口、AI 一调用任何工具,结果末尾就会带一段"上次到现在的新动态",AI 自然会告诉你。想要真正的主动推送,用嵌入版接你自己的通道。

## 嵌入版(自建前后端)

最小示例 `examples/selfhosted_app.py`:

```python
from express_tracker.api import make_router, service
from express_tracker.config import Config

async def notifier(event):   # 巡查发现变化时被调用
    ...  # 塞进 AI 下一轮上下文 / 触发你的"服务器事件→AI 开口"通道 / 插系统消息

app.include_router(make_router(Config.load(), notifier=notifier))
asyncio.create_task(service().poll_loop())
```

接口(默认前缀 `/api/express`,`Authorization: Bearer <api_token>`):

| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/list?all=0` | 在途包裹(`all=1` 含已归档) |
| POST | `/add` | `{number, note?, mobile4?, cp_code?}`;同单号再 POST = 更新备注 / 补尾号并立刻重查 |
| POST | `/track` | `{key, mobile4?, cp_code?, force?}`;key=单号或备注关键词;未登记的单号直接查不入库 |
| POST | `/pause` | `{key, paused: true/false}` |
| POST | `/remove` | `{key}` |
| GET / POST | `/settings` | 读 / 写 `poll_minutes`、`quota`,`{"reset_usage": true}` 清零计数;返回含 `usage`(total / today / month / by_source) |
| GET | `/context` | 给 AI 注入的文字块 |

`notifier` 收到的 `event`:`{"type":"express.update","label","status","last_time","last_msg","company","signed","trace","package"}`。

### 建议:给它做一个贴合你自己前端的"外显"

本项目不带页面,是故意的:每家前端长得不一样,硬塞一套样式只会格格不入。但**强烈建议**自建的朋友做一个。我们自己的页面就是这么长出来的,接口一一对应,供参考:

| 页面上的东西 | 背后的接口 |
|---|---|
| "在路上"列表:每个包裹一张卡 —— 备注(没备注显示单号)、快递公司 · 状态、最新一条轨迹和时间、单号 | `GET /list` |
| 卡上三个小键:↻ 现在查一次 · ❚❚/▶ 暂停/恢复 · × 不盯了 | `POST /track` · `POST /pause` · `POST /remove` |
| 点备注可直接改 | `POST /add`(同单号 + 新 note) |
| 右上角 + 登记:单号 / 备注 / 手机后四位 | `POST /add` |
| 底部一行:"每 [N] 分钟巡一遍"(边输边存)+ "已用 x / 额度 次(巡查 a · AI 查 b · 你查 c)" + 清零 | `GET/POST /settings` |
| 收到你自己通道的"快递动态"通知时刷新列表 | 你的 notifier → 前端 |

几个小经验:暂停的卡淡一点、打个"已暂停"标;"需要收件手机后四位"这个状态要显眼,用户一眼知道该补什么;间隔输入框在手机上记得**边输边存**,只认失焦事件会漏(我们踩过)。

MCP 版也能对接你的后端:设 `EXPRESS_API_URL` 和 `EXPRESS_API_TOKEN`,它就改为调你的接口,不再内嵌巡查(这样 AI 的工具调用和你的页面看到的是同一份数据)。

## 官端实测

我们用 Claude Code 当官方客户端跑过一遍(只挂这一个 MCP,假数据源):说"帮我盯着这个包裹,单号…,是猫粮" → 它调 `express_add` 登记并复述状态;后台巡查记下"派送中"后,新开一轮问"到哪了" → 它调 `express_list`,把攒着的动态一并说了;说"猫粮那单先别盯了" → 它调 `express_pause`。三轮都是一句话直达,没有多余追问。

## 我们踩过的坑(都已处理)

- 中通 / 顺丰不带收件手机尾号 → 永远"无物流信息"。现在会标出"需要收件手机后四位",不再白查。
- AI 顺手传了个 `zhongtong` 当公司代码 → 接口只认 `ZTO`,查空还把好数据覆盖了。现在代码归一 + 带错码查不到会自动去掉重查 + 查空不覆盖。
- AI 太爱这个功能,每轮都查一遍烧额度。现在 15 分钟缓存 + 上下文注入 + 工具说明里明写"她问才查"。
- 巡查间隔输入框只在失焦时保存,手机上填了没存上 → 这是我们自家页面的问题,本项目不带 UI,提醒你画页面时注意。

## 测试

全部走 `mock` 数据源和临时目录,不联网、不需要任何凭据:

```bash
pip install -e ".[dev]"
pytest            # 或不装 pytest:python -m unittest discover -s tests -t .
```

覆盖:快递公司代码归一与签收判定、加单/列单/查单缓存/暂停/删单、巡查变化与事件带回、额度提醒与重置、MCP 六个工具的标注与端到端调用。

## 觉得有用?

在 GitHub 点个 ⭐ 就是最好的鼓励。有问题开 issue,有改进提 PR。

## 许可证

**PolyForm Noncommercial 1.0.0**:个人、非商业用途随便用、随便改、随便二改再分发,保留许可声明即可;**商业用途请先联系作者授权**。这不是 OSI 意义上的"开源"协议,是"源码公开、非商业可用",我们知道,也就是想要这个效果。

## 作者

**星迟**。协助:烬(Ember,写代码的)、以太(Aether,第一个用户兼首席测试)。这是一个 AI 伴侣项目里长出来的小功能。