开发准备

代码存放目录

GPA MCP 工具的钩子代码分两个目录存放,信任级别相同(都在服务进程内以全部权限执行),但可写性不同:

  • handlers/:随镜像发布的内置钩子(例如制图工具用的 map_service_hooks.py),部署现场不可修改;
  • custom_handlers/:部署级自定义 Handler 的容器内目录,对应产品包的 /cfg/gpa_mcp/v2_handlers/ 目录。生产环境通过 Docker Volume 挂载,部署人员将 .py 文件放入该产品包目录即可生效,不需要重建镜像。本章后续内容主要围绕这个目录展开。

产品包 /cfg/gpa_mcp/v2_handlers/ 目录中的 Handler 钩子,可供 V2 和 V2.1 版本工具的 JSON 配置引用。

工具 JSON 里 preHandler/postHandler/resultHandler/自定义 businessHandler 字段填的文件名,服务会先查 handlers/ 目录、查不到再去 custom_handlers/ 目录找,因此配置时不需要关心具体放在哪个目录,但需要注意同名时 handlers/ 优先,自定义文件无法覆盖内置钩子的文件名。

在工具中挂载钩子

以给 createBuffer 工具挂载 preHandler 和 postHandler 为例:

{
"name": "createBuffer",
"gpaToolId": "iobjects_vectoranalyst:createbuffer",
"preHandler": "buffer_hooks:validate_distance",
"postHandler": "buffer_hooks:notify_result",
"inputSchema": { ... }
}

  • 字段值的格式是 文件名 或 文件名:函数名;
  • 只写文件名时,使用工具调用链路与扩展点介绍表格中对应的默认函数名(例如 preHandler: "buffer_hooks" 等价于 preHandler: "buffer_hooks:pre_handle");
  • 文件名不能带路径(不允许 /、\、..),只能引用 handlers/ 或 custom_handlers/ 目录内的模块名——这是安全边界,不是疏漏,配置文件不应成为任意代码的加载入口;
  • 一个 .py 文件里可以同时定义好几个钩子函数,被不同工具、或同一个工具的不同字段引用。

钩子函数签名与实现

所有钩子函数使用统一签名:

async def your_handler(ctx) -> None:
...

同步函数(def 不带 async)也支持,框架会自动判断并适配,但涉及网络/文件 IO 时应写成 async def,避免阻塞服务的事件循环、拖慢其他并发请求。

ctx 是 mcptools.v2.handler_api.HandlerContext 的实例,字段说明如下:

字段 类型 说明
ctx.tool_name str 工具名,如 "createBuffer"
ctx.gpa_tool_id str 内部执行标识,如 "iobjects_vectoranalyst:createbuffer"
ctx.tool_config dict 工具 JSON 定义的深拷贝;preHandler 里改它不会污染全局配置缓存
ctx.arguments dict 本次调用的入参;preHandler 里改了会影响后续所有段
ctx.result dict | None 业务结果;resultHandler/postHandler 读写它,postHandler 拿到的是深拷贝(改了不影响真正返回值)
ctx.state dict 本次调用内跨段传值的自由字典,preHandler 写的东西 postHandler 能读到
ctx.is_async_submit bool 本次调用是不是异步提交(asyncExecution: true);为 True 时不会跑 postHandler(resultHandler 不受此影响,只要业务没失败就会跑)
ctx.services HandlerServices | None 受控的服务句柄,见下表

ctx.services 只暴露几个安全能力,不会把内部的 dispatcher/executor 整个递出来:

字段 说明
ctx.services.datacatalog 数据目录查询服务
ctx.services.map_registry 临时地图资源登记服务(未启用时为 None)
ctx.services.logger 服务自带的 logger,直接用它打日志会带上 trace 上下文

中断执行

from mcptools.v2.handler_api import HandlerAbort

raise HandlerAbort("缓冲距离过大", suggestions=["请使用 50km 以内的缓冲距离"])

在 preHandler 或 resultHandler 里抛出代码,流水线立刻中断,直接把 message 和 suggestions 包成标准错误 JSON 返回给 LLM,后续阶段都不会执行。抛其他异常效果类似(阻断),但不会带 suggestions 字段。

准备工作完成后,请根据需要选择对应场景继续:场景一:自定义调用前逻辑、场景二:自定义业务处理、场景三:自定义返回结果,或场景四:自定义调用后逻辑。