mcp-design.md 13 KB

Instrument Config MCP Design

1. 目标

基于现有配置接口,使用 FastMCP 构建一个面向大模型的查询型 MCP 服务。

第一版目标:

  • 提供位置、系统、设备类型、仪表类型的查询能力
  • 提供设备、仪表的正式搜索能力
  • 支持通过 project_key 选择项目环境
  • 保持接口原有分页行为,默认请求 page_size=100
  • id 作为正式查询参数,避免名称重名带来的歧义

第一版不做的事情:

  • 不做名称到 id 的自动猜测
  • 不做复杂缓存失效策略
  • 不做写接口
  • 不做额外的业务推断型工具

2. 设计原则

2.1 查询参数以 ID 为准

正式搜索工具统一使用 id 参数,而不是名称参数。

所有业务工具还必须显式传入 project_key,用于定位项目配置和认证信息。

原因:

  • 名称可能重名
  • 树结构下可能出现同名节点
  • 大模型如果基于名称做自动选择,容易产生静默误查
  • 接口本身的正式过滤条件也是以 id 为主

因此第一版采用两层结构:

  • 列表工具:返回候选项和 id
  • 搜索工具:使用 id 发起正式查询

2.2 列表工具负责“给 ID”

第一版不单独提供 find_locationsfind_systems 这类解析工具。

原因:

  • 已有列表接口足够支撑“先查候选,再拿 id”流程
  • 减少工具数量,避免能力重复
  • 保持 MCP 工具边界清晰

2.3 分页保持后端原样

所有分页接口都保留后端原有分页行为:

  • 默认请求 page_size=100
  • 由调用方显式传入 page_num
  • MCP 不自动翻页

这样可以避免一次调用返回过大数据,也更符合后端原始接口行为。

当某一页没有找到目标结果时,MCP 应明确提示:

  • 当前只查询了指定页
  • 如需继续查找,可以继续传更大的 page_num

2.4 返回结构尽量保持后端原样

第一版不对树结构或接口返回结果做额外整理:

  • 不做扁平化
  • 不做去重
  • 不做额外路径拼接
  • 不做字段重命名

MCP 以“尽量接近后端返回”的方式对外暴露结果,只补充必要的工具层说明。

3. 接口分析

3.0 项目配置与认证

项目配置来自 sys_config 中的 mcp_project_data_projects

单个项目配置至少包含:

  • project_key
  • project_name
  • base_url
  • username
  • password
  • enabled

业务请求流程:

  1. 通过 project_key 找到项目配置
  2. 从项目配置读取 base_urlusernamepassword
  3. 先读取当前项目的 token 缓存
  4. 如缓存不存在或已过期,则调用登录接口重新获取 token
  5. 使用获取到的 token 访问配置接口

登录接口:

  • POST {base_url}/api/ai/auth/password_login

登录成功条件:

  • 返回 JSON
  • errcode00.0
  • 响应中包含 token

业务请求认证方式:

  • 请求头使用 Authorization
  • header 的值直接使用 token 原文
  • 不额外拼接 Bearer 前缀

token 缓存策略:

  • project_key 单独缓存
  • 缓存内容存储在 sys_config
  • expire_at 仍未过期时直接复用
  • 当缓存失效时重新登录并回写缓存

3.1 位置搜索

接口:POST /api/configapi/location/list

用途:

  • 按关键字搜索位置
  • keyword 为空时可全量拉取

输入特点:

  • 支持分页
  • 返回树结构

输出特点:

  • 节点包含 children
  • 节点包含 idcodefull_codenameparent_idlevel

MCP 处理建议:

  • 默认请求 page_size=100
  • 保持树结构原样返回
  • id 作为后续设备/仪表查询参数

3.2 仪表搜索

接口:POST /api/configapi/meter/list

用途:

  • 按安装位置、仪表类型、状态、测量对象关联维度搜索仪表
  • keyword 为空时可全量拉取

主要过滤字段:

  • location_id
  • show_below
  • keyword
  • meter_type_id
  • measurement_location_ids
  • measurement_system_ids
  • measurement_device_type_ids
  • status

字段语义:

  • location_id:仪表安装位置
  • measurement_location_ids:仪表关联的测量位置
  • measurement_system_ids:仪表关联的测量系统
  • measurement_device_type_ids:仪表关联的测量设备类型

注意:

  • 安装位置和测量位置不是一个概念
  • 返回中存在父子仪表关系:parent_idparent_name
  • keyword 更适合定义为“模糊匹配名称、编号等文本字段”

