Codex 与 QGIS使用MCP联动
发布时间:2026/10/9 5:16:08 锦皓数字建站

Codex 与 QGIS 联动通过 MCP 打通桌面 GIS 自动化本文记录如何在 Windows 环境下让 Codex Desktop 通过 MCP 调用 QGIS实现读取当前工程图层、检查连接状态、查询图层信息等基础联动能力。文章只关注通用接入流程不涉及具体业务数据处理。背景QGIS 是桌面 GIS 工具适合做数据查看、制图、空间分析和人工编辑。Codex 擅长理解自然语言、编写脚本、调用工具和串联工作流。把两者接起来后可以实现这样的交互通过 QGIS MCP 服务获取当前 QGIS 工程所有图层名称。或者查看当前 QGIS 工程中每个图层的名称、类型和坐标系。这类能力适合作为 GIS 自动化的基础设施Codex 负责理解意图和组织调用QGIS 负责实际读取工程、访问图层和执行 PyQGIS 操作。最终效果接入成功后Codex 能发现并调用 QGIS MCP 工具例如mcp__qgis.get_layers mcp__qgis.get_layer_names mcp__qgis.find_layer mcp__qgis.diagnose一次实际联动结果如下图层名称类型坐标系GJ2000vector_2EPSG:4490天地图-矢量地图rasterEPSG:3857QGIS 插件菜单状态整体架构这套方案不是 Codex 直接控制 QGIS也不是通过 HTTP 请求访问 QGIS。实际链路是MCP STDIOTCP length-prefixed JSONCodex Desktopcodex-qgis-bridge.pyQGIS MCP PluginQGIS / PyQGIS API几个关键点Codex 只识别 MCP Server 暴露出来的 tools。QGIS MCP 插件默认监听本地 TCP 端口例如127.0.0.1:9876。QGIS 插件端不是 HTTP 服务直接访问http://127.0.0.1:9876失败是正常的。中间需要一个桥接脚本把 Codex 的 MCP 调用转换成 QGIS 插件能理解的 socket 命令。环境准备本文使用的环境Windows QGIS 3.40 LTR Codex Desktop Python 3.13 QGIS MCP 插件 Python MCP SDK安装 Python MCP SDKpython-m pip install mcp验证 SDK 是否可用python-cfrom mcp.server.fastmcp import FastMCP; print(FastMCP)QGIS 侧配置在 QGIS 中安装并启用QGIS MCP插件。启用后在菜单中可以看到插件 - QGIS MCP - MCP :9876如果服务已启动Windows 上可以用下面命令检查端口Get-NetTCPConnection-LocalPort 9876正常情况下会看到类似LocalAddress LocalPort State OwningProcess 127.0.0.1 9876 Listen qgis-ltr-bin.exe这说明 QGIS 插件端已经就绪。为什么还需要桥接脚本QGIS MCP 插件只是在 QGIS 内部启动了一个本地 socket server。它使用的协议是4 字节大端长度前缀 JSON 命令而 Codex 需要的是 MCP STDIO Server。两边协议不同所以需要一个桥接脚本Codex MCP STDIO - Python bridge - QGIS socket server如果桥接脚本只是把 stdin 原样转发到 socketCodex 无法识别 tools。桥接脚本必须自己成为一个 MCP Server并显式注册工具。桥接脚本脚本路径示例E:\work\codex-qgis-bridge.py核心实现如下importjsonimportosimportsocketimportstructfromtypingimportAnyfrommcp.server.fastmcpimportFastMCP HEADERstruct.Struct(I)CLIENT_VERSIONcodex-qgis-bridge-0.1.0def_qgis_addr()-tuple[str,int]:hostos.getenv(QGIS_MCP_HOST,127.0.0.1)portint(os.getenv(QGIS_MCP_PORT,9876))returnhost,portdef_recv_exact(sock:socket.socket,size:int)-bytes:chunksbytearray()whilelen(chunks)size:chunksock.recv(size-len(chunks))ifnotchunk:raiseConnectionError(QGIS MCP socket closed before response was complete)chunks.extend(chunk)returnbytes(chunks)defqgis_command(command_type:str,params:dict[str,Any]|NoneNone)-Any:payload{type:command_type,params:paramsor{},client_version:CLIENT_VERSION,client_install:source,client_root:os.path.abspath(__file__),}tokenos.getenv(QGIS_MCP_TOKEN,).strip()iftoken:payload[token]token messagejson.dumps(payload,ensure_asciiFalse).encode(utf-8)host,port_qgis_addr()withsocket.create_connection((host,port),timeout15)assock:sock.sendall(HEADER.pack(len(message))message)response_sizeHEADER.unpack(_recv_exact(sock,HEADER.size))[0]responsejson.loads(_recv_exact(sock,response_size).decode(utf-8))ifresponse.get(status)!success:raiseRuntimeError(response.get(message,response))returnresponse.get(result)mcpFastMCP(qgis)mcp.tool()defget_layers(limit:int500,offset:int0)-dict[str,Any]:Return layers from the current QGIS project.returnqgis_command(get_layers,{limit:limit,offset:offset})mcp.tool()defget_layer_names(limit:int500,offset:int0)-list[str]:Return all layer names from the current QGIS project.resultqgis_command(get_layers,{limit:limit,offset:offset})return[layer[name]forlayerinresult.get(layers,[])]mcp.tool()deffind_layer(name_pattern:str)-dict[str,Any]:Find QGIS project layers by name pattern.returnqgis_command(find_layer,{name_pattern:name_pattern})mcp.tool()defdiagnose()-dict[str,Any]:Return basic QGIS MCP bridge diagnostics.host,port_qgis_addr()resultqgis_command(get_layers,{limit:1,offset:0})return{qgis_host:host,qgis_port:port,connected:True,layer_count:result.get(total_count),}if__name____main__:mcp.run()这个最小版本先暴露 4 个通用工具工具用途diagnose检查桥接脚本是否能连上 QGISget_layers获取当前工程图层摘要get_layer_names获取当前工程所有图层名称find_layer按名称搜索图层Codex 配置编辑 Codex 配置文件C:\Users\Administrator\.codex\config.toml添加 MCP Server 配置[mcp_servers.qgis] command D:/Python/python.exe args [E:/work/codex-qgis-bridge.py] env { QGIS_MCP_HOST 127.0.0.1, QGIS_MCP_PORT 9876 } startup_timeout_sec 120如果python在 PATH 中稳定也可以写command python但 Windows 上更推荐写 Python 绝对路径避免 Codex 启动时拿到错误的 Python。配置保存后需要重启 Codex Desktop 或新开任务。MCP 工具是在会话启动时加载的老会话不会自动刷新。联动验证1. 验证 QGIS 插件端Get-NetTCPConnection-LocalPort 9876看到qgis-ltr-bin.exe正在监听即 QGIS 端正常。2. 验证桥接脚本可以不经过 Codex直接用 Python 调桥接脚本python-cimport importlib.util,json; specimportlib.util.spec_from_file_location(bridge, rE:\work\codex-qgis-bridge.py); mimportlib.util.module_from_spec(spec); spec.loader.exec_module(m); print(json.dumps(m.qgis_command(get_layers, {limit: 10}), ensure_asciiFalse))如果能返回图层 JSON说明Python bridge - QGIS MCP Plugin - QGIS Project这段链路已经正常。3. 验证 Codex 工具发现重启 Codex 后工具列表中应出现mcp__qgis.diagnose mcp__qgis.get_layers mcp__qgis.get_layer_names mcp__qgis.find_layer这一步成功说明Codex - MCP STDIO bridge也已经正常。4. 让 Codex 读取图层可以直接向 Codex 提问通过 QGIS MCP 服务获取当前 QGIS 工程所有图层名称返回示例天地图-矢量地图也可以要求输出更完整的信息通过 QGIS MCP 服务获取当前 QGIS 工程所有图层名称并显示坐标系常见问题Codex 发现不到 QGIS 工具优先检查config.toml是否写在当前用户的.codex目录。command指向的 Python 是否存在。args中的桥接脚本路径是否存在。QGIS MCP 插件是否已经启动。Codex 是否在配置修改后重启。访问http://127.0.0.1:9876失败这是正常现象。QGIS MCP 插件不是 HTTP 服务而是 socket 服务。它使用长度前缀 JSON 协议不能用浏览器或Invoke-WebRequest直接访问。中文图层名在终端乱码Windows 终端编码可能不是 UTF-8。QGIS 返回的 JSON 本身没有问题Codex 中一般能正常显示。调试时可以用layer[name].encode(unicode_escape).decode(ascii)修改桥接脚本后 Codex 没变化重启 Codex Desktop 或新开一个任务。MCP tools 在会话启动时加载。QGIS 插件端口被占用如果9876被占用可以在 QGIS MCP 菜单中换端口同时修改 Codex 配置env { QGIS_MCP_HOST 127.0.0.1, QGIS_MCP_PORT 9877 }安全建议QGIS MCP 能把外部命令带进 QGIS 进程应保持本地、安全、可控只监听127.0.0.1。不要暴露到公网或局域网。如需更严格控制启用QGIS_MCP_TOKEN。桥接脚本只暴露必要工具。涉及删除、写文件、执行代码等能力时要谨慎开放。小结Codex 与 QGIS 联动的核心是协议桥接Codex MCP tools - Python MCP bridge - QGIS MCP Plugin - PyQGIS只要 QGIS 插件端口正常、桥接脚本是标准 MCP Server、Codex 配置指向正确Codex 就可以稳定读取当前 QGIS 工程信息并作为后续 GIS 自动化的入口。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。