ERPNext MCP Tools
by Arui-world
README.md
# ERPNext MCP Tools
基于 Frappe 官方 `frappe-mcp==0.1.0` 的 ERPNext MCP Server。服务通过 Frappe HTTP 请求身份执行工具,并叠加 RBAC、User Permission、DocType/字段白名单、敏感字段策略、审计、幂等与乐观锁。
## Endpoint 与认证
```text
POST /api/method/erpnext_mcp_tools.mcp.handle_mcp
```
endpoint 的 `allow_guest=False`。生产环境应使用 Frappe OAuth 或 API Key/Secret;开发环境可以使用 Session Cookie。MCP 客户端使用 Streamable HTTP,调用顺序为 `initialize`、`notifications/initialized`、`tools/list`、`tools/call`。
认证信息只通过环境变量或 Secret 管理器注入,不写进仓库、日志、测试快照或 MCP 参数。
## 工具清单
| 类别 | 工具 |
|---|---|
| 基础 | `erpnext_health`、`erpnext_get_current_user`、`erpnext_get_user_business_context` |
| Schema | `erpnext_search_doctypes`、`erpnext_get_doctype_schema` |
| 通用只读 | `erpnext_get_list`、`erpnext_get_doc`、`erpnext_get_count` |
| 领域只读 | `erpnext_get_stock_balance`、`erpnext_get_item_stock_by_warehouses`、`erpnext_get_item_group_low_stock`、`erpnext_get_customer_summary`、`erpnext_get_supplier_summary`、`erpnext_get_receivables_summary` |
| 受控写入 | `erpnext_create_draft`、`erpnext_update_draft` |
写工具仅开放 `Sales Order`、`Purchase Order` 和 `Material Request` 草稿,不提供通用 submit、cancel 或 delete。更新草稿必须提供上次读取到的 `expected_modified`,所有写入必须提供 8–128 位 `idempotency_key`。
Agent 侧的连接配置、工具分配与响应归一化约定见姊妹项目 [erpnext-agent](https://github.com/Arui-world/erpnext-agent);本仓库 README 的「返回契约」与 `tests/test_mcp_protocol.py` 是工具行为的事实定义。
当前站点未安装 HRMS,因此不伪装支持 HRMS 提供的 `Expense Claim`。安装 HRMS 后需要先增加独立字段策略、权限测试和业务回归,再加入运行时白名单。
## 返回契约
成功和失败都返回稳定、可 JSON 序列化的 `structuredContent`:
```json
{
"ok": true,
"data": {},
"meta": {
"trace_id": "...",
"tool": "erpnext_get_doc",
"user": "user@example.com",
"content_trust": "untrusted_business_data"
}
}
```
```json
{
"ok": false,
"error": {
"code": "PERMISSION_DENIED",
"message": "当前用户无权执行该操作"
},
"meta": {"trace_id": "..."}
}
```
常见错误码包括 `AUTHENTICATION_REQUIRED`、`PERMISSION_DENIED`、`DOCTYPE_BLOCKED`、`FIELD_NOT_ALLOWED`、`INVALID_ARGUMENTS`、`INVALID_FILTER_OPERATOR`、`MISSING_REQUIRED_FIELDS`、`NOT_FOUND`、`VERSION_CONFLICT`、`IDEMPOTENCY_CONFLICT`、`AUDIT_FAILED` 和 `INTERNAL_ERROR`。内部异常与堆栈不会返回给模型,排错使用 `trace_id` 查询站点日志。
## JSON-RPC 示例
列出 Company:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "erpnext_get_list",
"arguments": {
"doctype": "Company",
"fields": ["name", "modified"],
"limit_page_length": 20
}
}
}
```
查询物料在当前用户可见仓库中的全部 Bin 库存:
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "erpnext_get_item_stock_by_warehouses",
"arguments": {"item_code": "test item1"}
}
}
```
返回的 `warehouses` 是逐仓库数量,`totals` 是同一物料库存单位下的合计;仅包含当前用户可见的叶子仓库及已有 Bin 的仓库。
创建 Material Request 草稿:
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "erpnext_create_draft",
"arguments": {
"doctype": "Material Request",
"idempotency_key": "agent-session-42-request-1",
"payload": {
"material_request_type": "Purchase",
"company": "Example Company",
"transaction_date": "2026-08-08",
"schedule_date": "2026-08-09",
"items": [
{
"item_code": "ITEM-0001",
"qty": 1,
"uom": "Nos",
"conversion_factor": 1,
"schedule_date": "2026-08-09"
}
]
}
}
}
}
```
工具只创建 `docstatus=0` 的草稿。提交、作废、删除和财务过账不属于本 MCP Server;后续如开放 submit,必须由 Agent HITL 网关签发绑定用户、目标、参数摘要和短 TTL 的一次性审批令牌。
## 安全边界
- 通用查询不接受任意 SQL、`ignore_permissions`、debug 或 pluck。
- 每页最多 100 条;fields、filters、order_by 均根据 DocType Meta 校验。
- Schema、列表字段和单据详情同时按当前用户的 Frappe permlevel 裁剪;高权限字段不会仅因拥有 DocType read 权限而暴露。
- `User`、OAuth Token、Bank Account、Salary Slip、日志等敏感 DocType 被阻断。
- 密码、密钥、银行账号、税号、薪资、联系方式、owner 等字段被阻断或脱敏。
- 子表行数、文本长度和序列化深度有硬上限。
- 库存余额除 Item、Warehouse、Company 权限外,还强制要求 Stock Ledger Entry 读取权,避免底层库存函数绕过角色边界。
- 财务摘要仅统计 `docstatus=1` 且未结清单据,金额按币种分组,禁止跨币种直接相加。
- 创建草稿在调用 ERPNext 业务钩子前校验最小必填字段,缺失时返回稳定的 `MISSING_REQUIRED_FIELDS`。
- 写入审计失败时事务 fail closed 并回滚;读取审计异常不会阻断业务结果。
- 所有业务文本按 `untrusted_business_data` 返回,Agent 不应把单据内容当作系统指令。
## 安装与兼容性
App 安装和 migrate:
```bash
bench --site <site> install-app erpnext_mcp_tools
bench --site <site> migrate
```
Frappe 16.23.0 使用 Python 3.14、Pydantic 2.12.5 和 Werkzeug 3.1.6;`frappe-mcp 0.1.0` 的包元数据锁定较旧的 Pydantic/Werkzeug。不能为满足该旧锁定而降级 Frappe 核心依赖。当前 Bench 使用:
```bash
./env/bin/python -m pip install 'jsonschema>=4.24,<5'
./env/bin/python -m pip install --no-deps 'frappe-mcp==0.1.0'
```
升级 `frappe-mcp` 前必须重新执行协议、权限、写入和 HTTP 回归测试。
## 验证
```bash
./env/bin/frappe-mcp check --app erpnext_mcp_tools --verbose
bench --site dev.localhost run-tests --app erpnext_mcp_tools
cd apps/erpnext_mcp_tools
pre-commit run --all-files
```
官方 Inspector CLI 示例:
```bash
npx @modelcontextprotocol/inspector --cli \
http://dev.localhost:8000/api/method/erpnext_mcp_tools.mcp.handle_mcp \
--transport http \
--method tools/list \
--header "Authorization: token ${API_KEY}:${API_SECRET}"
```
## Agent 接入约束
1. 启动时调用 `initialize` 和 `tools/list`,不要在客户端硬编码可能变化的 Schema。
2. 使用最终用户对应的 OAuth 身份,不要让所有 Agent 共用 Administrator。
3. 将 `meta.content_trust=untrusted_business_data` 传播到模型上下文策略。
4. 写入前由 Agent 展示结构化草稿预览;重试必须复用同一幂等键。
5. 更新前先重新读取单据并携带精确 `modified`;`VERSION_CONFLICT` 后不可盲重试。
6. `PERMISSION_DENIED` 不应升级身份重试;应提示用户申请 ERPNext 权限。
## 故障排查
- HTTP 403:认证未进入 Frappe 请求,检查 OAuth、API token 或 Session Cookie。
- `DOCTYPE_BLOCKED` / `FIELD_NOT_ALLOWED`:请求越过固定安全策略,不应通过 `ignore_permissions` 绕过。
- `IDEMPOTENCY_IN_PROGRESS`:相同请求正在执行,稍后使用同一键重试。
- `IDEMPOTENCY_CONFLICT`:同一键被用于不同参数,生成新键并先核对是否已有草稿。
- `AUDIT_FAILED`:写操作已经回滚,先恢复站点日志/存储后再重试。
- `INTERNAL_ERROR`:使用返回的 `trace_id` 检查 `logs/erpnext_mcp_tools.log` 和 Error Log。
- 官方 CLI 对无参数工具的空 `properties`、filters 联合类型可能显示提示;以 CLI 退出码、JSON Schema 校验和 Inspector 实际调用为准。
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues