Files
fire-safety-ymd/docs/specs/fire-safety-ymd-superagent-mcp-spatial-readonly-v1.md
T
2026-09-05 19:33:45 +08:00

17 KiB
Raw Blame History

森林防火空间只读 MCP v1 Spec

项 内容
状态 Implemented;live readiness 与本地 7 tools/call 已通过;同一 SuperAgent 中 th-hotel-simple-superagent 已稳定启用/调用并作为兼容档案参照;公网 /mcp 已到达 Go,但旧版本门禁曾返回错误,消防服务按 proven-profile 兼容方式部署后仍需完成真实工具回调
日期 2026-09-05
Checkpoint fire-safety-ymd-superagent-mcp-proven-profile-compatibility(基础空间工具契约沿用 v1)
需求来源 用户提供 8 张 PostgreSQL/PostGIS 表结构与每表 2 条样例,要求先基于现有数据建设 MCP

1. 背景与已核对输入

docs/import/db-samples/ 中包含以下表的结构和样例:

  • st_2_mpslfh_t_slfh_syd:水源地,点。
  • st_2_xianyouxushuichiguan:蓄水池,点。
  • st_2_xianyoufanghuotongdao:防火通道,线。
  • st_2_fanghuojianchazhan:防火检查站,点。
  • st_2_fanghuoliaowangshao:防火瞭望哨,点。
  • st_2_fanghuowangge:防火网格,面。
  • st_2_linqugongkuangqiye:林区工矿企业,面。
  • st_2_mudifenqu_mian:墓地坟区,面。

SQL 文件仅作为结构和数据契约参考。文件包含 DROP TABLE 等导出语句,本项目不会执行这些文件。

当前样例足以确定首版字段映射,但不能证明全库的数据完整性、坐标系、几何类型一致性、索引、时效或资源当前可用性。尤其是:

  • 所有 geom 列均声明为宽泛的 geometry(GEOMETRY),没有 typmod SRID。
  • 现场全量导入进一步确认:st_2_xianyoufanghuotongdao.geom 的源记录混合二维与 Z 维度,原 geometry(GEOMETRY) 二维 typmod 会拒绝 Z;用户改用裸 geometry 保留原始维度后报告重导成功。该导入反馈仍需只读 probe 核验最终数量和分布。
  • 样例几何十六进制是未携带 SRID 的 WKB;坐标数值及水源表独立经纬度字段看起来符合 WGS84,但这只能作为待验证线索。
  • 样例 DDL 只有防火网格明确包含 GiST 几何索引。
  • 多张表含姓名、电话等受限字段;首版 MCP 不返回这些字段。
  • 用户提供的 SuperAgent 现场截图显示公网 /mcp 请求已经到达 Go 服务,Bearer、Content-Type 和 JSON-RPC 前置校验通过,随后旧版本门禁返回错误;截图没有捕获客户端版本字段是否存在、类型和值。同一 SuperAgent 中 th-hotel-simple-superagent 已稳定启用/调用,这是兼容档案的现场参照,但消防 MCP 尚未完成公网数据库工具调用。

2. 目标

  • 在现有 Go HTTP 服务内提供默认关闭的 POST /mcp。
  • 与不能配置协议版本的 SuperAgent 客户端保持兼容:服务端固定返回 MCP 2025-06-18,对齐已稳定接通的 th-hotel-simple-superagent compatibility profile;initialize 中的版本字段和后续协议 Header 都不作为拒绝门禁。使用 JSON-RPC 2.0 和单请求 JSON 响应,目标是打通工具调用而不是追求最新协议。
  • 通过独立 Bearer Token 鉴权;不得复用 SuperAgent Open API Key。
  • 只提供固定、参数化、只读、按服务端可信数据范围执行的空间查询工具;默认使用镇街白名单,数据库全范围必须显式启用。
  • 支持在现有业务记录中按地名搜索有界候选,让没有坐标的用户先确认一个候选,再进入距离和包含关系查询。
  • 使用 PostgreSQL/PostGIS 作为事实来源,并在启用 MCP 前校验 PostGIS、表、SRID、几何类型和有效性。
  • 工具结果明确表达数据来源、生成时间、限制与需要现场确认的事项。
  • 提供一个只输出 schema/几何统计、不输出业务记录或联系人数据的 PostGIS readiness probe。

