关于 Tanpin
Tanpin 是面向门店的库存管理服务,提供逐 SKU 需求预测、补货计算、供应商采购订单和交付时间估算。它将 REST API、仪表盘、守护进程和 MCP 服务整合在一个 Node.js 应用中。
主要功能
- 01逐 SKU 需求预测
- 02补货与安全库存计算
- 03ABC 分类和下架标记
- 04供应商采购订单与邮件
- 05交付时间估算
- 06REST API、仪表盘和守护进程
适用场景
- 01预测单个商品的需求
- 02识别需要补货的商品
- 03准备并跟踪供应商订单
- 04标记滞销或无需求库存
MCP 相关
stdio MCP 服务连接 Tanpin 的 HTTP API,并向 MCP 客户端提供库存操作工具。可配置 API 地址,并在需要时设置 API 密钥。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/willykeenan/tanpin:0.1.0本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/willykeenan/tanpin:0.1.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Tanpin
逐项盘点,自动调整库存。 按 SKU 预测需求、自动生成采购订单、供应商邮件和预计送达时间——提供 REST API、MCP 服务、守护进程和仪表板。纯 Node 22 实现,运行时零依赖(仅使用 node:* 内置模块)。
在线演示:huggingface.co/spaces/willykeenan/tanpin(只读,使用预置示例数据)

