Metadata-Version: 2.4
Name: erp-mcp
Version: 0.1.0
Summary: ERP MCP Server: expose Kingdee Cloud Galaxy / YonSuite business queries to AI agents.
Author: OTD
License: Proprietary
Project-URL: Homepage, https://github.com/stevendingliujian-collab/erp-mcp
Project-URL: Repository, https://github.com/stevendingliujian-collab/erp-mcp
Project-URL: Issues, https://github.com/stevendingliujian-collab/erp-mcp/issues
Keywords: mcp,erp,kingdee,yonsuite,ai-agent,model-context-protocol
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp[cli]>=1.2.0
Requires-Dist: pydantic>=2.5
Requires-Dist: pydantic-settings>=2.1
Requires-Dist: requests>=2.31
Requires-Dist: pyyaml>=6.0
Requires-Dist: packaging>=23
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: responses>=0.25; extra == "dev"

# ERP MCP Server

面向 AI Agent 的 ERP 业务查询 MCP Server。**V1.3 当前提供 15 个工具**（14 个只读查询 + 1 个带二次确认门的写操作），覆盖销售/采购/库存/客户/应收应付/生产/经营看板/进度视图/审批/元数据探索。底层通过适配器层对接金蝶云星空与用友 YonSuite/YonBIP，工具接口与 ERP 系统解耦。

最新发布说明：[RELEASE_v1.3.md](RELEASE_v1.3.md)

## 安装

```bash
pip install -e ".[dev]"
```

金蝶云星空使用官网 Python SDK。默认从 `vendor/kingdee_sdk/kingdee.cdp.webapi.sdk-*.whl`
加载；如果放在其他目录，可通过 `KINGDEE_SDK_PATH` 指向该 whl 文件。

## 配置

复制 `.env.example` 为 `.env` 并填入对应 ERP 的连接信息。`ERP_TYPE` 取值：

- `kingdee`：金蝶云星空（默认）
- `yonsuite`：用友 YonSuite / YonBIP

版本管理相关（可选）：

- `ERP_VERSION_PIN`：锁定版本号，设置后 `erp_self_update` 拒绝升级
- `ERP_UPDATE_INSTALLER`：`uv` 或 `pip`，显式指定升级安装方式（留空=自动探测）

## 启动

```bash
# stdio 模式
python -m erp_mcp.server

# 查看版本
erp-mcp --version
```

## 调试

```bash
npx @modelcontextprotocol/inspector python -m erp_mcp.server
```

## 测试

```bash
pytest -q
```

金蝶连接探测：

```bash
python scripts/test_kingdee_connection.py
```

## 接入 Claude Desktop

金蝶：

```json
{
  "mcpServers": {
    "erp": {
      "command": "python",
      "args": ["-m", "erp_mcp.server"],
      "env": {
        "ERP_TYPE": "kingdee",
        "KINGDEE_HOST_URL": "https://yourcompany.ik3cloud.com/k3cloud/",
        "KINGDEE_ACCT_ID": "...",
        "KINGDEE_APP_ID": "...",
        "KINGDEE_APP_SECRET": "...",
        "KINGDEE_USER_NAME": "...",
        "KINGDEE_LCID": "2052"
      }
    }
  }
}
```

YonSuite：

```json
{
  "mcpServers": {
    "erp": {
      "command": "python",
      "args": ["-m", "erp_mcp.server"],
      "env": {
        "ERP_TYPE": "yonsuite",
        "YONSUITE_BASE_URL": "https://yapi.yonyoucloud.com",
        "YONSUITE_APP_KEY": "...",
        "YONSUITE_APP_SECRET": "...",
        "YONSUITE_TENANT_ID": "..."
      }
    }
  }
}
```

通过 uvx 直接运行 PyPI 上的发布版（无需本地 clone）：

```json
{
  "mcpServers": {
    "erp": {
      "command": "uvx",
      "args": ["erp-mcp"],
      "env": {
        "ERP_TYPE": "kingdee",
        "KINGDEE_HOST_URL": "https://yourcompany.ik3cloud.com/k3cloud/",
        "KINGDEE_ACCT_ID": "...",
        "KINGDEE_APP_ID": "...",
        "KINGDEE_APP_SECRET": "...",
        "KINGDEE_USER_NAME": "..."
      }
    }
  }
}
```

## 工具一览（V1.3，15 个）

### 基础查询（8 个，只读）

| 工具名 | 业务场景 |
|---|---|
| `erp_query_sales_orders` | 销售订单查询 |
| `erp_query_purchase_orders` | 采购订单查询 |
| `erp_query_delivery` | 发货 / 销售出库查询 |
| `erp_query_inventory` | 即时库存查询（含低库存退化判断） |
| `erp_query_customers` | 客户档案查询 |
| `erp_query_receivables` | 应收账款查询 |
| `erp_query_payables` | 应付账款查询 |
| `erp_query_production_orders` | 生产工单查询 |