3. 非目标

  • 不开放任意 SQL、任意表查询、文件读取或通用 HTTP 代理。
  • 不写数据库,不调度人员或资源,不生成/下发指挥命令。
  • 不声称防火通道是可通行路线,不实现路网拓扑、最短路、坡度或实时封路计算。
  • 不声称网格队伍的实时位置或集结点;现有表只提供责任队伍和区域信息。
  • 不依据资源记录存在就宣称水源、检查站或瞭望哨当前可用。
  • 不返回负责人、队长、值班人员、书记、联系电话或图片地址。
  • 不提供任意地址、山名或道路的外部地理编码,不实现完整地名库、拼音/别名纠错,也不自动选定地名候选。
  • 不在本 checkpoint 内建立最终用户身份、动态角色或多租户模型;首版使用每环境 MCP 凭证和服务端静态数据库全范围/镇街白名单。

4. MCP 传输与安全契约

4.1 支持的方法

  • initialize
  • notifications/initialized
  • tools/list
  • tools/call

服务端固定返回协议版本标识 2025-06-18 和 tools capability。首版是无会话、非流式的 Streamable HTTP 子集:POST 返回 application/json;GET /mcp 返回 405 Method Not Allowed。

SuperAgent compatibility profile

服务端固定返回 result.protocolVersion=2025-06-18,但不把客户端版本字段当作版本协商或拒绝依据:可解析的 JSON-RPC initialize 中,params.protocolVersion 可以缺失、为空、为非字符串或为其他值,均不单独触发版本错误。MCP-Protocol-Version Header 可以缺失或携带任意值,也不作为版本拒绝门禁;SuperAgent 配置中不需要填写版本字段或 Header。

固定返回 2025-06-18 不表示服务实现或声明支持客户端填写的任意版本,也不把 2025-03-26、2025-11-25 等值列为实现目标。该 profile 只为兼容已稳定接通的 SuperAgent 客户端,版本字段/Header 之外的 HTTP 方法、JSON-RPC 结构、鉴权、Origin、Content-Type、工具 schema、只读数据范围和敏感字段边界仍严格执行。

initialize 日志只记录 direct_success(客户端值为 2025-06-18)或 compatibility_success(版本字段缺失或其他值被兼容处理),不记录客户端原始版本值。

4.2 入站控制

  • MCP 默认关闭,关闭时不注册 /mcp。
  • 只接受 Content-Type: application/json。
  • 使用 Authorization: Bearer <FIRE_SAFETY_MCP_AUTH_TOKEN>,常量时间比较。
  • Token 至少 32 个可打印 ASCII 字符,且不能等于 SuperAgent Open API Key。
  • 初始化响应固定返回 protocolVersion: 2025-06-18;请求 body 中的 protocolVersion 以及 MCP-Protocol-Version Header 均为兼容元数据,不作为拒绝门禁。SuperAgent 配置不需要增加版本字段或 Header。
  • 请求体默认最大 256 KiB,最大可配置 1 MiB。
  • 首版只接受服务到服务请求;带非空 Origin 的请求拒绝,避免浏览器和 DNS rebinding 风险。
  • 不接受模型提供的用户、角色、租户或授权范围。范围模式和可选镇街列表只来自服务端配置。
  • 每次工具调用有硬超时;日志只记录 request ID、方法/工具名、耗时和结果类别,不记录参数、坐标、Token、SQL 或结果正文。

4.3 服务端数据范围

  • FIRE_SAFETY_MCP_SCOPE_MODE=town_allowlist 是默认值;启用 MCP 时要求 FIRE_SAFETY_MCP_ALLOWED_TOWNS 非空,并按数据库镇街字段精确过滤。
  • FIRE_SAFETY_MCP_SCOPE_MODE=all 必须显式配置;它授权查询当前配置数据库中 MCP 固定查询表的全部镇街以及镇街字段为空的记录,此时 FIRE_SAFETY_MCP_ALLOWED_TOWNS 必须为空。
  • all 不会放宽只读、固定 SQL、空间半径、结果数量、字段脱敏、SRID 或 readiness 限制。
  • 业务表中的镇街值不能自动决定授权范围;范围只能由可信服务端配置建立。

5. MCP 工具

除地名候选工具外,所有工具的坐标参数使用 WGS84:longitude 范围 [-180, 180],latitude 范围 [-90, 90]。需要距离的工具接收 radius_meters 和 limit,同时有工具级默认值和硬上限。

5.1 fire_safety_search_place_candidates

输入 place_name(去除首尾空白后 2 至 100 字符)和可选 limit(默认 10、最大 20),在 8 张现有业务表的名称、镇街和村庄字段中做不区分大小写的文字包含匹配。百分号和下划线按普通文字处理,不作为 SQL 通配符。

返回稳定资源类型、记录 ID、名称、镇街、村庄、命中字段、exact | partial、WGS84 坐标和 location_kind:

  • 点记录返回 recorded_point。
  • 线记录返回首个组成线的起点,面记录返回 ST_PointOnSurface 代表点,两者标记为 representative_point。