关于 measurement_flag 的当前工作假设:

  • 0:无
  • 1:设备
  • 2:其他非设备对象

该语义来自现有页面文案和样例数据,但后续若后端给出正式定义,应以正式定义为准。

3.3 系统树

接口:POST /api/configapi/system/tree

用途:

  • 获取系统类型树和系统节点

结构特点:

  • type=1:系统类型
  • type=2:系统

MCP 处理建议:

  • 保持树结构原样返回
  • 不额外补充扁平节点列表

3.4 系统列表

接口:POST /api/configapi/system/list

用途:

  • 按系统类型查询系统列表
  • 可作为系统全量列表接口使用

主要过滤字段:

  • system_type_id
  • show_below

MCP 处理建议:

  • 默认请求 page_size=100
  • 用于给设备搜索或其他工具提供系统 id

3.5 设备类型列表

接口:POST /api/configapi/devicetype/list

用途:

  • 返回设备类型字典

特点:

  • 当前支持 page_size=-1 全量返回
  • 非分页核心接口

3.6 设备搜索

接口:POST /api/configapi/device/list

用途:

  • 按位置、系统、设备类型、关键字查询设备
  • keyword 为空时可全量拉取

主要过滤字段:

  • location_id
  • show_below
  • keyword
  • system_ids
  • device_type_ids

MCP 处理建议:

  • 默认请求 page_size=100
  • 使用 id 过滤
  • 返回结构尽量保持后端原样

3.7 仪表类型列表

接口:POST /api/configapi/metertype/list

用途:

  • 返回仪表类型字典

特点:

  • 当前支持 page_size=-1 全量返回
  • 供仪表搜索使用

4. 业务关系梳理

4.1 位置

  • 位置是树结构
  • 设备安装在位置上
  • 仪表也安装在位置上

4.2 系统

  • 系统存在系统类型和具体系统两层
  • 设备可归属某系统
  • 仪表可关联某系统作为测量对象

4.3 设备

  • 设备有设备类型
  • 设备属于位置和系统

4.4 仪表

  • 仪表有仪表类型
  • 仪表存在父子层级
  • 仪表安装在某位置
  • 仪表可以关联位置、系统、设备类型等测量对象维度

5. 第一版 MCP 工具清单

第一版只保留必要工具,不做额外封装工具。

5.1 list_locations

用途:

  • 查询位置候选项
  • 为后续正式搜索提供 location_id

建议入参:

  • project_key: str
  • keyword: str | None = None

内部调用:

  • /api/configapi/location/list

返回字段:

  • id
  • name
  • code
  • full_code
  • parent_id
  • level

备注:

  • keyword 为空时全量拉取
  • 返回后端原始树结构

5.2 list_system_tree

用途:

  • 获取系统类型和系统的树结构信息

建议入参:

  • project_key: str

内部调用:

  • /api/configapi/system/tree

返回字段:

  • 保持后端原始字段结构

5.3 list_systems

用途:

  • 获取系统列表
  • 为设备或其他查询提供 system_id

建议入参:

  • project_key: str
  • page_size: int = 100
  • page_num: int = 1
  • system_type_id: int = 0
  • show_below: bool = True

内部调用:

  • /api/configapi/system/list

返回字段:

  • id
  • name
  • code
  • system_type_id
  • system_type_name

5.4 list_device_types

用途:

  • 获取设备类型列表

建议入参:

  • project_key: str

内部调用:

  • /api/configapi/devicetype/list

返回字段:

  • id
  • name
  • code

5.5 list_meter_types

用途:

  • 获取仪表类型列表

建议入参:

  • project_key: str

内部调用:

  • /api/configapi/metertype/list

返回字段:

  • id
  • name
  • code

5.6 search_devices

用途:

  • 按正式过滤条件查询设备

建议入参:

  • project_key: str
  • page_size: int = 100
  • page_num: int = 1
  • keyword: str | None = None
  • location_id: int = 0
  • show_below: bool = True
  • system_ids: list[int] | None = None
  • device_type_ids: list[int] | None = None

内部调用:

  • /api/configapi/device/list

返回字段:

  • id
  • name
  • code
  • device_type_id
  • device_type_name
  • location_id
  • location_name
  • system_id
  • system_name
  • parent_id
  • is_group
  • location_full_code

备注:

  • keyword 为空时表示不按关键字过滤
  • 若当前页未找到目标结果,可继续传更大的 page_num

