14 KiB
MCP 优化方案
基于当前 MCP(LLM Agent 工具)实现现状整理。日期:2026-09-16。 前提:完整测试 132 个用例全部通过(含数据权限守卫
everyMcpMapperMethodShouldBeProtectedOrExplicitlyTenantSafe、SQL 可重写守卫等),服务可正常编译运行。
一、现状总览
1.1 MCP 工具清单(24 个)
| # | 工具名 | 归属类 | 能力 |
|---|---|---|---|
| 1 | crm_universal_search |
CrmUniversalSearchToolProvider | 跨实体关键词通用搜索(客户/商机/拓客/日报/签到/待办/活动/跟进) |
| 2 | crm_user_profile |
UserProfileToolProvider | 当前用户画像(账号/姓名/角色/部门/租户) |
| 3 | crm_entity_detail |
CrmEntityDetailToolProvider | 按实体类型+ID 查详情,可带相关明细(customer/opportunity/daily_report/checkin/sales_expansion/channel_expansion/crm_expansion/todo) |
| 4 | crm_work_report_search |
WorkReportSearchToolProvider | 日报分页检索 |
| 5 | crm_opportunity_search |
OpportunitySearchToolProvider | 商机分页检索(阶段/金额/排序/字段投影) |
| 6 | crm_customer_search |
CrmDetailSearchToolProvider | 客户分页检索 |
| 7 | crm_checkin_search |
CrmCheckinSearchToolProvider | 外勤打卡分页检索 |
| 8 | crm_expansion_search |
CrmExpansionSearchToolProvider | 销售/渠道拓客分页检索 |
| 9 | crm_crm_expansion_search |
CrmCrmExpansionSearchToolProvider | CRM 拓客(crm_crm_expansion)检索 |
| 10 | crm_followup_search |
CrmFollowupSearchToolProvider | 商机/拓客跟进检索 |
| 11 | crm_todo_search |
CrmTodoSearchToolProvider | 待办检索 |
| 12 | crm_work_today_status |
CrmWorkTodayStatusToolProvider | 今日工作状态 |
| 13 | crm_org_user_search |
CrmOrgUserSearchToolProvider | 组织用户检索 |
| 14 | crm_report_query |
CrmReportQueryToolProvider | 报表/统计查询(见 1.2) |
| 15 | crm_report_catalog |
CrmReportCatalogToolProvider | 报表与查询能力目录 |
| 16 | crm_metadata_catalog |
CrmMetadataToolProvider | 模块/字段/工具能力目录 |
| 17 | crm_dict_options |
CrmDictOptionsToolProvider | 系统字典类型和选项 |
| 18 | crm_field_comment |
CrmFieldCommentToolProvider | MCP 返回字段的中文注释(读 DB COMMENT) |
注:上表已覆盖全部 ToolProvider 类。
crm_search/crm_detail等仅表示内部方法,非独立注册工具。
1.2 crm_report_query 支持的 reportType(10 种)
dashboard_summary、sales_performance、opportunity_funnel、opportunity_trend、daily_report_completion、checkin_summary、expansion_summary、customer_summary、todo_summary、channel_analytics(下含 channelTierDistribution、channelIntentDistribution、channelCoverageSummary、channelRevenueTier、channelContactWecomDistribution 5 个子维度)。
1.3 Mapper 规模(LlmMcpMapper.xml:83 个 select)
- 列表/搜索:35 个(各实体 search + count + universalSearch)
- Dashboard/汇总:27 个(dashboard* 、Summary、funnel/trend/completion、channel 聚合)
- 详情:15 个(selectDetail + 相关明细 selectFollowups/Contacts/Coverages/Comments/Messages/Opportunities)
- 基础数据:selectUserProfile/Roles/Orgs、searchOrgUsers、searchOrganizations、searchRoles、selectDictTypes/Options、selectColumnComments
1.4 数据权限模型
McpDataPermissionInterceptor 通过 SQL 重写注入 owner 过滤。策略见 buildPolicies()(McpDataPermissionInterceptor.java):
| RESOURCE 常量 | owner 列 | 关键受管方法 |
|---|---|---|
| RESOURCE_DAILY_REPORT | r.user_id | searchWorkReports、详情、comments、messages、日报完成率 |
| RESOURCE_CHECKIN | c.user_id | 签到系列 |
| RESOURCE_CUSTOMER | c.owner_user_id | 客户系列 |
| RESOURCE_EXPANSION | s.owner_user_id / c.owner_user_id / ce.owner_user_id | 销售/渠道/CRM 拓客系列 + channel* 聚合 |
| RESOURCE_WORK | t.user_id / l.operator_user_id / u.user_id | 待办、活动、今日状态 |
| RESOURCE_OPPORTUNITY | o.owner_user_id(+项目归属地投影,预售后台可见) | 商机系列、跟进、漏斗 |
| RESOURCE_ALL | u.user_id | searchOrgUsers |
受两类守卫测试保护:每个 mapper 方法要么受策略保护、要么显式 tenant-safe;受保护语句必须能被重写器正确注入过滤条件。
二、可优化点
按「优先级 P0(高) / P1(中) / P2(低)」与「收益」分组,每条含:现状、优化建议、影响。
P0-1 actual_signed_summary 报表「已声明但不可用」(实际缺陷)
- 现状:CrmReportQueryToolProvider.java
- reportType 参数枚举里含
"actual_signed_summary"(L64),crm_report_catalog也声明了该 reportType(L61); - 但
query()的分发 switch(L87-98)没有case "actual_signed_summary",会落到default抛BusinessException("不支持的 reportType"); - 而 L110 的
if ("actual_signed_summary".equals(reportType)) { signedSummary(...) }分支位于 switch 之后,因 switch 先抛异常而永远不可达。 - 底层
signedSummaryRows / signedSummaryQualitymapper 已就绪(XML 2508/2548)且已注册数据权限,只是没被 switch 接上。
- reportType 参数枚举里含
- 建议(核心修复,最小改动):在 switch 增加
case "actual_signed_summary" -> signedSummaryRows/...或在query()入口先处理该 reportType,使声明与实现一致。建议顺带新增一个「枚举声明的 reportType 与 switch 分支一一对应、且 catalog 同步」的守卫测试,防止再次漂移。 - 影响:Agent 按 catalog 调用该报表当前会直接报错 → 属功能性缺陷,建议优先修复。
P0-2 商机筛选/权限口径的「预售后台」与 SDK 差异(一致性风险中)
- 现状:McpDataPermissionInterceptor.java 中商机策略
preSalesVisible=true并带o.project_ownership_location投影,而日报/签到/客户/拓客等preSalesVisible=false。这个差异是有意的(对应商机模块的预售后台可见规则),但没有注释说明为什么商机特殊。 - 建议:在策略处补注释说明「商机为何独享 preSalesVisible 与项目归属地投影」,避免后续维护误改。纯注释改动,零回归。
- 收益:提升可维护性,防止未来误删权限差异导致商机数据越权或越收窄。
P0-3 用户相关查询的敏感字段需审校(安全加固)
- 现状:用户画像已暴露工号/职位/入职日期等。需核验
selectUserProfile/searchOrgUsers是否为显式列(而非*),确认无 password/salt/token/密钥字段带出。 - 建议:通读
selectUserProfile、searchOrgUsers两个查询,确认全部为显式列,且不含密码/盐/token/密钥类字段。若有*,改为显式列。同时补一个黑名单断言测试:任何 MCP 返回的字段名不得出现password/secret/token/key等(类似现有权限守卫)。 - 收益:杜绝敏感信息经 MCP 泄漏的隐患,测试固化。
P0-4 数据权限守卫测试存在,但缺乏「跨实体 join 越权」用例
- 现状:现有测试验证每个受保护语句可被重写,但未覆盖关联明细查询(如
selectChannelExpansionOpportunities、selectCustomerOpportunities、selectSalesExpansionOpportunities)在注入 owner 条件后,相联合的 on 条件是否真正约束到目标行。 - 建议:增加针对性测试:构造「主实体可见但关联实体不可见」的数据,断言关联明细不会越权返回超此 owner 范围的行。
- 收益:堵住「通过详情联查旁路越权」的潜在漏洞。
P1-1 报表 channel_analytics 的 groupBy 口径无文档校验
- 现状:
channel_analytics有参数groupBy(CrmReportQueryToolProvider.java),但内部各聚合方法(tier/intent/coverage/revenue/wecom)对groupBy的支持程度看不到统一校验。 - 建议:明确每个子维度「是否接受 groupBy、接受哪些取值」,对不支持的值在入口做显式校验并报错(而非静默忽略),避免返回误导性聚合。补充文档化说明到
crm_report_catalog。 - 收益:提升统计可信度、避免 Agent 拿到「以为分组了其实没有」的数据。
P1-2 crm_report_catalog 的工具清单可能未随新工具同步
- 现状:CrmReportCatalogToolProvider.java 内联维护了一个工具清单(含
crm_universal_search、crm_work_report_search等),但未见crm_user_profile/crm_work_today_status/crm_crm_expansion_search/crm_report_query之外全部 24 个工具是否都在清单内,且是手工维护,易遗漏。 - 建议:改为从注册表动态生成工具清单(遍历已注册 ToolProvider 的名称与描述),消除手工同步漂移;或至少补齐缺失工具并加「清单与注册工具数量一致」的守卫测试。
- 收益:Agent 发现工具的能力与真实能力一致,避免「目录找不到但实际能调」。
P1-3 selectColumnComments(crm_field_comment)的表白名单是硬编码
- 现状:FieldCommentRegistry.java 以硬编码
tables()/TABLE_COLUMNS维护「别名→物理列」映射,新增表/字段需人工双处登记(列表+注释),易漏。 - 建议:为映射的完整性加守卫测试:
TABLE_COLUMNS键集合与tables()返回的说明集合一致;并可核对每个映射的物理列是否在本批次已暴露的查询字段内。 - 收益:固化「新增字段必登记注释」这一约定,避免字段有值但无注释。
P1-4 limit 上限保护已较好,但部分列表可细化
- 现状:商机等已统一
pageSize/limit默认 10、最大 50(OpportunitySearchToolProvider.java,含 offset 计算)。这是好实践。 - 建议:核验所有其余 search 工具(日报/客户/打卡/拓客/跟进/待办/CRM拓客)是否都用了同一套
MAX_LIMIT与getOffset兜底,避免个别工具直接透传 limit 造成大结果集。可抽公共参数抽取器统一。 - 收益:统一防爆量,避免个别查询成为性能/响应放大点。
P2-1 列表 keyword 搜索未纳入 coverage/联系人等子表字段
- 现状:销售/渠道列表已新增
coverageSummary聚合列(本轮),但 keyword 匹配仍只针对主表字段,不会命中覆盖省/市或渠道联系人姓名。 - 建议:如需「按覆盖地市、按联系人搜索」,在 keyword 的 LIKE 条件里补充对 coverage 子表(省/市)与 contact 表的关联匹配。
- 收益:搜索召回更贴合业务;成本是 SQL 变复杂,需先确认是否为业务目标。
P2-2 覆盖地市 / 日报消息只存在于「详情联查」,无独立搜索工具
- 现状:
coverage、work_report_message已通过crm_entity_detail的相关明细暴露,但无独立搜索入口。 - 建议:若 Agent 需要「按覆盖省/市全局统计」或「全局检索某条日报消息」,可新增
crm_coverage_search/crm_report_message_search独立工具;否则保持现状(避免工具爆炸)。 - 收益:按需增强;过度新增会导致工具面过大、Agent 选择成本上升。
P2-3 未处理缺口(已知合理保留)
sys_org.parent_id/org_code/sort_order:框架表无建表脚本,列名未核实,暂不补真实值。business_calendar_day、report_reminder_*、speech_recognition_config、dashboard_analytics_card_config:配置类表,未暴露属合理(报告鉴权用白名单、配置不属业务数据)。crm_oms_dict_mapping:OMS 后调用映射表,按需求明确不暴露。
三、推荐落地顺序(不含重构)
| 序号 | 项 | 工作量 | 回归风险 |
|---|---|---|---|
| 1 | P0-1 修复 actual_signed_summary 不可用缺陷(核心) | 小 | 低 |
| 2 | P0-3 敏感字段审校 + 黑名单测试 | 小 | 低 |
| 3 | P0-4 关联明细越权测试 | 中 | 零(纯加测试) |
| 4 | P1-1 channel_analytics groupBy 校验 | 小 | 低 |
| 5 | P1-2 report_catalog 动态化/补全 + 守卫 | 中 | 低 |
| 6 | P1-3 field_comment 注册一致性守卫 | 小 | 零(纯加测试) |
| 7 | P1-4 limit 兜底统一 | 小-中 | 低 |
| 8 | P2-1/P2-2 按需 | 视需求 | 视改动 |
四、当前状态结论
- 功能完整性:核心业务主表 + 详情 + 报表 + 数据权限均已覆盖,132 用例全绿,服务可启动。不存在阻断性缺陷。
- 最值得优先做:P0-2(敏感字段黑名单)与 P0-3(关联明细越权测试)——属于「安全加固」性质,风险低、收益高,建议放到下一批次。
- 其余 P1/P2 多为可维护性与体验增强,可按迭代排期,不必一次性做完。