# feishu-project-mcp — details

### 方式B：Meegle CLI（备选）

```bash
npm install -g @lark-project/meegle
meegle auth login --device-code --host project.feishu.cn
```
注意：Device Code 授权可能需要管理员审批，MCP Token 方案更可靠。

## 核心工具速查


### 查询类（最常用）
| 工具 | 用途 | 参数要点 |
|------|------|---------|
| `search_project_info` | 空间名→project_key | `project_key: "xsguanli"` |
| `list_todo` | 我的待办/已办 | `action: "todo"/"done"`, 可选`project_key` |
| `get_workitem_brief` | 工作项详情 | `project_key` + `work_item_id` |
| `get_node_detail` | 节点状态详情 | 返回所有节点及状态 |
| `list_workitem_types` | 空间的工作项类型 | 商机(story)/缺陷(issue)/任务(sub_task) |
| `list_workitem_comments` | 评论列表 | |
| `get_workitem_op_record` | 操作记录 | |
| `search_user_info` | 姓名→user_key | |
| `list_project_team` | 空间团队 | |
| `search_view_by_title` | 视图搜索 | 需要`view_scope`和`key_word`参数 |


### 编辑类
| 工具 | 用途 | 参数要点 |
|------|------|---------|
| `transition_node` | 节点流转/审批 | **`action: "confirm"`** 通过, `"rollback"` 回退 |
| `add_comment` | 添加评论 | 支持@人，markdown格式 |
| `update_field` | 更新字段值 | |
| `create_workitem` | 创建工作项 | |

## 已知项目空间

| 名称 | project_key | simple_name |
|------|------------|-------------|
| 销售管理 | `6593cd71471290e3cc6be6e6` | `xsguanli` |
| 客户项目管理 | `658bb60520ea78a2125f1b99` | `csrw` |
| 售后管理 | `658288abfb8bd616b17025f1` | `br-shgl` |

## 工作项类型（完整列表 — 2026-07-10 验证）

通过 `list_workitem_types(project_key="6593cd71471290e3cc6be6e6")` 获取，共13种：

| 类型 | type_key | api_name | MQL表名 | 用途 |
|------|----------|----------|---------|------|
| 商机 | `story` | story | `销售管理.story` | 项目商机/需求 |
| **活动** | `65ae1e5d44338dbe7c39a29a` | sj | **`销售管理.活动`** | **销售拜访/跟进记录** |
| 销售订单 | `662612aea6bb7089fea044ef` | xsdd | `销售管理.销售订单` | 订单/报价 |
| **客户** | `65ae1e403c87b152f3365ca6` | kh | **`销售管理.客户`** | **客户档案** |
| **联系人** | `65b8a6cacde27fd415d769ca` | lxr | **`销售管理.联系人`** | **客户联系人/决策链角色** |
| 线索 | `62974d248fe7c8c294784b2b` | customermanagement | `销售管理.线索` | 销售线索 |
| 缺陷 | `issue` | issue | — | Bug/问题 |
| 版本 | `version` | version | — | 版本管理 |
| 图表 | `chart` | chart | — | 报表 |
| 任务 | `sub_task` | sub_task | — | 子任务 |
| 拜访计划-生命科学专用 | `6835627f161b6a38bbf66e37` | bfjh | — | PI拜访 |
| 月销售目标 | `690add8d0ee907fb0965db16` | yj | — | 月度KPI |
| 年销售目标 | `699febf93ae563b63990df5a` | nxsmb | — | 年度KPI |


### 待办盘点
```
list_todo(action="todo", page_size=150) → 按project_name/节点聚合
```


### ⚠️ 持久化大结果文件的解析坑（2026-07-24）

`search_by_mql` 结果超 ~100KB 时会被持久化到 `/var/folders/.../hermes-results/tool_*.txt`，只回传预览。解析该文件时注意：

