# 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_locations`、`find_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_url`、`username`、`password` 3. 先读取当前项目的 token 缓存 4. 如缓存不存在或已过期,则调用登录接口重新获取 token 5. 使用获取到的 token 访问配置接口 登录接口: - `POST {base_url}/api/ai/auth/password_login` 登录成功条件: - 返回 JSON - `errcode` 为 `0` 或 `0.0` - 响应中包含 `token` 业务请求认证方式: - 请求头使用 `Authorization` - header 的值直接使用 token 原文 - 不额外拼接 `Bearer ` 前缀 token 缓存策略: - 按 `project_key` 单独缓存 - 缓存内容存储在 `sys_config` - 当 `expire_at` 仍未过期时直接复用 - 当缓存失效时重新登录并回写缓存 ## 3.1 位置搜索 接口:`POST /api/configapi/location/list` 用途: - 按关键字搜索位置 - `keyword` 为空时可全量拉取 输入特点: - 支持分页 - 返回树结构 输出特点: - 节点包含 `children` - 节点包含 `id`、`code`、`full_code`、`name`、`parent_id`、`level` 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_id`、`parent_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` 全量返回 - 供仪表搜索使用 ## 3.8 拓扑详情与节点数据 接口: - `POST /api/configapi/topo/get` - `POST /api/configapi/topo/get_data` 用途: - `topo/get`:读取单张拓扑结构、`data_options`、`dimension_config` - `topo/get_data`:读取单张拓扑在某个展示维度和单个时间点下的节点数据 关键约束: - `topo/get_data` 一次只支持一个拓扑、一个 `display`、一个时间点 - `display=instant` 不带时间参数 - `display=accu` 需要显式传 `accu_step` 和 `ts` - 不能直接返回时间范围数据,需要由 MCP 层做多次调用聚合 当前 MCP 处理: - `topology.get_node` 查询时实时调用 1 次 `instant` - 再调用 12 次小时累计和 7 次日累计 - 节点值按 `node_id` 直接匹配 ## 3.9 拓扑分组配置 来源字段: - `topo/get` 返回中的 `dimension_config` 适用范围: - 仅 `topology_type=2` 的拓扑图 结构特点: - `dimension_config.dimensions` 表示分组配置 - `dimension_config.filter` 表示筛选配置 - `filter.conditions[].fields` 存储业务对象 ID 列表,需要额外解析名称 枚举语义: - `order`: `1=顺序`,`2=逆序` - `filter_type`: `1=所有`,`2=任一` - `match_type`: `1=等于`,`2=不等于`,`3=包含`,`4=不包含` 对象类型语义: - `11=位置` - `12=系统类型` - `13=系统` - `14=设备类型` - `15=设备` - `16=仪表类型` - `17=仪表型号` - `18=仪表` - `19=拓扑图分组` - `20=拓扑图` 名称解析来源: - `11` -> `list_locations` - `12` -> `list_system_tree` - `13` -> `list_systems` - `14` -> `list_device_types` - `15` -> `search_devices` - `16` -> `list_meter_types` - `18` -> `search_meters` - `19` -> 本地拓扑分组缓存 - `20` -> 本地拓扑注册缓存 当前限制: - `17`(仪表型号)暂未实现名称解析,需要后续补接口或规则 ## 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` 做强校验,只透传查询条件 ## 5.8 `topology.group_list` 用途: - 返回缓存中的拓扑分组树 建议入参: - `project_key: str` ## 5.9 `topology.list` 用途: - 返回缓存中的拓扑列表 - 可按分组或对象类型过滤 建议入参: - `project_key: str` - `group_id: int | None = None` - `object_type_code: int | None = None` ## 5.10 `topology.get_node` 用途: - 获取一个节点及其直接邻域 - 同时返回当前拓扑的节点实时值和累计值窗口 建议入参: - `project_key: str` - `topology_id: int` - `node_id: str = 'root'` - `include_siblings: bool = True` - `include_children: bool = True` 返回结构: - `data_window` - `topology` - `node` - `parents` - `children` - `siblings` 说明: - `topology` 中包含 `data_options`、`metric_definitions`、`dimension_config` - `node.data.instant` 为实时值 - `node.data.accu.hourly` 为最近 12 个整点小时累计值 - `node.data.accu.daily` 为最近 7 个日零点累计值 ## 5.11 `topology.get_group_config` 用途: - 返回 type=2 拓扑图的分组配置和筛选配置 建议入参: - `project_key: str` - `topology_id: int` 返回结构: - `supported` - `raw_dimension_config` - `groupings` - `filter` 说明: - `groupings` 由 `dimension_config.dimensions` 派生 - `filter` 由 `dimension_config.filter` 派生 - `filter.conditions[].field_items` 为解析后的 `id + name` ## 5.12 `topology.find_context` 用途: - 按实体快速反查命中的拓扑节点上下文 建议入参: - `project_key: str` - `entity_type: str` - `entity_id: int` - `topology_id: int | None = None` - `include_siblings: bool = True` - `ancestor_depth: int = 5` - `descendant_depth: int = 2` ## 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` 时视为业务失败 - 错误中包含 `state` 和 `state_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_locations`、`list_systems`、`list_device_types`、`list_meter_types` 4. 实现 `search_devices` 和 `search_meters` 5. 增加少量联调脚本或示例调用