Add project-bound OAuth MCP queries and controlled analysis with scoped evidence, limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs. Default nearby searches to 200 meters and five results. Integrate investigation messaging/presentation, health/readiness and typed history; remove duplicate web search and fix online simulation and compressed baseline writes. Include project migrations 023-025; keep local credentials and demo seeding excluded. Validation: 319 focused pytest checks; real PostGIS temporary-table regression; read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
222 lines
9.4 KiB
Python
222 lines
9.4 KiB
Python
"""One finite query catalog; names, schemas and permissions come from this registry."""
|
||
|
||
from dataclasses import dataclass
|
||
from pydantic import BaseModel
|
||
from mcp import types
|
||
from app.mcp import models as m
|
||
from app.mcp.analysis_models import BurstDetectionInput, BurstLocationInput, ValveIsolationInput, LeakageIdentificationInput
|
||
from app.mcp.analysis_models import ScenarioInput, ContaminantSimulationInput, ValveClosureInput, FlushingInput, PressureRegulationInput, SensorPlacementInput
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class QueryTool:
|
||
name: str
|
||
description: str
|
||
model: type[BaseModel]
|
||
permission: str
|
||
semantics: str
|
||
task_only: bool = False
|
||
computation: bool = False
|
||
|
||
def protocol_tool(self) -> types.Tool:
|
||
if self.name == "run_simulation":
|
||
suffix = "。任务调用复用证据留存。"
|
||
elif self.computation:
|
||
suffix = "。仅计算建议,不保存方案、不控制设备;任务调用复用证据留存。"
|
||
elif self.name == "query_nearby_elements":
|
||
suffix = "。仅查询当前已授权项目的模型管网,按距离和元素ID稳定排序;按需分页,不自动遍历全量;空间邻近不代表管网连通或供水关系。"
|
||
else:
|
||
suffix = (
|
||
"。先限定对象和时间范围,再读取明细;集合默认20条,按需逐页扩大,"
|
||
"不要自动遍历全量;截断或非零offset不能证明完整范围无异常。"
|
||
)
|
||
return types.Tool(
|
||
name=self.name,
|
||
description=self.description + suffix,
|
||
inputSchema=self.model.model_json_schema(),
|
||
outputSchema=m.QueryResult.model_json_schema(),
|
||
annotations=types.ToolAnnotations(
|
||
readOnlyHint=False, destructiveHint=self.name == "run_simulation", openWorldHint=False
|
||
),
|
||
)
|
||
|
||
|
||
_specs = [
|
||
("run_simulation", "按水力步长在在线业务模型上执行指定时间段的实时模拟,不复制数据库;更新该时段的模型条件及实时基线,返回指定节点和管段结果;duration单位为秒,不创建方案或正式分析运行,结果不是实测", ScenarioInput, "simulation.run", "analysis", False, True),
|
||
("run_contaminant_simulation", "在隔离模型中模拟假设污染源的浓度传播;返回指定节点/管段结果,不反演污染源,不代表实测污染范围", ContaminantSimulationInput, "simulation.run", "analysis", False, True),
|
||
("analyze_valve_closure", "在隔离模型中模拟关阀的水力影响,返回指定节点/管段结果;不操作真实阀门", ValveClosureInput, "simulation.run", "analysis", False, True),
|
||
("analyze_flushing", "在隔离模型中评估指定排放节点、冲洗流量和关阀组合的水力结果;不是现场冲洗或消毒合格证明", FlushingInput, "simulation.run", "analysis", False, True),
|
||
("analyze_pressure_regulation", "在隔离模型中评估给定调泵序列和水箱初始水位的压力影响;不自动优化调度,不实际控制泵", PressureRegulationInput, "simulation.run", "analysis", False, True),
|
||
("optimize_sensor_placement", "使用灵敏度或K-means算法推荐压力监测点;仅返回候选节点,不保存正式方案,不保证污染溯源可观测性", SensorPlacementInput, "optimization.run", "analysis", False, True),
|
||
("run_leakage_identification", "基于SCADA压力与模型进行DMA漏损分配识别,需有依据的总漏损流量和时间对齐;返回区域估计,不是已确认漏点。内部执行遗传算法与水力计算", LeakageIdentificationInput, "burst.run", "analysis", False, True),
|
||
("run_burst_detection", "爆管异常侦测:使用至少5个完整压力传感器此前15天数据;只提示压力异常,不定位、不证明爆管", BurstDetectionInput, "burst.run", "analysis", False, True),
|
||
("run_burst_location", "爆管候选定位:对比异常与正常压力,可辅助流量;需有依据的漏水量假设。压力节点可省略,自动读取项目SCADA压力测点;monitoring正常窗可成对省略,默认前一天同时间段,须说明该对照假设。simulation须有已有分析运行UUID并读取同窗模型基线,不是实测;定位内部会执行水力计算,不创建新的事件方案", BurstLocationInput, "burst.run", "analysis", False, True),
|
||
("analyze_valve_isolation", "按事故候选要素分析拓扑隔离边界、建议关闭阀门和受影响节点;不是污染扩散或水力影响范围,不执行关阀,须现场核实", ValveIsolationInput, "simulation.run", "analysis", False, True),
|
||
(
|
||
"get_element_properties",
|
||
"读取当前项目的单个管网元素属性",
|
||
m.ElementInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"list_elements",
|
||
"分页读取管网元素,不自动读取全网",
|
||
m.ElementPageInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"query_nearby_elements",
|
||
"查询坐标附近的模型元素,默认pipe;center为EPSG:4326 [经度,纬度],可直接使用geocode.center;radius_m默认200米、最大10000米,limit默认5、最大100,支持offset。按点到完整几何的WGS84椭球最短距离筛选并排序,返回element_id、distance_m和属性;不查询原始GIS管线、不自动扩大半径、不解析道路交叉点",
|
||
m.NearbyElementsInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"get_network_options",
|
||
"读取模型时间、能源或网络选项",
|
||
m.OptionsInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"get_network_schema",
|
||
"读取当前支持的元素、SCADA和选项结构",
|
||
m.SchemaInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"list_scada_devices",
|
||
"分页读取SCADA设备和物理单位",
|
||
m.PageInput,
|
||
"scada.view",
|
||
"asset",
|
||
),
|
||
("get_scada_device", "读取一个SCADA设备", m.DeviceInput, "scada.view", "asset"),
|
||
(
|
||
"query_scada_readings",
|
||
"查询带时区且最多24小时的原始或清洗监测数据",
|
||
m.ScadaInput,
|
||
"scada.view",
|
||
"observed",
|
||
),
|
||
(
|
||
"query_model_baseline_series",
|
||
"读取最多20个元素的模型基线,不能称为实测",
|
||
m.BaselineSeriesInput,
|
||
"simulation.view",
|
||
"model_baseline",
|
||
),
|
||
(
|
||
"get_model_baseline_at_time",
|
||
"读取单个元素指定时刻的模型基线",
|
||
m.BaselineAtTimeInput,
|
||
"simulation.view",
|
||
"model_baseline",
|
||
),
|
||
(
|
||
"query_model_baseline_field",
|
||
"分页读取指定时刻的模型字段",
|
||
m.BaselineFieldInput,
|
||
"simulation.view",
|
||
"model_baseline",
|
||
),
|
||
(
|
||
"query_analysis_series",
|
||
"读取指定分析运行和元素字段时序",
|
||
m.AnalysisSeriesInput,
|
||
"simulation.view",
|
||
"analysis",
|
||
),
|
||
(
|
||
"query_analysis_values",
|
||
"分页读取指定分析运行某时刻的元素字段",
|
||
m.AnalysisValuesInput,
|
||
"simulation.view",
|
||
"analysis",
|
||
),
|
||
(
|
||
"list_analysis_runs",
|
||
"分页读取持久分析运行",
|
||
m.RunsInput,
|
||
"webgis.view",
|
||
"analysis",
|
||
),
|
||
("get_analysis_run", "读取单个持久分析运行", m.RunInput, "webgis.view", "analysis"),
|
||
(
|
||
"list_analysis_results",
|
||
"分页读取分析成果,可筛选result_type",
|
||
m.ResultsInput,
|
||
"webgis.view",
|
||
"analysis",
|
||
),
|
||
(
|
||
"list_sensor_placement_runs",
|
||
"分页读取已有监测点选址运行",
|
||
m.PageInput,
|
||
"optimization.run",
|
||
"analysis",
|
||
),
|
||
(
|
||
"get_sensor_placement_run",
|
||
"读取已有监测点选址成果",
|
||
m.RunInput,
|
||
"optimization.run",
|
||
"analysis",
|
||
),
|
||
(
|
||
"query_element_history",
|
||
"比较实测与模型基线或分析结果;analysis_comparison要求run_id;limit/offset按序列、时间排序的数据点分页",
|
||
m.HistoryInput,
|
||
"scada.view",
|
||
"observed",
|
||
),
|
||
(
|
||
"get_element_gis_properties",
|
||
"读取元素关联GIS台账",
|
||
m.ElementInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"get_source_gis_properties",
|
||
"读取指定图层源要素台账",
|
||
m.SourceGisInput,
|
||
"webgis.view",
|
||
"asset",
|
||
),
|
||
(
|
||
"get_workbench_runtime",
|
||
"读取工作台已有运行态",
|
||
m.StrictInput,
|
||
"simulation.view",
|
||
"task",
|
||
),
|
||
(
|
||
"get_task_context",
|
||
"读取绑定任务的有界摘要及handling_skill元数据;section=skill读取Server按任务workflow分发的业务处置Skill正文与版本,属于流程指引而非事件证据;其他section选择task/report/evidence/actions/progress/access_requests/plan/run,data_path按返回路径读取嵌套字段,limit/offset分页;report_revision保留选定报告版本",
|
||
m.TaskContextInput,
|
||
"operational_task.view",
|
||
"task",
|
||
True,
|
||
),
|
||
(
|
||
"query_peripheral_data",
|
||
"查询有限外围数据源;synthetic是合成证据",
|
||
m.PeripheralInput,
|
||
"simulation.view",
|
||
"peripheral",
|
||
),
|
||
(
|
||
"get_task_evidence",
|
||
"读取当前绑定任务已保存的证据快照",
|
||
m.EvidenceInput,
|
||
"operational_task.view",
|
||
"task",
|
||
True,
|
||
),
|
||
]
|
||
TOOLS = {row[0]: QueryTool(*row) for row in _specs}
|