5.7 search_meters

用途:

  • 按正式过滤条件查询仪表

建议入参:

  • project_key: str
  • page_size: int = 100
  • page_num: int = 1
  • keyword: str | None = None
  • location_id: int = 0
  • show_below: bool = True
  • meter_type_id: int = 0
  • measurement_location_ids: list[int] | None = None
  • measurement_system_ids: list[int] | None = None
  • measurement_device_type_ids: list[int] | None = None
  • status: int | None = None

内部调用:

  • /api/configapi/meter/list

返回字段:

  • id
  • name
  • code
  • meter_type_id
  • meter_type_name
  • location_id
  • location_name
  • parent_id
  • parent_name
  • measurement_flag
  • measurement_device_id
  • measurement_device_name
  • measurement_location_id
  • measurement_location_name
  • measurement_system_id
  • measurement_system_name
  • measurement_device_type_id
  • measurement_device_type_name
  • status
  • meter_model_id
  • meter_model_name
  • meter_model_brand
  • code_prefix

备注:

  • keyword 为空时表示不按关键字过滤
  • 若当前页未找到目标结果,可继续传更大的 page_num
  • 第一版不对 measurement_flag 做强校验,只透传查询条件

6. MCP 调用流程建议

6.1 按位置查仪表

步骤:

  1. 调用 list_locations(keyword=...) 需要传入 project_key
  2. 从返回结果中选择目标 location_id
  3. 调用 search_meters(project_key=..., location_id=..., show_below=...)

6.2 按系统查设备

步骤:

  1. 调用 list_systems(project_key=..., ...)list_system_tree(project_key=...)
  2. 从返回结果中选择目标 system_id
  3. 调用 search_devices(project_key=..., system_ids=[...])

6.3 按设备类型和系统查仪表

步骤:

  1. 调用 list_device_types(project_key=...) 获取 device_type_id
  2. 调用 list_systems(project_key=..., ...) 获取 system_id
  3. 调用 search_meters(project_key=..., measurement_device_type_ids=[...], measurement_system_ids=[...])

7. 分页设计

第一版不做自动翻页。

约定:

  • 如果接口支持分页,则默认使用 page_size=100
  • 由调用方显式指定 page_num
  • MCP 返回当前页原始结果

适用接口:

  • location/list
  • system/list
  • device/list
  • meter/list

不涉及分页控制的接口:

  • system/tree
  • devicetype/list
  • metertype/list

8. 返回结果规范

第一版建议尽量保持后端接口的返回结构:

  • 保留 state
  • 保留 state_info
  • 保留 data
  • 不重组为统一的 items 结构

这样可以降低实现复杂度,也便于和后端接口文档直接对照。

9. 错误处理策略

第一版建议采用简单直接的错误策略。

9.1 HTTP 错误

  • 请求失败时直接抛出错误
  • 错误信息包含接口路径、状态码、响应体摘要

9.2 业务错误

  • 当返回 state != 0 时视为业务失败
  • 错误中包含 statestate_info

9.3 空结果

  • 空结果不视为错误
  • 保持后端原始返回结构,只是结果列表为空

10. FastMCP 实现建议

建议采用三层结构。

10.1 HTTP Client 层

职责:

  • 发送 HTTP 请求
  • 注入公共参数,如 operator
  • 根据 project_key 定位项目配置
  • 处理登录和 token 获取
  • 处理响应状态和业务状态
  • 透传分页参数

建议模块:

  • client.py

10.2 Service 层

职责:

  • 封装接口调用
  • 保持后端返回结构
  • 只做必要的参数组织

建议模块:

  • services/location.py
  • services/system.py
  • services/device.py
  • services/meter.py

10.3 MCP Tool 层

职责:

  • 暴露 FastMCP 工具
  • 定义工具参数和返回结果

建议模块:

  • server.py

11. 第一版暂不处理的问题

以下能力可在后续版本再补:

  • 按名称自动解析 id
  • 仪表型号列表及按型号查询
  • 本地缓存和缓存刷新机制
  • 更细的参数校验
  • 返回结果裁剪和排序策略

12. 建议的下一步

实现顺序建议如下:

  1. 搭建 FastMCP 基础服务
  2. 实现通用 HTTP Client 和分页参数透传
  3. 实现 list_locationslist_systemslist_device_typeslist_meter_types
  4. 实现 search_devicessearch_meters
  5. 增加少量联调脚本或示例调用