### 聚合 / 进度视图（3 个，只读）

| 工具名 | 业务场景 |
|---|---|
| `erp_business_dashboard` | 经营看板：今日发货 + 应收/应付概览 + 库存预警 + 在产工单（一次调用 5 板块） |
| `erp_query_purchase_order_progress` | 采购订单进度（到货/入库 vs 订单数量） |
| `erp_query_sales_order_progress` | 销售订单进度（发货/开票 vs 订单数量） |

### 审批工作流（2 个）

| 工具名 | 业务场景 |
|---|---|
| `erp_query_pending_approvals` | 待审批单据查询（只读） |
| `erp_approve_document` | **审批通过/驳回（写操作，强制 `confirm=true` 二次确认门）** |

### 元数据探索（2 个，只读）

| 工具名 | 业务场景 |
|---|---|
| `erp_list_forms` | 列出所有可查询的业务表单（按关键字过滤） |
| `erp_describe_form` | 查看单个表单的字段映射、业务规则、版本警示。**支持 `live=True` 实时拉取 ERP 真实字段并与本地 catalog 做 diff**（金蝶 only） |

> 元数据探索工具（`erp_describe_form(live=True)`）是 V1.3 期间的核心调试基础设施：当 `erp_query_*` 因字段不存在报错时，LLM 可以自助调用它定位真实字段，无需开发者介入。详见 [RELEASE_v1.3.md](RELEASE_v1.3.md)。

### 版本检测与自助升级（2 个，V1.8）

| 工具名 | 业务场景 |
|---|---|
| `erp_check_update` | 检查 PyPI 是否有新版本（只读），列出可升级版本与更新日志摘要 |
| `erp_self_update` | **升级到新版本（写操作，强制两步确认）**：首次调用只预览命令，`confirm=true` 才执行 |

> 注：本表未含 V1.4–V1.7 新增的聚合工具（现金流预测、排行榜、客户健康、月报、每日摘要等）与 V1.6 的 `erp_get_ontology`。完整工具集以 MCP Inspector 实际加载为准。

## 升级与版本管理

ERP MCP Server 发布在 PyPI（包名 `erp-mcp`），支持 Agent 自助检测并升级：

1. **检测**：调用 `erp_check_update` —— 对比当前版本与 PyPI 最新版本
2. **预览**：调用 `erp_self_update`（不带参数）—— 显示探测到的安装方式（uv / pip）与确切命令，**不执行**
3. **确认升级**：再次调用 `erp_self_update(confirm=true)` —— 真正执行升级
4. **重启**：升级完成后**手动重启 MCP 客户端**，新版本才生效（MCP server 无法热重载）

`erp_self_update(target_version="x.y.z")` 可指定版本，兼作回滚降级入口。

**锁定版本**：设置 `ERP_VERSION_PIN=0.1.0` 后，`erp_self_update` 一律拒绝升级（即便 `confirm=true`），`erp_check_update` 仅提示不推动。适合不希望被自动升级的生产环境。

**安装方式**：默认按运行环境自动探测 uv / pip；可用 `ERP_UPDATE_INSTALLER=uv`（或 `pip`）显式覆盖。

### 发布新版本（维护者）

CI 由 `.github/workflows/` 两个 workflow 驱动：

- `ci.yml`：push / PR 到 `main` 时，在 Python 3.10–3.12 上跑 `pytest` + `build` + `twine check`
- `publish.yml`：推送 `v*` tag 时，校验版本一致 → 跑测试 → 构建 → 发布到 PyPI

发布步骤：

```bash
# 1. 改版本号（唯一来源）
#    erp_mcp/__init__.py: __version__ = "0.2.0"

# 2. 提交
git add erp_mcp/__init__.py && git commit -m "chore: bump version to 0.2.0"
git push origin main

# 3. 打 tag 并推送 —— 触发 publish.yml 自动发布
git tag v0.2.0
git push origin v0.2.0
```

> tag 名（去掉 `v`）必须与 `erp_mcp.__version__` 一致，否则 `publish.yml` 的 verify 步骤会失败。

**一次性配置（PyPI Trusted Publishing，无需 API token）**：
PyPI → 项目 `erp-mcp` → Settings → Publishing → Add a pending publisher，
填 owner `stevendingliujian-collab` / repo `erp-mcp` / workflow `publish.yml` / environment `pypi`。
首次发布前用 "pending publisher" 即可（包尚不存在也能配）。

## 适配器注意事项

### 金蝶云星空（K3 Cloud 8.x 真实租户对齐）