1. 文件整体是 `{"result": "<内层JSON字符串>"}`，需两次 `json.loads`。
2. **内层 `result` 字符串尾部混有 `\nlog_id: xxx` 追踪信息**，直接 `json.loads` 报 `Extra data`。先 `inner_raw.split("\nlog_id:")[0]` 再解析。
3. 字段值按 `value_type` 分五种形状：`string_value`（取原文）、`double_value`/`long_value`（数值，如 金额/单价/销售数量/价税合计）、`key_label_value`（枚举，取 `.label`，如 状态/单据类型/销售部门/关联客户）、`key_label_value_list`（枚举数组，label 逗号拼接）。
4. 完整解析脚本模式见 `references/mql-persisted-output-parsing.md`。


### ✅ 按跟进人过滤活动（2026-09-08 实测攻克）

活动记录里的「跟进人」**不是** `current_status_operator`（那是客户类型字段，活动类型 SELECT 它返回 null），也不是 `创建者`。它是角色 `role_821d0b`「活动参与人员」，MQL 用角色语法 `__活动参与人员` 查询：

```sql
-- ✅ 按跟进人过滤活动（array_contains 匹配 multi-user 角色）
SELECT `名称`, `创建时间`, `跟进部门` FROM `销售管理`.`活动`
WHERE `创建时间` >= '2026-08-29' AND array_contains(`__活动参与人员`, '宋学伟')
ORDER BY `创建时间` DESC
```

活动类型四个角色（`list_workitem_role_config` 查得）：`role_821d0b`活动参与人员、`role_d2cc73`部门经理、`role_d9af53`客户-所属人、`role_eaaeb6`客户-协作人。

⚠️ **跟进内容在 `field_b99055`（活动描述），不是 `description`**（2026-09-09 血泪教训）——`description` 是内建描述字段，活动类型里恒空，查它必然误判「该人活动未填跟进内容」。真实落点在 `field_b99055`（MQL 显示名「活动描述」），所有销售员（含赵云浩、刘虹晶）的跟进内容都在这里且很完善。SELECT 必须显式带 `field_b99055`，否则拿不到任何实质内容。

按人拉活动做分析前，先判断该人属于哪种填写习惯，别假设 description/name 必有料。


### 决策链自动化管理（2026-07-11）

**核心发现**：`决策链URL链接` 是客户（kh）类型的 MQL 可读字段。
- **读**：`SELECT 客户名称, 决策链URL链接 FROM 销售管理.客户 WHERE 客户名称 = "精确名称"`
- **写**：`update_field` 对表单模板字段返回 `mcp_result: ""` 但不实际持久化。回填需飞书Open API或手动操作
- ⚠️ ORDER BY DESC 全量拉取时该字段可能为空；必须 WHERE 精确匹配客户名
- **集团复用模式**：同集团客户可共享决策链URL（如西安华伟→复用苏州天隆的决策链）

**自动化工具**：`manage_decision_chain.py`（在 Hermes输出-工作类/ 目录下）
```bash
python3 manage_decision_chain.py scan [--limit N]   # 批量扫描决策链
python3 manage_decision_chain.py get 客户名          # 查询单个
python3 manage_decision_chain.py set 客户名 URL      # 设置/更新（回填飞书）
python3 manage_decision_chain.py clear 客户名        # 清空
python3 manage_decision_chain.py missing            # 列出缺失的客户
python3 manage_decision_chain.py export [--format csv]  # 导出
```

**数据流**：`飞书MQL扫描 → 本地JSON → set/clear回填 → 飞书更新`


### ⚠️ 关键发现：创建者≠销售员（血泪教训）
MQL `创建者` 返回飞书系统记录创建人（常为单一自动化/集成账号，如 殷士海 yinshihai@lundong.com），不是真实销售员。真实销售员在 `get_workitem_brief` → `role_members` → `销售员`。

**绝不能按创建者归属部门或计算KPI**。用户明确指出：*"你可千万不要给我瞎估算，这里的信息全部要真实的"*。正确做法：补全客户名 → 按客户归属真实部门 → 真实销售额汇总。严禁按目标占比分摊。

### ⚠️ 核心发现：`relation_id` 是必需参数（2026-07-11）

