Metadata-Version: 2.4
Name: crm-mcp
Version: 0.5.0
Summary: CRM MCP Server: expose OTD CRM business queries to AI agents.
Author: OTD
License: Proprietary
Project-URL: Homepage, https://github.com/stevendingliujian-collab/crm-mcp
Project-URL: Repository, https://github.com/stevendingliujian-collab/crm-mcp
Project-URL: Issues, https://github.com/stevendingliujian-collab/crm-mcp/issues
Keywords: mcp,crm,otd,ai-agent,model-context-protocol
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp[cli]<2.0,>=1.9.0
Requires-Dist: pydantic>=2.5
Requires-Dist: pydantic-settings>=2.1
Requires-Dist: requests>=2.31
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"

# CRM MCP Server

把 OTD CRM 系统的业务查询能力封装成 MCP 工具，供 AI Agent（经飞书/Hermes）调用。
架构范式对齐姊妹项目 ERP-MCP：适配器模式、`.env` 配置、统一 Markdown 渲染、
统一错误码、+8 时区、溯源字段。

## 功能

提供 38 个 MCP 工具，覆盖客户 / 联系人 / 商机 / 合同 / 项目 / 应收 / 跟进的查询与
写入，外加老板视角聚合与经营指标。所有写操作均为两步确认（先预览，再传
`confirm=true` 写入）。

### 查询与详情（只读）

| 工具 | 说明 |
|---|---|
| `crm_query_customers` | 按关键词 / 地区 / 行业 / 负责人 / 成交状态查客户档案 |
| `crm_get_customer` | 客户详情 |
| `crm_query_contacts` | 按关键词 / 客户查联系人 |
| `crm_query_opportunity_contacts` | 查某商机关联的联系人 |
| `crm_query_opportunities` | 按关键词 / 客户 / 负责人 / 金额 / 阶段 / 是否有合同查商机 |
| `crm_get_opportunity` | 商机详情 |
| `crm_list_business_stages` | 列出商机阶段字典 |
| `crm_query_contracts` | 按关键词 / 客户 / 负责人 / 是否签约查合同 |
| `crm_get_contract` | 合同详情 |
| `crm_query_contracts_by_opportunity` | 查某商机转化出的合同 |
| `crm_query_projects` | 按关键词 / 客户 / 负责人 / 维保到期查项目 |
| `crm_query_receivables` | 按客户 / 合同 / 计划收款日查应收款（催收用） |
| `crm_query_contract_receivables` | 查某合同的应收款计划 |
| `crm_query_invoices` | 按客户 / 合同 / 开票状态查开票情况 |
| `crm_query_follow_ups` | 查某关联对象（商机/客户/合同等）的跟进记录 |

### 写操作（两步确认）

| 工具 | 说明 |
|---|---|
| `crm_create_customer` / `crm_update_customer` | 新建 / 修改客户（update 为 read-merge-write） |
| `crm_assign_customer` | 分配客户负责人 |
| `crm_create_contact` / `crm_update_contact` | 新建 / 修改联系人 |
| `crm_create_opportunity` / `crm_update_opportunity` | 新建 / 修改商机 |
| `crm_advance_opportunity_stage` | 推进 / 修改商机阶段 |
| `crm_create_follow_up` | 创建跟进记录 |

### 老板视角聚合与经营指标（只读）

| 工具 | 说明 |
|---|---|
| `crm_boss_brief` | 老板简报（应收/回款/销售漏斗/交付风险全景） |
| `crm_sales_funnel` | 销售漏斗（在谈管线 + 加权预测） |
| `crm_salesperson_ranking` | 销售业绩榜 |
| `crm_receivables_summary` | 应收款总览（逾期 / 客户欠款排行） |
| `crm_contract_trend` | 签约趋势（按月） |
| `crm_project_health` | 项目交付健康度（维保到期预警） |
| `crm_receivable_aging` | 应收账龄分析（逾期分桶 + 逾期客户排行） |
| `crm_collection_efficiency` | 回款效率（回款率 + DSO 回款周期） |
| `crm_conversion_funnel` | 商机转化率（有效商机数 / 报价转化率） |
| `crm_customer_360` | 客户 360 全景（商机/合同/应收账龄/跟进） |
| `crm_business_metrics` | 一键经营体检 + 0–100 健康度评分 |
| `crm_sales_attainment` | 销售达成率（年度/季度目标 vs 实际） |

### 版本管理

| 工具 | 说明 |
|---|---|
| `crm_check_update` | 对比当前版本与 PyPI 最新版本，提示是否有新版 |
| `crm_self_update` | 升级 crm-mcp 自身（两步确认） |

说明：

- 查询结果统一渲染为 Markdown 表格，含「内部ID」列 —— 该 ID 可作为
  `crm_query_follow_ups` 的 `related_id`，或详情 / 写操作工具的 id 参数，实现
  「查商机/客户 → 看详情 / 查跟进 / 改资料」串联。