候选不等于用户位置。无结果时 Agent 必须请用户补充名称或地图选点;多结果时必须请用户选择;即使只有一个候选,也要先回显确认,不能直接调用后续距离工具。该工具不返回联系人字段,不调用外部地图服务。

5.2 fire_safety_resolve_incident_context

输入演练点坐标,查询覆盖该点的防火网格,返回网格 ID、镇街、区域标签。用于建立后续查询上下文,不返回个人信息。

5.3 fire_safety_find_nearby_water_sources

合并水源地和蓄水池,按球面距离返回候选水源。返回名称/标签、类型、镇街、村、坐标、距离、容量(数据存在时)、源表报告状态和未经解释的源时间原值(数据存在时)。结果固定标记“当前可用性未验证”;时间原值的单位和时区待数据所有者确认。

5.4 fire_safety_find_command_post_candidates

合并防火检查站和防火瞭望哨,返回附近候选设施、坐标、距离及源表报告状态。结果只代表空间候选,必须由现场核验安全、通信、容量、可达性和火势上风向等条件。

5.5 fire_safety_list_nearby_access_lines

返回附近防火通道名称、按 WGS84 geometry 计算的米制长度、最近接入点和未经解释的源更新时间原值。该工具不称为 route planner;它不返回“推荐路线”或“可通行”结论。

5.6 fire_safety_get_responsible_units

根据坐标查询覆盖网格及其防火中队名称。明确返回 live_location_available=false 和 assembly_site_available=false,因为当前表没有队伍实时位置、战备状态或正式集结点字段。

5.7 fire_safety_find_nearby_risk_areas

合并墓地坟区和林区工矿企业,返回点位周边风险区域类别、名称/标签、镇街、村、方位、是否覆盖演练点和距离。联系人字段不返回。

6. 统一结果语义

工具成功结果使用:

{
  "status": "ok | no_results",
  "data": {},
  "metadata": {
    "generated_at": "RFC3339 UTC",
    "data_sources": [],
    "spatial_reference": "EPSG:4326",
    "result_count": 0
  },
  "warnings": []
}

structuredContent 保存上述对象,同时在 content[0].text 返回同一对象的 JSON 文本,兼容只读取文本内容的 MCP 客户端。

data_sources 只使用稳定领域名称(如 water_source、fire_grid),不向模型暴露历史物理表名。

warnings 使用 results_limited_to_server_authorized_towns 表示镇街白名单模式,使用 results_include_all_towns_in_configured_database 表示显式数据库全范围模式,避免 Agent 混淆结果范围。所有工具同时返回 source_records_with_invalid_geometries_are_excluded,防止 Agent 把排除后的无结果解释成数据库确认不存在相关记录。

工具级失败仍使用 JSON-RPC 成功响应中的 isError=true,结构化错误只公开稳定错误码:

  • INVALID_ARGUMENT
  • TOOL_NOT_FOUND
  • DATA_SOURCE_UNAVAILABLE
  • QUERY_TIMEOUT
  • INTERNAL_ERROR

数据库 DSN、SQL、表结构细节和原始驱动错误不得进入 MCP 响应。

7. PostgreSQL/PostGIS 契约

  • 使用 pgx/v5 原生连接池;当前仅面向 PostgreSQL,不引入 ORM。
  • 连接设置 default_transaction_read_only=on、statement_timeout 和应用名。
  • SQL 固定在 Repository,所有值使用 $n 参数;只有代码内表白名单可参与标识符拼接。
  • 查询始终应用服务端范围、半径和返回数量上限;town_allowlist 使用参数化镇街数组,all 使用服务端布尔参数显式跳过镇街条件,模型不能提供该参数。
  • 地名查询使用参数化的文字包含匹配,先按精确名称、精确村庄、精确镇街,再按前缀和普通包含排序;不拼接用户输入。线面候选的二维代表点只在只读查询中计算,不修改原始几何。
  • MCP v1 只支持已实库确认的 EPSG:4326。启用 MCP 时 FIRE_SAFETY_POSTGIS_EXPECTED_SRID 必须显式配置为 4326。
  • readiness 校验要求每张已用表存在、PostGIS 可用、非空几何的 SRID 均为 4326、类型符合点/线/面预期且坐标不越过 WGS84 范围;表/SRID/类型/越界不符合时服务拒绝启用 MCP。
  • 无效几何、空几何、空表或缺少空间索引作为 readiness warning。固定查询使用 ST_IsValid 排除无效几何,不自动调用 ST_MakeValid,不修改原始事实;生产前应评估数据缺口并补齐查询表达式适用的 GiST 索引。
  • 原始防火通道列保持裸 geometry 以容纳混合二维/Z。不得用 ST_Force2D 或重写 WKB 修改原始数据;确需二维投影时仅在只读查询/派生层显式执行并测试其结果语义。
  • 不自动执行样例 SQL、SRID 修复或索引 DDL。