`list_related_workitem` **必须同时传 `relation_id`**，否则返回 `code=1000050863, relation_id is required`。

```python
# Step 1: 获取所有可用的 relation_id
relations = mcp('list_workitem_relations', {
    'project_key': SALES_PK, 'work_item_id': work_item_id
})
# Step 2: 用 relation_id 查询关联项
items = mcp('list_related_workitem', {
    'project_key': SALES_PK, 'work_item_id': work_item_id,
    'relation_id': relations['list'][0]['id']  # ← 必需！
})
# 返回: [{id, name, work_item_type_key}]
```

**已验证的关联查询**（圣湘生物 wid=4196204802）：
| 关联名称 | 返回数 | 示例 |
|---------|--------|------|
| 客户-联系人关联 | **50条** | 段正兴、陈汝、王慧… |
| 客户-商机管理关联 | N条 | （待验证） |
| 客户-客户项目管理（客户） | N条 | （待验证） |


### 获取关联客户
```
1. list_workitem_relations(project_key) → 找"销售订单-客户关联"
   关系ID: 6593cd71471290e3cc6be6e6:work_item_relation:relation_1713785263022
2. list_related_workitem(project_key, work_item_id, relation_id)
   → 返回 {id, name, work_item_type_key} 列表
```


### 获取销售员

**通过 Hermes MCP 工具（最简单）**：
```
get_workitem_brief(project_key, work_item_id)
  → role_members → 找"销售员"角色 → members[0].name
```

**通过 Python 直连 HTTP（⚠️ 两个陷阱）**：

⚠️ **陷阱1：`role_members` 是 ARRAY，不是 dict！**
```python
# ❌ 错误：把 role_members 当成 dict
role_members = result.get("role_members", {})
sp = role_members.get("role_7dd6e0", [{}])[0].get("name")  # KeyError!

# ✅ 正确：遍历数组查找
attr = result.get("work_item_attribute", result)
for role in attr.get("role_members", []):
    if role.get("key") == "role_7dd6e0":
        sp = role.get("members", [{}])[0].get("name", "")
        break
```

⚠️ **陷阱2：`content[0].text` 尾部带 `\nlog_id:...`，直接 `json.loads` 会炸**
```python
# 必须先 strip log_id 行
import re
clean = re.sub(r'\nlog_id:.*$', '', text.strip())
data = json.loads(clean)
```

> 完整解析模式 + 角色 key 参考表见 `references/direct-http-get-workitem-brief.md`

### 决策链自动化读写
见 `references/decision-chain-automation.md` — MQL WHERE精确查询+update_field回填+自动化扫描工具
脚本：`scripts/manage_decision_chain.py`

## 故障排查

**MCP 工具不出现（`hermes mcp list` 显示 enabled 但无法调用）**：
1. 检查 `mcp` SDK 是否安装：`/opt/homebrew/bin/python3.13 -c "import mcp; print('OK')"`
2. 未安装 → 见「前置条件」安装命令
3. 已安装但工具仍未出现 → 重启 Hermes（MCP 工具在启动时注册）
4. 重启后仍然失败 → 检查 transport 是否正确（必须是 `streamable_http`，不是 `sse`）

**`hermes mcp test` 返回 405 Method Not Allowed**：
- **transport 配置错误**：`transport: sse` 会导致 405。飞书 MCP Server 不接受 SSE 的 GET 请求。
- **修复**：将 config.yaml 中 transport 改为 `streamable_http`（或用 `hermes config set mcp_servers.feishu-project.transport streamable_http`）
- 正确配置后 `hermes mcp test` 应返回 `✓ Connected` + `✓ Tools discovered: 48`

**Token 过期**：
- 飞书 MCP Token 长期有效（除非在飞书后台手动撤销）。若过期，从飞书项目「功能入口」重新获取并更新 config.yaml。

## 凭据管理

Token 在 `config.yaml` 的 `mcp_servers.feishu-project.headers.X-Mcp-Token`。

OAuth用户token（用于个人云盘/知识库）：`~/.hermes/.feishu_user_token`。