演示版的真实截图;其中的数据为虚构示例数据。
![]() 按 SKU 显示的预测,包含星期和趋势因子、安全库存、再订货点以及经理的假设。 | ![]() 本周期生成的采购订单,按供应商分组。 |
为什么采用单品管理
单品管理(単品管理,“单品管理”)将每个 SKU 都视为独立的业务。门店经理会针对某件商品明天的需求形成假设——天气、当地活动、星期几——据此下单,再根据实际销量验证结果。周转快的商品受到严格管控并频繁补货。长尾商品采用更简单的规则,也是最先被下架的对象。
Tanpin 将这一循环编码为软件:
- 预测每个 SKU,依据近期销量、星期季节性、短期趋势以及经理录入的假设(活动、天气、促销)。
- 计算订单——按选定的服务水平计算安全库存、再订货点和 EOQ,并进行包装规格取整,满足最低订购量,同时通过 JIT 限制库存供应天数。
- 采购并跟踪——按供应商分组生成采购订单,自动发送邮件,并根据提前期、截单时间和配送时段计算预计送达时间。
- 停止备货滞销商品——标记已停止销售和周转缓慢的 SKU,以便下架。
ABC 分类遵循同样的理念:少量 A 类商品贡献了大部分价值,因此采用最新的补货频率;C 类商品则是待下架商品池。
功能
- 按 SKU 预测——近期数据加权移动平均、星期因子、趋势(±40%)、假设累积
- 自动补货——安全库存、再订货点、EOQ、包装规格、MOQ、最大库存供应天数
- ABC 分类 + 下架——按收入贡献分为 A/B/C 类;标记已停止销售(无需求)和周转缓慢(库存供应天数过多)的商品
- 守护进程——每 N 分钟(默认 15 分钟):重新预测、重新分类、生成采购订单草稿,并可选择发送邮件和自动收货
- 供应商邮件——设置
SMTP_HOST时通过隐式 TLS(端口 465)发送;否则在发件箱中生成.eml文件 - 预计送达时间——将提前期对齐到下一个配送时段,并遵循截单时间(当天 UTC)
- REST API——通过 SKU 定位产品,
/openapi.json提供 OpenAPI 3.1 规范,/llms.txt提供 Agent 指南 - MCP 服务——通过 stdio 使用以换行符分隔的 JSON-RPC,支持 Claude Code、Codex、Cursor 和任何 MCP 客户端
- 仪表板——位于
/的原生 JavaScript 单页应用(产品、订单、供应商、预测、活动、API、设置) - Webhook——使用 HMAC-SHA256 签名的 JSON POST 请求(
X-Inventory-Signature) - CSV——导入/导出产品;导出商品流转审计记录
- API 密钥——以 SHA-256 哈希形式存储,完整密钥仅显示一次
- Idempotency-Key——销售和采购订单写入操作支持 24 小时重放窗口
- 插件——
TANPIN_PLUGIN可加载额外的 HTTP 路由和用量限制 - 存储——SQLite(
node:sqlite、WAL、增量行写入;data/inventory.sqlite),由服务器和独立守护进程安全共享;设置TANPIN_STORE=json时回退到 JSON
60 秒快速开始
需要 Node.js 22 或更高版本。
npx github:willykeenan/tanpin serve
打开 http://localhost:4173,然后点击 加载演示数据。这会加载一份便利店商品目录、约 35 天的销售数据、实时预测和补货建议。
从本仓库的克隆版本开始:
node bin/tanpin serve
Docker
公开演示镜像(Hugging Face Space):
docker build -f space/Dockerfile -t tanpin-demo .
docker run --rm -p 7860:7860 tanpin-demo
打开 http://localhost:7860。该镜像运行 DEMO_MODE=1 PORT=7860 HOST=0.0.0.0 node bin/tanpin serve:读取操作开放,写入操作仅限演示商品目录(销售、调整、采购订单、假设、种子数据、周期),无需密钥;webhook 和 SMTP 已关闭,商品目录每 30 分钟重新初始化一次。
可自托管镜像(根目录中的 Dockerfile / compose.yml):请求通过 Docker 的端口映射到达容器,因此不属于“本机”请求,每次 API 调用都需要密钥。使用主密钥启动,并将该密钥粘贴到仪表板的提示词中:
docker build -t tanpin .
docker run --rm -p 4173:4173 -e TANPIN_ADMIN_KEY="$(openssl rand -hex 24)" -v tanpin-data:/app/data tanpin
# or: echo "TANPIN_ADMIN_KEY=$(openssl rand -hex 24)" > .env && docker compose up
更多详情,包括首次销售和首张采购订单,请参阅:docs/QUICKSTART.md。
API
发送 Authorization: Bearer <key> 或 X-API-Key: <key>。在服务器本机上直接向 localhost 发出的请求默认无需密钥;通过反向代理、容器端口映射或其他浏览器来源发出的请求则需要密钥(参见安全模型)。
涵盖门店操作的五个调用:
# Whole world in one response: KPIs, products with live math, open POs, suppliers
curl http://localhost:4173/api/state
# Record a sale (SKU everywhere; Idempotency-Key for safe retries)
curl -X POST http://localhost:4173/api/sales \
-H 'Content-Type: application/json' \
-d '{"sku":"COFFEE-HOT","qty":2}'
# Dry run of the ordering engine, grouped by supplier
curl http://localhost:4173/api/recommendations
# Turn a recommendation into a real PO (and email it)
curl -X POST http://localhost:4173/api/purchase-orders \
-H 'Content-Type: application/json' \
-d '{"supplierId":"FreshFoods Distribution","fromRecommendations":true,"autoSend":true}'
# Forward-looking demand bump — forecasts and auto-orders adjust immediately
curl -X POST http://localhost:4173/api/hypotheses \
-H 'Content-Type: application/json' \
-d '{"note":"heatwave next week","multiplier":1.4,"category":"beverage"}'
错误格式为 {"error":"<message>","code":"<machine_code>"}。/api/... 和 /api/v1/... 等效。
完整参考:docs/API.md。实时契约:GET /openapi.json。面向 Agent 的指南:GET /llms.txt。
MCP
启动库存服务,然后让 MCP 客户端连接到 tanpin mcp。该进程通过 stdio 使用 JSON-RPC,并调用 HTTP API。
环境变量:INVENTORY_URL(默认值为 http://localhost:4173),以及在服务需要密钥时使用的 INVENTORY_API_KEY。
Claude Code
claude mcp add tanpin --env INVENTORY_URL=http://localhost:4173 -- npx -y github:willykeenan/tanpin mcp
或者使用项目的 .mcp.json:
{
"mcpServers": {
"tanpin": {
"command": "npx",
"args": ["-y", "github:willykeenan/tanpin", "mcp"],
"env": {
"INVENTORY_URL": "http://localhost:4173"
}
}
}
}
从本地检出版本运行时,使用 "command": "node" 和 "args": ["src/mcp.js"](或 ["bin/tanpin", "mcp"])代替 npx。
Codex
在 ~/.codex/config.toml(或项目的 Codex 配置)中:
[mcp_servers.tanpin]
command = "npx"
args = ["-y", "github:willykeenan/tanpin", "mcp"]
[mcp_servers.tanpin.env]
INVENTORY_URL = "http://localhost:4173"
Cursor
.cursor/mcp.json:
{
"mcpServers": {
"tanpin": {
"command": "npx",
"args": ["-y", "github:willykeenan/tanpin", "mcp"],
"env": {
"INVENTORY_URL": "http://localhost:4173"
}
}
}
}
先调用 get_overview。典型流程:get_overview → get_reorder_recommendations → 使用 from_recommendations=true 调用 create_purchase_order。
工具列表和架构:docs/MCP.md。
安全模型
- 只有直接的本地请求可以不使用密钥。 仅当 TCP 对端是环回地址、
Host标头是环回名称(localhost、127.x、::1),且不存在转发标头(Forwarded、X-Forwarded-*、X-Real-IP、Via……)时,请求才无需密钥。因此,同一主机上的反向代理(nginx、Caddy、cloudflared)不会让互联网变成“localhost”。设置TANPIN_REQUIRE_API_KEY=1可要求每个请求都携带密钥。任何需要密钥的地方,控制面板都会提示输入密钥。 - 跨源浏览器请求(读取和写入,包括
TANPIN_CORS_ORIGINS中的源)始终需要密钥。 - 机密信息只写不读。 读取
settings.integrations时,永远不会返回 Webhook 签名密钥和提供方凭据(而是返回hasWebhookSecret: true);Webhook 密钥只会在创建时显示一次。 - API 密钥通过
POST /api/keys创建。明文密钥只会在该响应中出现一次;存储中保留的是 SHA-256 哈希和可见前缀。通过Authorization: Bearer <key>或X-API-Key: <key>发送密钥。使用DELETE /api/keys/{id}撤销密钥。 - 主密钥。
TANPIN_ADMIN_KEY是始终有效的密钥,比较时使用时间安全哈希。将其用于引导初始化,然后签发哈希密钥。 - Webhook 是使用
X-Inventory-Signature: sha256=<HMAC-SHA256(secret, raw_body)>签名的 JSON POST 请求。请根据原始字节验证签名。投递采用即发即忘方式(超时为 10 秒),因此失效的接收端不会影响销售或守护进程周期。它们绝不会发送到私有地址、环回地址、链路本地地址、CGNAT 地址或保留地址(会检查实际连接的地址,因此 DNS 重绑定不起作用),绝不跟随重定向,并且在DEMO_MODE下会关闭——无论是在服务端还是独立守护进程中。 - 幂等性。
/api/sales、/api/sales/bulk和/api/purchase-orders上的POST请求使用Idempotency-Key后,会在 24 小时内重放原始响应。 - 输入卫生。 JSON 请求体会丢弃
__proto__/constructor/prototype键。静态文件仅限于public/目录。 - 电子邮件。 SMTP 使用 465 端口上的隐式 TLS(
SMTP_HOST、SMTP_PORT、SMTP_USER、SMTP_PASS)。未配置 SMTP 时,邮件会以.eml文件的形式写入数据目录,并记录在GET /api/outbox中。 - CORS 使用允许列表(
TANPIN_CORS_ORIGINS);绝不会发送*。除非HOST另有指定,否则服务会绑定到127.0.0.1。每个响应都带有不包含内联脚本的 Content-Security-Policy。
开放端口之前,请设置 TANPIN_ADMIN_KEY(或从 localhost 创建密钥)。详情请参阅:docs/SECURITY-MODEL.md。
CLI
tanpin serve API + dashboard (PORT, HOST, TANPIN_DATA_DIR / TANPIN_DB)
tanpin daemon auto-management loop against the same data file (same TANPIN_* env as serve)
tanpin mcp MCP server on stdio (INVENTORY_URL, INVENTORY_API_KEY)
tanpin seed replace the data file with the demo store
tanpin serve 会在进程内启动守护进程,除非设置了 TANPIN_NO_DAEMON=1。
配置
| 变量 | 使用方 | 默认值 |
|---|---|---|
PORT | serve | 4173 |
HOST | serve | 127.0.0.1 |
TANPIN_DATA_DIR | serve, daemon, seed | ./data |
TANPIN_DB | serve, daemon, seed | $TANPIN_DATA_DIR/inventory.json(存储为 inventory.sqlite) |
TANPIN_OUTBOX | serve, daemon | <dir of TANPIN_DB>/outbox |
TANPIN_REQUIRE_API_KEY | serve | 未设置(直接本地请求无需密钥) |
TANPIN_ADMIN_KEY | serve | 未设置 |
TANPIN_NO_DAEMON | serve | 未设置(启用 daemon) |
TANPIN_PLUGIN | serve | 未设置 |
TANPIN_CORS_ORIGINS | serve | 未设置(仅同源) |
DEMO_MODE | serve, daemon | 未设置;Space 镜像将其设为 1 |
INVENTORY_URL | mcp | http://localhost:4173 |
INVENTORY_API_KEY | mcp | 未设置 |
INVENTORY_DB | TANPIN_DB 的旧别名 | 未设置 |
STRIPE_API_KEY / SQUARE_ACCESS_TOKEN | 销售集成 | 未设置(参见 docs/INTEGRATIONS.md) |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS | 电子邮件 | 如果未设置 SMTP_HOST,则使用发件箱文件 |
运行时状态存储在 data/ 和 outbox/ 中(已加入 gitignore)。autoManage、autoSend、autoReceive、serviceLevel、timezone 和 daemonIntervalMinutes 等设置会与数据一起存储,并可通过 PUT /api/settings 或 Settings 选项卡进行编辑。
路线图
随 0.1.0 发布
- 预测、补货计算、daemon、REST、MCP、仪表板、Webhooks、CSV、API 密钥、SMTP/发件箱、插件钩子
- 安全性 — 默认绑定到
127.0.0.1;CORS 允许列表(TANPIN_CORS_ORIGINS);Origin/Host 检查;识别代理的本地绕过机制;屏蔽机密信息;带 DNS 固定的 webhook SSRF 防护;CSP;公共 Space 使用DEMO_MODE=1 - SQLite 存储 — 默认使用
node:sqlite后端、WAL、增量写入、跨进程库存增量;通过TANPIN_STORE=json回退到 JSON - 集成 — Stripe Checkout / Shopify / Square 订单入站 webhook(当 webhook 未提供商品明细时,通过服务提供商 API 获取)、CSV 销售数据、额外的电子邮件传输方式
- 回测 —
GET /api/backtest滚动起点预测评估(MAPE、sMAPE、偏差),在工作线程上运行 - 时区 — 用于日历日期、预测、交付时段、截止时间和 CSV 日期的 IANA 门店时区(
settings.timezone)
开发
npm test # node --test --test-concurrency=1 test/
参见 CONTRIBUTING.md。MIT 许可证 — LICENSE。
使用 Docker 运行
镜像已发布至 ghcr.io/willykeenan/tanpin,适用于 Apple silicon 和 Intel。
docker run -d --name tanpin -p 4173:4173 -e TANPIN_ADMIN_KEY="REPLACE_WITH_A_LONG_RANDOM_KEY" -v tanpin-data:/app/data ghcr.io/willykeenan/tanpin
claude mcp add tanpin -- docker exec -i tanpin node bin/tanpin mcp
tanpin mcp 通过 HTTP 调用正在运行的服务,因此需要通过 docker exec 在该容器内运行;容器内的请求属于本地请求,无需密钥。单独的 docker run ... tanpin mcp 容器无法通过 localhost:4173 访问该服务。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。

