"""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 import calculation_models as jobs from app.mcp import extended_models as e from app.mcp.analysis_models import BurstDetectionInput, BurstLocationInput, ValveIsolationInput, LeakageIdentificationInput, NodeCoverageInput 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 mutating: bool = False def protocol_tool(self) -> types.Tool: if self.mutating: suffix = "。默认dry_run只预览;执行必须携带预览摘要、相同request_id并通过宿主原生许可。由项目成员的普通聊天执行写入;失败或超时先核实状态,再按原请求接续。" elif self.name == "run_simulation": suffix = "。任务调用复用证据留存。" elif self.computation: suffix = "。返回分析建议并留存任务证据;正式方案与现场操作按各自业务流程执行。" elif self.name == "query_nearby_elements": suffix = "。仅查询当前已授权项目的模型管网,按距离和元素ID稳定排序;按核查目标分页;连接与供水关系由拓扑分析核验。" elif self.name == "list_scada_devices": suffix = ( "。默认每页100个,最多500个;page.has_more=true时继续递增offset读取后续页面。" "大目录内联保留本页全部设备的编号、类型和单位,模型映射及坐标用get_scada_device核验。" ) else: suffix = ( "。先限定对象和时间范围,再读取明细;集合默认20条,按需逐页扩大," "按当前目标读取必要页面,并说明截断和分页覆盖范围。" ) return types.Tool( name=self.name, description=self.description + suffix + ( "。返回queued/running和job_id时,使用get_calculation_job查询状态、get_calculation_result读取成功结果,排队回执用于跟踪;直接返回分析结果时立即读取。" if self.name in {"run_burst_location", "run_leakage_identification", "run_burst_detection", "run_simulation", "run_burst_simulation", "run_contaminant_simulation", "analyze_valve_closure", "analyze_flushing", "analyze_pressure_regulation", "optimize_sensor_placement", "save_simulation_scenario", "create_sensor_placement"} else "" ), inputSchema=self.model.model_json_schema(), outputSchema=m.QueryResult.model_json_schema(), annotations=types.ToolAnnotations( readOnlyHint=not (self.computation or self.mutating or self.name == "cancel_calculation_job"), destructiveHint=self.mutating or self.name == "run_simulation", openWorldHint=self.name in {"search_project_places", "reverse_geocode"} ), ) _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", "爆管候选定位:对比异常与正常压力,可辅助流量;依据现有异常形成候选,水力算法使用有依据的burst_leakage作为输入。任务上下文location_demo.available=true时可省略漏量,Server直接返回候选管段与相似度,随后继续地图展示、隔离边界与影响分析。压力节点可省略,自动读取项目配置;monitoring正常窗省略时默认前一天同时间段,须说明假设;回放必须显式提供冻结正常窗。simulation须有已有分析运行UUID及同窗模型基线,结果标注模型来源", BurstLocationInput, "burst.run", "analysis", False, True), ("analyze_valve_isolation", "按事故候选要素分析拓扑隔离边界、建议关闭阀门和受影响节点;候选分析无需等待现场确诊。输出为拓扑隔离与供水失源范围;水力影响另用关阀模拟评估,现场操作前核实阀门映射及可用性", ValveIsolationInput, "simulation.run", "analysis", False, True), ("generate_node_coverage", "为当前项目已核实的模型节点集合生成空间覆盖示意,适用于候选分布、监测覆盖比较、区域成员、隔离或模拟结果节点等,支持各类项目分析。支持最多8组、合计10000个唯一节点,从真实GIS查询坐标;监测设备、投诉或地址须先关联模型节点。可按连接分组,记录凹包/缓冲参数、缺失节点和display_only;结果为节点空间覆盖示意,保留分组及几何覆盖范围。返回result.map_payload及coverage,任务中用set_overlay引用本次evidence_id、省略data展示;地图通过展示工具发布", NodeCoverageInput, "webgis.view", "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.ScadaDeviceListInput, "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", "查询外围业务工单及监测数据;外围工单属于系统数据,监测数据按实际来源保留来源信息", m.PeripheralInput, "simulation.view", "peripheral", ), ( "get_task_evidence", "读取当前绑定任务已保存的证据快照", m.EvidenceInput, "operational_task.view", "task", True, ), ] _specs += [ ("list_regions", "分页查询当前项目区域/DMA列表", m.PageInput, "webgis.view", "asset"), ("get_region", "查询当前项目区域边界,返回EPSG:4326几何;供水关系按拓扑核验", e.RegionInput, "webgis.view", "asset"), ("get_region_nodes", "分页查询区域配置的成员节点,未配置时使用区域几何筛选", e.RegionInput, "webgis.view", "asset"), ("get_node_connections", "按目标节点分页查询直接连接的管线", e.NodeConnectionsInput, "webgis.view", "asset"), ("query_network_extent", "按EPSG:4326矩形范围分页查询当前项目模型几何;GIS缺失时返回当前项目的具体缺口", e.ExtentInput, "webgis.view", "asset"), ("list_major_nodes", "按关联管道最小管径查询骨干节点,经纬度EPSG:4326", e.MajorNodesInput, "webgis.view", "asset"), ("get_model_configuration", "读取模式、曲线、控制规则、需水量、水质及反应等模型配置;集合按页返回", e.ModelConfigurationInput, "webgis.view", "asset"), ("list_model_configurations", "分页读取模式或曲线ID", e.ConfigurationListInput, "webgis.view", "asset"), ("search_project_places", "按项目配置的行政区搜索地名,最多10个候选,依赖天地图配置", e.PlaceSearchInput, "webgis.view", "asset"), ("reverse_geocode", "把EPSG:4326地图取点转为项目范围内地名,保留输入坐标", e.ReverseGeocodeInput, "webgis.view", "asset"), ("get_project_info", "读取当前项目概况与地图范围,公开字段限于项目业务概况", m.StrictInput, "webgis.view", "asset"), ("export_project_model", "从当前项目数据库导出INP或ChangeSet压缩文件,由宿主保存到会话文件", e.ProjectExportInput, "webgis.view", "asset"), ("get_sensor_placement_candidate", "读取监测点候选节点工程属性", e.SensorCandidateInput, "optimization.run", "asset"), ("export_sensor_placement", "导出已有监测点方案的Excel工程清单,由宿主保存到会话文件", e.SensorExportInput, "optimization.run", "analysis"), ("list_task_schedules", "分页读取当前项目定时任务配置,执行状态由调度系统管理", m.PageInput, "operational_task.view", "task"), ("get_task_run_history", "分页读取指定项目任务的运行历史", e.TaskHistoryInput, "operational_task.view", "task"), ("get_mutation_status", "读取当前身份和会话内的写入执行状态;started只表示已启动,超时后先核实目标数据,再按原请求接续", e.MutationStatusInput, "webgis.view", "task"), ("calculate_demand_allocation", "按管长权重分配总需水量到节点、区域或全网,返回分配建议,模型变更通过业务写入执行,按页返回", e.DemandAllocationInput, "webgis.view", "analysis", False, True), ("run_burst_simulation", "在隔离模型中模拟假设管道破口面积的水力影响;返回给定情景的模拟结果,正式方案通过方案工具保存", e.BurstSimulationInput, "simulation.run", "analysis", False, True), ("edit_model_element", "预览或增删改模型节点、管段、泵阀、水池和区域;变更范围为业务模型,现场设备操作按对应流程执行", e.ModelElementMutationInput, "webgis.edit", "asset", False, False, True), ("edit_model_configuration", "预览或修改模型需水量、模式、曲线、控制规则、水质及模拟选项", e.ConfigurationMutationInput, "webgis.edit", "asset", False, False, True), ("clean_scada_readings", "预览或清洗明确设备与24小时内时间窗的数据,写入cleaned_value", e.ScadaCleaningInput, "scada.clean", "observed", False, False, True), ("write_scada_readings", "预览或补录/修正SCADA数据,要求真实来源依据;观测来源按真实记录核验", e.ScadaWriteInput, "scada.clean", "observed", False, False, True), ("delete_scada_readings", "预览或删除明确设备与24小时内时间窗的SCADA记录", e.ScadaDeleteInput, "scada.clean", "observed", False, False, True), ("save_simulation_scenario", "预览或重新计算并保存正式隔离场景成果;有真实计算开销,观测数据按真实来源记录", e.SavedScenarioInput, "simulation.run", "analysis", False, False, True), ("create_sensor_placement", "预览或计算并保存正式压力监测点选址方案", e.SensorCreateInput, "optimization.run", "analysis", False, False, True), ("update_sensor_placement", "预览或修改本人有权编辑的监测点方案,保留旧点位乐观校验", e.SensorUpdateInput, "optimization.run", "analysis", False, False, True), ("create_operational_task", "预览或创建项目任务,Agent运行终态由执行系统管理", e.TaskCreateInput, "operational_task.write", "task", False, False, True), ("create_task_schedule", "预览或创建定时任务配置", e.ScheduleCreateInput, "operational_task.write", "task", False, False, True), ("update_task_schedule", "预览或启停定时任务配置,校验版本", e.ScheduleUpdateInput, "operational_task.write", "task", False, False, True), ("retry_operational_task", "预览或重新入队已终止任务;后续会话由任务调度系统创建", e.TaskRetryInput, "operational_task.write", "task", False, False, True), ("cancel_task_run", "预览或取消指定任务运行,校验运行版本", e.TaskCancelInput, "operational_task.write", "task", False, False, True), ] _specs += [ ("get_calculation_job", "读取当前身份与会话的持久计算状态;排队回执用于跟踪,分析依据读取成功结果", jobs.CalculationJobInput, "webgis.view", "task"), ("get_calculation_result", "读取成功计算的原始结果;data_path选择嵌套集合,limit/offset按需分页", jobs.CalculationResultInput, "webgis.view", "task"), ("cancel_calculation_job", "请求取消本人当前会话的计算;运行中取消由独立计算进程处理,须继续核实最终状态", jobs.CalculationJobInput, "webgis.view", "task"), ("list_calculation_jobs", "分页读取本人当前会话及任务范围的计算状态", jobs.CalculationJobsInput, "webgis.view", "task"), ] TOOLS = {row[0]: QueryTool(*row) for row in _specs}