17 KiB
森林防火空间只读 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-superagentcompatibility 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 支持的方法
initializenotifications/initializedtools/listtools/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-VersionHeader 均为兼容元数据,不作为拒绝门禁。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_ARGUMENTTOOL_NOT_FOUNDDATA_SOURCE_UNAVAILABLEQUERY_TIMEOUTINTERNAL_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-VersionHeader 不作为拒绝门禁。服务端不宣称支持任意其他版本,也不追求最新版本;日志按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或经审批的外部地理编码服务。 - 路线规划需要的路网拓扑、路面/宽度/坡度/车辆限制、实时封路、火场和天气数据。
- 队伍集结需要的正式集结点、实时位置、战备状态、装备和容量数据。