- 通过官网 `k3cloud_webapi_sdk` 的 `InitConfig` 初始化签名配置
- 所有查询走 `ExecuteBillQuery` + `FormId` 模式；元数据走 `QueryBusinessInfo`
- 单据审核状态码（A/B/C/D/Z）与生产业务状态码（1-6）由适配器自动翻译为中文
- **V1.3 实测对齐了 K3 Cloud 8.x 标准字段**（见 [RELEASE_v1.3.md](RELEASE_v1.3.md) 的"实测字段对齐链路"）：
  - SAL_OUTSTOCK：`FCustomerID` / `FMaterialID` / `FRealQty` / `FSalesManID`（全大写 ID 后缀）
  - AR_Receivable / AP_Payable：`FDATE` / `FCUSTOMERID` / `FSUPPLIERID` / `FENDDATE_H` / `FCURRENCYID`（全大写）
  - STK_Inventory：`FAVBQty`（全大写 VB），安全库存字段不可直接访问
  - PRD_MO：业务状态用 `FStatus`（分录字段，枚举 1-6），不是 `FDocumentStatus`
- 跨租户字段命名差异较大时（二次开发 / 老版本 / 模块启用），用 `erp_describe_form(live=True)` 自助诊断
- Session 失效（401 / 200 含"会话/session"文本）自动重新登录 + 重试一次

### 用友 YonSuite / YonBIP（V1.2 真实接入流程重写）

**三步认证**（自建应用）：

1. `GET https://apigateway.yonyoucloud.com/open-auth/dataCenter/getGatewayAddress?tenantId=X` → 拿到该租户的 `gatewayUrl` + `tokenUrl`
2. `GET {tokenUrl}/open-auth/selfAppAuth/base/v1/getAccessToken?appKey=X&timestamp=T&signature=S` → access_token（2h 有效，自动缓存 + 提前 60s 续 + 401 反应式刷新）
   - 签名：`URLEncode(Base64(HmacSHA256(sorted_param_str, appSecret)))`
3. `POST {gatewayUrl}{业务路径}?access_token=xxx` Content-Type=application/json

业务接口：

- 路径形如 `/yonbip/sd/voucherorder/list`
- Body 用 `simpleVOs` + `open_*_begin/end` + `nextStatusName` 规范
- **客户档案路径是 `/yonbip/digitalModel/merchant/list`**（YonBIP 内部叫 merchant，不是 customer）
- 字段嵌套：`name.simplifiedName` / `shortname.simplifiedName` 等用 dotted path 提取
- 销售订单按客户名过滤走**两步查询**：先 merchant/list 拿 agentId → 再 simpleVOs `agentId eq <id>` 过滤

**配置项**（V1.2 后）：

- `YONSUITE_APP_KEY` / `YONSUITE_APP_SECRET` / `YONSUITE_TENANT_ID`（必需）
- `YONSUITE_META_URL`（可选，默认 `https://apigateway.yonyoucloud.com`）
- `YONSUITE_GATEWAY_URL` / `YONSUITE_TOKEN_URL`（可选 fallback，默认 `https://c2.yonyoucloud.com/iuap-api-gateway` / `iuap-api-auth`）

## 协作规范

### 分支与提交

- `main` 是受保护分支，**禁止直接 push、禁止 force push**。
- 所有改动走 feature 分支 + Pull Request：
  - 命名建议：`feat/...`、`fix/...`、`refactor/...`、`docs/...`
  - 单个 PR 聚焦一件事，便于 review 与回滚
- 提交信息遵循 Conventional Commits：`feat:` / `fix:` / `refactor:` / `docs:` / `test:` / `chore:`

### 标准开发流程

```bash
# 1. 拉最新 main
git checkout main
git pull --rebase origin main

# 2. 开 feature 分支
git checkout -b feat/your-change

# 3. 改动 + 提交 + 跑测试
pytest -q

# 4. 推分支（首次加 -u）
git push -u origin feat/your-change

# 5. GitHub 上开 PR → main，等 review + merge
```

### 推送被拒怎么办

- **绝不要用 `git push -f` 解决冲突**——这会覆盖别人的提交，造成丢工作。
- 正确做法：把本地分支 rebase 到最新远端分支上，解决冲突后再推：

```bash
git fetch origin
git rebase origin/main          # 或者 origin/<目标分支>
# 解冲突 → git add <file> → git rebase --continue
git push                         # 不需要 -f
```

- 如果**自己独占的 feature 分支** rebase 后必须改写远端历史，用 `--force-with-lease` 而不是 `--force`：

```bash
git push --force-with-lease origin feat/your-change
```

别人已经推了新提交时它会自动拒绝，防止误覆盖。

### 不小心在 main 上提交了怎么救

```bash
git checkout -b feat/rescue          # 把当前提交搬到新分支
git checkout main
git reset --hard origin/main         # 本地 main 回到远端状态
git checkout feat/rescue             # 继续在新分支推 PR
```

### 回退已合并的 PR

不要重写 main 历史，而是用 `git revert -m 1 <merge-commit>` 生成反向提交，再走一次 PR 合并（参考本仓库 PR #3）。

详见 `ERP-MCP-Server-MVP-设计文档.md`。