8. 配置

环境变量 默认值 启用时要求
FIRE_SAFETY_MCP_ENABLED false 显式为 true
FIRE_SAFETY_MCP_AUTH_TOKEN 空 必填,独立高熵 Token
FIRE_SAFETY_MCP_SCOPE_MODE town_allowlist town_allowlist 或 all
FIRE_SAFETY_MCP_ALLOWED_TOWNS 空 town_allowlist 时必填;all 时必须为空
FIRE_SAFETY_MCP_MAX_BODY_BYTES 262144 1..1048576
FIRE_SAFETY_MCP_TOOL_TIMEOUT 5s >0 且不超过 30s
FIRE_SAFETY_POSTGIS_ENABLED false MCP 启用时必须为 true
FIRE_SAFETY_POSTGIS_DSN 空 PostGIS 启用时必填;Secret
FIRE_SAFETY_POSTGIS_EXPECTED_SRID 空/0 MCP 启用时必须显式为 4326
FIRE_SAFETY_POSTGIS_CONNECT_TIMEOUT 5s >0 且不超过 30s
FIRE_SAFETY_POSTGIS_QUERY_TIMEOUT 3s >0 且不超过 30s
FIRE_SAFETY_POSTGIS_MAX_CONNS 4 1..20

9. 验收标准

  • MCP 关闭时 /mcp 不暴露,健康检查保持可用。
  • MCP 启用但 Token、合法范围、PostGIS 或显式 SRID 缺失时配置加载失败;all 与非空镇街列表同时出现时失败,错误不含 Secret。
  • 无 Token、错误 Token、错误 Content-Type、非空 Origin、超大 body 和非法 JSON 均被稳定拒绝。
  • initialize、initialized notification、tools/list 和 7 个 tools/call 契约通过本地测试。
  • initialize 对客户端版本字段缺失、空值、非字符串、2025-06-18 或其他值均固定返回 2025-06-18;版本字段和 MCP-Protocol-Version Header 不作为拒绝门禁。服务端不宣称支持任意其他版本,也不追求最新版本;日志按 direct_success/compatibility_success 分类且不包含原始版本值。
  • 真实 SuperAgent 验收必须在部署后依次出现 initialize → notifications/initialized(HTTP 202)→ tools/list(7 个固定工具)→ 至少一个受控只读 tools/call;只有 initialize 成功不能宣称 MCP 已接通。
  • 工具 schema 限制地名长度、经纬度、半径、数量和未知字段;工具不接受授权身份参数。
  • Service 测试证明可信数据库全范围/镇街白名单来自构造时配置,并覆盖互斥校验、无结果、超时和仓储失败。
  • Repository 只使用参数化值,MCP 结果不包含联系人字段。
  • readiness 测试证明无效几何不阻塞启动、会产生排除 warning,且全部固定查询显式包含 ST_IsValid;SRID、类型和越界仍为硬失败。
  • 地名 Service/Repository 测试覆盖首尾空白、长度、控制字符、结果上限、服务端范围、8 张固定表和候选确认 warning。
  • readiness probe 不输出业务行、电话、负责人、DSN 或 SQL。
  • gofmt、go test -count=1 ./...、go test -race -count=1 ./... 和 go vet ./... 通过。

10. 上线前未确认项

  • 实库源 CRS 已由数据提供方确认为 EPSG:4326 且无偏移;2026-09-05 已通过单独审核、显式确认和单事务迁移为 4,048 条非空几何补齐 SRID 4326,严格 readiness 已通过。重新导入无 SRID 的原始 SQL 时仍必须重新经过该流程,应用查询不得静默赋值。
  • 实库 35 条无效面几何按首版决策排除;需评估由此造成的网格、责任单位和风险区域覆盖缺口是否满足生产要求。
  • 各资源状态字段的枚举、更新时间含义和数据刷新责任人。
  • MCP 回调网络地址、TLS、SuperAgent Token 轮换方式及真实工具权限。
  • SuperAgent 无法配置协议版本;proven-profile 兼容响应、notifications/initialized、tools/list 和真实 tools/call 仍待目标机重建后确认。是否发送 MCP-Protocol-Version 不影响版本兼容验收。
  • 最终用户身份、区域权限、精确位置权限和审计保留策略。
  • 真实数据量下地名重复率、字段质量和无索引包含搜索性能;后续是否引入标准地名表、别名词典、pg_trgm 或经审批的外部地理编码服务。
  • 路线规划需要的路网拓扑、路面/宽度/坡度/车辆限制、实时封路、火场和天气数据。
  • 队伍集结需要的正式集结点、实时位置、战备状态、装备和容量数据。