- 所有写操作两步确认：首次调用仅返回预览、不写入，须再次调用并传
  `confirm=true` 才真正执行；写操作向 stderr 输出 `[CRM_MCP_AUDIT]` 审计日志。
- CRM 只有应收，不含应付；应付请用 ERP 系统查询。毛利 / 库存 / 生产 / 现金
  等指标超出 CRM 数据范围，需 ERP 协同。

## 安装

```bash
pip install crm-mcp
```

或免安装直接用 `uvx crm-mcp`（见下文「用 uvx 免安装调用」）。从源码开发安装：

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

## 配置

复制 `.env.example` 为 `.env` 并填写 OTD CRM 凭据：

```bash
CRM_API_URL=https://app.otd-odincloud.com
CRM_TENANT_ID=        # 填了就跳过租户发现
CRM_USERNAME=your_username
CRM_PASSWORD=your_password
CRM_USER_ID=
CRM_TIMEOUT=30
CRM_VERSION_PIN=        # 填了即锁定版本：crm_check_update 只提示、crm_self_update 拒绝升级
CRM_UPDATE_INSTALLER=   # 升级方式覆盖：uv / pip，留空自动探测
CRM_TRANSPORT=stdio     # 传输模式：stdio（单用户）| streamable-http（共享服务多用户）
CRM_HOST=127.0.0.1      # streamable-http 监听地址
CRM_PORT=8000           # streamable-http 监听端口
CRM_USER_STORE=         # 凭据库 JSON 路径（共享部署多用户：员工标识→CRM账密/租户）
```

### 用 uvx 免安装调用

发布到 PyPI 后，MCP 客户端可直接用 `uvx` 拉起，无需预先安装：

```json
{
  "mcpServers": {
    "crm": {
      "command": "uvx",
      "args": ["crm-mcp"],
      "env": {
        "CRM_USERNAME": "...",
        "CRM_PASSWORD": "...",
        "CRM_TENANT_ID": "..."
      }
    }
  }
}
```

## 运行

```bash
python -m crm_mcp.server          # stdio 模式
crm-mcp                           # 同上（安装后）
```

用 MCP Inspector 调试：

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

### 共享服务多用户部署（方式 B）

一套服务器服务全公司员工时，crm-mcp 按身份为每个员工缓存独立的 CRM 会话
（LRU + TTL），令牌互不串扰，数据范围与写操作审计自动归属到真实操作人。

身份解析优先级：**HTTP 请求头 → 员工标识 `crm_user` → 工具参数 `crm_token`
→ 服务端环境变量**。

**推荐：服务端凭据库 + 员工标识。** 管理员维护一份凭据库文件（员工标识 →
CRM 账号/密码/租户），用 `CRM_USER_STORE` 指向它；调用方只需把**非秘密的**
员工标识作为 `crm_user` 参数传入，crm-mcp 即以该员工身份登录、令牌自动续期。
账号密码只在服务端，不进入模型上下文。支持多租户。

```jsonc
// crm_users.json（chmod 600）
{
  "zhangsan@parent": { "username": "zhangsan", "password": "…", "tenant_id": "T001" },
  "lisi@subA":       { "username": "lisi",     "password": "…", "tenant_id": "T042" }
}
```

**HTTP 请求头通道**（待 Agent 框架支持按用户注入头后启用）：设
`CRM_TRANSPORT=streamable-http`，由调用方传 `X-CRM-Token` 或
`X-CRM-Username`+`X-CRM-Password`、可选 `X-CRM-Tenant`。建议绑定 `127.0.0.1`
并由上游反向代理终止 TLS。

未带任何身份信息时回退到环境变量（即 stdio 单用户配置）。

完整的 Hermes / OpenClaw 配置示例见 [docs/multi-user-setup.md](docs/multi-user-setup.md)。

## 测试

```bash
pytest -q
```

## 升级

`crm-mcp --version` 查看当前版本。运行中可让 Agent 自助升级：

1. `crm_check_update` —— 对比当前版本与 PyPI 最新版本，返回是否有新版。
2. `crm_self_update` —— 两步确认的写操作：首次调用只返回升级命令预览，
   传 `confirm=true` 才真正执行（自动探测 uv tool / pip 安装方式，
   `target_version` 可指定版本以回滚）。
3. 升级后当前进程仍运行旧版，需**重启 MCP 客户端**使新版生效。

设置 `CRM_VERSION_PIN` 可锁定版本：`crm_check_update` 只提示，`crm_self_update` 拒绝升级。

## 后端说明

CRM 后端是 ABP 框架（ASP.NET Boilerplate）应用：

- 多租户，请求带 `__tenant` 头
- 两步认证：租户发现（可选）+ 登录拿 JWT
- 响应 `Content-Type` 常为 `text/plain`，body 可能是双重编码的 JSON 字符串
- 列表查询统一 `POST {pageIndex, pageSize, filter}`，响应 `{totalCount, items}`
