Metadata-Version: 2.4
Name: xy-erp-mcp
Version: 1.0.0
Summary: 新页 ERP MCP 连接器（Python 实现，API-only，云托管友好）
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp<2,>=1.9
Requires-Dist: httpx>=0.27
Requires-Dist: uvicorn>=0.30
Requires-Dist: pydantic>=2.0

# xy-erp-mcp · 新页 ERP MCP 连接器（Python）

从原 `.NET / ErpMcpServer` 项目独立出来的 **纯 Python** MCP 连接器实现，全部走 ERP REST API（不依赖 SQL 直连），面向**云托管平台**（如 WorkBuddy、TRAE Work CN）发布为托管型连接器的场景设计。

## 特性

- **API-only**：所有能力通过新页 ERP 的 REST API 提供，无需 SQL Server 连接串。
- **双传输**：`stdio`（本地直连）与 `Streamable HTTP`（云托管，默认 `/mcp`，stateless）。
- **零配置自动发现**：启动时按 live ERP 的「中文标签」自动解析 formId / fieldId，跨环境自适应；`form_templates.json` 仅作可选覆盖层。
- **业务语义工具**：客户/商品/供应商查询、销售/采购订单开单、库存/财务聚合汇总，LLM 只见逻辑字段名，不接触 ERP 内部 fieldId。
- **可选 Bearer 网关**：设置 `ERP_MCP_TOKEN` 后，`/mcp` 必须带 `Authorization: Bearer <token>`，便于平台侧统一鉴权。
- **幂等 + 审计**：写操作支持 `idempotencyKey`，重复提交直接回放；每次写操作落 `audit.log`。

## 快速开始

```bash
# 1. 准备环境
python -m venv .venv && source .venv/Scripts/activate   # Windows: .venv\Scripts\activate
pip install -e .

# 2. 配置（复制后填写真实 ERP 信息）
cp .env.example .env

# 3a. 本地 stdio（供本地 WorkBuddy / Claude Desktop 等直连）
TRANSPORT=stdio ERP_BASE_URL=... ERP_PROJECT_ID=... python -m xy_erp_mcp

# 3b. HTTP（云托管 / 容器）
TRANSPORT=http PORT=3000 python -m xy_erp_mcp
# 端点：http://0.0.0.0:3000/mcp   健康检查：http://0.0.0.0:3000/healthz
```

## 环境变量

| 变量 | 必填 | 说明 |
|---|---|---|
| `ERP_BASE_URL` | ✅ | ERP API 基地址，如 `http://127.0.0.1:6080` |
| `ERP_USERNAME` / `ERP_PASSWORD` | ✅ | ERP 账号 |
| `ERP_PROJECT_ID` | ✅ | ERP 项目 ID（缺失则启动退出） |
| `TRANSPORT` | | `stdio`（默认）或 `http` |
| `HOST` / `PORT` | | HTTP 监听地址，默认 `0.0.0.0:3000` |
| `ERP_MCP_TOKEN` | | 可选 Bearer 网关口令（云托管建议开启） |
| `ERP_TEMPLATE_FILE` | | 可选 `form_templates.json` 覆盖层路径 |

## 发布为 WorkBuddy 连接器（stdio 模式，用户自填 ERP 凭据）

本项目采用 **stdio** 接入方式：连接器在用户**本机以子进程运行**，由 WorkBuddy 直接拉起，
**不经过任何中转网关**。用户通过连接表单填写自己的私有 ERP 信息，连接器以本机进程**直连其 ERP**：

| 表单字段（用户自填） | 注入环境变量 | 说明 |
|---|---|---|
| ERP API 地址 | `ERP_BASE_URL` | 私有部署可填内网/公网地址，如 `http://192.168.0.100:6080` |
| 账套ID / 项目ID | `ERP_PROJECT_ID` | ERP 账套标识 |
| ERP 用户名 | `ERP_USERNAME` | WebAPI 登录账号 |
| ERP 密码 | `ERP_PASSWORD` | WebAPI 登录密码（密文保存） |

这些凭据**仅存于用户本机** `~/.workbuddy`，不入云端、不进市场表单之外。
这正好契合「部分客户（老板/文员）可能仅持有自己 ERP 账号，想直接连」的场景——无需管理员先行托管服务端。

> 注：原 .NET 端的「渠道/多租户/scope 白名单」逻辑在本模式下不适用（每个用户直连自己的 ERP 账套），
> 故未内置；`scope`/`salesman_id` 等身份字段作为可选工具参数暴露，默认 `all`/`None`。

### 连接器包（`connector/`，按官方规范生成）

WorkBuddy 官方对连接器有固定包规范（详见 <https://open.workbuddy.cn/docs/connector>）。
本项目已在 `connector/` 生成完整包，**可直接提交审核**：

```
connector/
├── connector-meta.json    # 元信息：名称/描述/示例/auth_mode=token
├── mcp.json               # stdio 连接配置（type=stdio + command/args + env 引用表单字段）
├── token-schema.json      # 用户自填表单（收集 4 项 ERP 凭据）
├── icon.svg               # 市场图标
└── skills/xy-erp/SKILL.md # AI 使用说明（可选但推荐）
```

`connector/mcp.json` 关键片段（`${VAR}` 占位符名称必须与 token-schema.json 的字段 `key` 完全一致）：

```json
{
  "mcpServers": {
    "xy-erp-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["xy-erp-mcp"],
      "env": {
        "TRANSPORT": "stdio",
        "ERP_BASE_URL": "${ERP_BASE_URL}",
        "ERP_PROJECT_ID": "${ERP_PROJECT_ID}",
        "ERP_USERNAME": "${ERP_USERNAME}",
        "ERP_PASSWORD": "${ERP_PASSWORD}"
      }
    }
  }
}
```

### 发布步骤

1. **先把 Python 包发布到 PyPI**（使 `uvx xy-erp-mcp` 可一键安装）：
   ```bash
   python -m build && twine upload dist/*
   ```
   > 包名即 `pyproject.toml` 中的 `xy-erp-mcp`，与 mcp.json 的 `args` 一致。
2. **打包并提交审核**：将 `connector/` 整个目录打包，提交 WorkBuddy 团队审核。
   审核通过后进入连接器市场，用户填自己的 4 项 ERP 凭据即可一键连接。
3. **本地免审核试用**：不进市场时，把下面片段合并进使用方 `~/.workbuddy/mcp.json`
   （前提是已 `pip install xy-erp-mcp` 或 `uvx` 可拉到包），再到连接器管理页点「信任」：
   ```json
   {
     "mcpServers": {
       "xy-erp-mcp": {
         "type": "stdio",
         "command": "uvx",
         "args": ["xy-erp-mcp"],
         "env": { "TRANSPORT": "stdio" }
       }
     }
   }
   ```
   之后用户在连接表单中填写自己的 ERP 地址/账套ID/用户名/密码即可。

### 本地先验证（推荐）

发布前在本地用 stdio 直接拉起，确认能连上你的 ERP：

```bash
TRANSPORT=stdio ERP_BASE_URL=http://127.0.0.1:6080 ERP_PROJECT_ID=xxx \
ERP_USERNAME=admin ERP_PASSWORD=xxx python -m xy_erp_mcp
# 进程会进入 MCP stdio 监听，等待 WorkBuddy/客户端通过 stdin 通信
```

> 若想走 HTTP 自测（容器/反向代理场景），仍可 `TRANSPORT=http` 启动并用 `/healthz` 探活；
> 但**发布到 WorkBuddy 市场请统一用上面的 stdio 模式**。`Dockerfile` 与 `ERP_MCP_TOKEN` 网关仅留给
> 需要自建远程 HTTPS 端点的场景，非本发布方式必需。

## 与原 .NET 项目的能力对照

| .NET (ErpMcpServer) | Python (xy-erp-mcp) | 状态 |
|---|---|---|
| `ErpApiService` | `erp_client.py` | ✅ 完整移植 |
| `SemanticContract` 自动发现 | `contract.py` + `erp_client.build_template_store` | ✅ |
| `FormTemplateStore` 覆盖层 | `templates.py` | ✅ |
| `ErpApiReadSource` 投影 | `read_source.py` | ✅ |
| `McpTools`（25 个工具） | `tools.py` | ✅ |
| `McpResources` | `resources.py` | ✅ |
| `McpPrompts`（5 个） | `prompts.py` | ✅ |
| 直连 SQL（`readMode=sql`） | — | ❌ 本期不移植（API-only） |
| 渠道/多租户/scope 白名单 | — | ❌ 上移至托管平台 |

## 工具一览

- **通用**：`erp_get_form_list` / `erp_get_field_descriptions` / `erp_get_target_form_meta` / `erp_query` / `erp_query_tree` / `erp_query_bom` / `erp_list_attachments`
- **业务查询**：`erp_lookup_customer` / `erp_lookup_product` / `erp_lookup_supplier` / `erp_query_sales_orders` / `erp_query_purchase_orders` / `erp_query_inventory`
- **聚合汇总**：`erp_sales_summary` / `erp_purchase_summary` / `erp_inventory_summary` / `erp_finance_summary`
- **开单/建档**：`erp_create_sales_order` / `erp_create_purchase_order` / `erp_create_customer` / `erp_create_product` / `erp_create_supplier`
- **通用 CRUD**：`erp_create_bill` / `erp_update_bill` / `erp_delete_bill`
