如果你最近刷到过“QGIS-MCP”相关演示,大概率会看到同一个场景:一个人打开 QGIS,加载了一堆图层,然后在旁边某个 AI 对话框里说了一句“把省级行政区按人口数做渐变配色”,过一会儿图层真的按要求渲染出来了。这里的关键词有两个:QGIS 是开源桌面 GIS,MCP 是模型上下文协议,它让 AI 助手可以调用外部工具。这篇文章不打算复刻某个具体项目,而是按我自己做演示和排查的思路,把环境准备、最小链路、参数设计、常见报错一次讲清楚。适合刚接触 MCP、又想把它接到 GIS 工作流里的读者。
1. 先弄清楚 MCP 和 QGIS 是怎么接上的
1.1 MCP 不是一个软件,而是一种工具调用协议
MCP 的全称是 Model Context Protocol,中文一般叫模型上下文协议。这个名字容易让人误以为它是个硬件接口或者某个系统总线,其实它做的是一件事:把本机的“能力”暴露给 AI 助手。
一个 AI 客户端启动后,会去连接一个 MCP 服务器。服务器这边把自己提供的功能整理成一张工具列表,每个工具都带有名称、参数说明、输出说明。AI 收到用户指令后,根据工具描述决定调用哪个工具、传什么参数。服务器执行完操作,把结果返回给 AI,AI 再组织成自然语言回复用户。
所以整个链路不是“AI 直接操控 QGIS 窗口”,而是:
text复制用户 -> AI 客户端 -> MCP 服务器 -> QGIS 自身的接口
AI 不直接读界面上的坐标、不帮你点菜单,它通过服务器拿到的是结构化数据和操作结果。
1.2 在 QGIS 场景里,MCP 服务器到底做了什么
QGIS 本身不是为 AI 设计的,但它提供了不少可编程入口:
- 内置 Python 控制台,可以通过 PyQGIS 操作图层、样式、项目文件。
- 处理框架,可以调用 Buffer、Clip、Dissolve 这类地理处理算法。
- qgis_process 命令行工具,可以脱离图形界面执行处理算法。
- 插件机制,可以通过 Python 插件扩展功能。
MCP 服务器如果把 QGIS 的操作封装成工具,AI 就能完成类似下面的事情:
- 读取当前 QGIS 项目里有哪些图层。
- 获取某个图层的字段列表、要素数量、空间范围、坐标系。
- 按属性表达式过滤要素。
- 执行缓冲、裁剪或其他空间算法。
- 导出处理结果为 GeoPackage、Shapefile 或图片文件。
- 把天地图、高德、百度这类在线底图添加到项目里。
这里要强调一点:MCP 返回给 AI 的通常是文本、JSON 或文件路径,而不是“把 QGIS 界面截图给 AI 看”。AI 判断结果靠的是返回值、属性和日志,不是视觉。
1.3 演示前的环境清单
先列一张我自己做演示时常用的环境清单,避免一开始就陷在依赖问题里。
| 项目 | 建议 | 说明 |
|---|---|---|
| QGIS | 能正常打开 Python 控制台的版本即可 | 不需要追求最新版本,稳定优先 |
| 操作系统 | Windows、macOS、Linux 都可以 | Windows 下注意路径和编码问题 |
| Python | 最好使用 QGIS 自带的 Python 环境 | 这样 PyQGIS 不用额外配置 |
| MCP SDK | 以你使用的 MCP 框架文档为准 | 常见的是 Python SDK 或 FastMCP |
| AI 客户端 | 支持添加 MCP 服务器的客户端均可 | 不只是某一个桌面客户端 |
| 本地数据 | 一个 GeoPackage 或 Shapefile | 字段名简单,坐标系明确 |
| 网络 | 本地演示不需要外网 | 加载在线底图时才需要网络和授权 |
为什么建议用 QGIS 自带的 Python 环境?因为 PyQGIS 的导入过程和 QGIS 安装路径、系统库关系很大。如果你用系统 Python 去 import qgis.core,经常会报 ModuleNotFoundError。换到 QGIS 自带的 Python 解释器,大多数问题会直接消失。
提醒:如果只是想验证 MCP 链路,先不要加载特别大的影像数据或几十万要素的矢量图层。用一个小数据集把链路跑通,再逐步加大数据量。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 把最小演示跑通:让 AI 读出项目里的图层
2.1 为什么要从“读图层列表”开始
第一次做 QGIS-MCP 演示,我最不建议一上来就让 AI 执行缓冲、裁剪或属性修改。理由有两个:
- 写操作的失败原因很多,结果还不可逆。
- 一旦出了问题,你很难判断是哪一段出错。
“读取图层列表”是只读操作,风险很低。它能同时验证三件事:
- MCP 服务器能否正常启动。
- 客户端能否发现工具。
- QGIS 的 Python 环境能否被服务器正确使用。
只要这个链路通了,后面加更多工具就只是工作量问题,不再是方向问题。
2.2 三种落地通道对比
在做 QGIS-MCP 集成时,常见的落地通道有三种。它们的原理不同,适用场景也不同。
| 通道 | 做法 | 适合场景 |
|---|---|---|
| qgis_process | 通过命令行调用 QGIS 处理算法 | 批量任务、无界面环境 |
| QGIS 内置 Python | 在 QGIS 自己的 Python 环境里启动 MCP 服务器 | 需要 PyQGIS 完整能力的场景 |
| 独立 Python 进程 | 手动设置 PYTHONPATH 和环境变量,再导入 PyQGIS | 想和桌面版解耦时用,但坑最多 |
我最推荐第二种。因为它最贴近日常使用 QGIS 的方式,PyQGIS 的模块导入最可靠,也不容易出现“找不到 qgis.core”的问题。
2.3 客户端配置文件示例
大多数支持 MCP 的客户端,都会通过一个 JSON 配置来声明 MCP 服务器。下面是一个示意配置,具体字段名要看你用的客户端和 SDK 文档:
json复制{
"mcpServers": {
"qgis-mcp-demo": {
"command": "python",
"args": ["-m", "qgis_mcp_server"],
"cwd": "C:/Workspace/qgis-mcp-demo"
}
}
}
这里的 command 要特别注意。如果你在虚拟环境里运行,应该写成虚拟环境里的 Python 绝对路径;如果是 QGIS 自带 Python,也要写对应的解释器路径,否则客户端可能启动的是另一个 Python。
2.4 用 Python MCP SDK 写一个最小工具
下面这段代码只用于展示工具的组织方式,不是某个现成项目的完整代码:
python复制from mcp.server.fastmcp import FastMCP
mcp = FastMCP("qgis-mcp-demo")
@mcp.tool()
def list_layers(project_path: str = "") -> list[dict]:
"""读取 QGIS 项目中的图层列表。
没有传 project_path 时,读取当前默认项目或已打开的 .qgz 文件。
"""
# 这里需要连接 QGIS 的 Python API
# 伪代码:QgsProject.instance().mapLayers()
return [
{"name": "province", "type": "vector", "count": 34},
{"name": "rivers", "type": "vector", "count": 120},
]
if __name__ == "__main__":
mcp.run()
这段代码的重点不是具体算法,而是结构:每个工具必须有清晰描述,返回值最好是 JSON 友好的 Python 对象。AI 对工具的理解,主要依赖工具名和 docstring,因此描述里要写清楚参数含义、返回内容、有没有写入风险。
2.5 第一次启动怎么判断成功
建议按这个顺序检查:
- 先单独在终端里启动 MCP 服务器,不要通过 AI 客户端。
- 看终端有没有报错,如果有,先解决掉。
- 再在客户端里刷新 MCP 工具列表。
- 让 AI 调用
list_layers这个工具。 - 看返回值是不是一个 JSON 数组。
如果 AI 回复“工具不存在”,基本就是服务器没起来,或者客户端加载的配置不对。
这里不要急着调并发或加很多工具。最小演示的目的是确认链路,不是为了展示功能数量。
3. 再往深走:属性统计、缓冲分析、批量导出
3.1 先做图层信息摘要,再做复杂操作
当 list_layers 能正常返回后,可以加一个 get_layer_summary 工具。它返回的内容通常是:
- 字段列表
- 要素数量
- 空间范围
- 坐标系 EPSG
- 几何类型
为什么先加这个?因为 AI 在对数据执行空间操作前,必须知道自己面对的是什么坐标系、什么几何类型。如果图层本身是 EPSG:3857,你直接用米为单位做缓冲,参数就要按米处理;如果图层是 EPSG:4326,按度做缓冲,数字完全不同。
3.2 一个典型的工具函数形态
下面是示意代码,展示如何设计参数和输出:
python复制@mcp.tool()
def get_layer_summary(project_path: str, layer_name: str) -> dict:
"""返回指定图层的字段、要素数、范围、坐标系信息。
Args:
project_path: QGIS 项目文件路径。
layer_name: 图层名称。
"""
...
参数写得越明确,AI 越不容易传错。尤其是图层名称,如果项目里有重名图层,MCP 服务器端应该先返回候选列表,而不是直接报错。
3.3 执行缓冲或裁剪时要处理四个参数
以缓冲分析为例,MCP 工具至少要考虑:
input_layer:输入图层。distance:缓冲距离。distance_unit:米、千米、度,必须指定。output_path:输出文件路径。overwrite:是否允许覆盖已存在文件。
很多演示只写了 distance,结果 AI 生成一个参数后,跑出来的结果和用户预期完全不同。因为“100”这个数字在不同单位下含义差很多。所以工具描述里一定要写“distance 为浮点数,distance_unit 为字符串,可选值是 meters / kilometers / degrees”。
3.4 在线底图能不能交给 AI 操作
热搜里经常出现“qgis如何下载百度或高德地图”“qgis加载天地图”。这些在国内 GIS 工作流里很常见,也确实可以做成 MCP 工具,但要做三层判断。
第一层,有没有合法访问条件。天地图一般需要申请 key,高德和百度也都有自己的接入方式和授权要求。演示现场用临时 URL 跑通很容易,正式项目一定要看服务条款和配额限制。
第二层,密钥放在哪里。不要写死在工具描述里,不要提交到 Git 仓库。建议放在本地配置文件里,并在 .gitignore 中忽略掉。
第三层,坐标系和图层的叠加问题。在线瓦片底图通常是 Web 墨卡托投影,也就是 EPSG:3857。如果你的矢量数据是 EPSG:4326 或其他投影,AI 在叠加底图时不能假定两个图层天然对齐。
我自己的习惯是:让 AI 先返回“当前底图坐标系”和“目标图层坐标系”,再决定是否做重投影,最后才执行加载底图的操作。
4. 把 QGIS 能力封装成 MCP 工具时,四个设计点最影响成败
4.1 工具参数尽量显式化
MCP 的核心在于把自然语言转换成结构化调用。工具如果只有 data_path 和 action 两个参数,AI 大概率会乱猜。更好的做法是把动作拆开,一个工具做一件事。
比如不要写:
text复制run_gis(data_path, action)
而是要写:
text复制buffer_layer(input_file, distance, distance_unit, output_file)
clip_layer(input_file, mask_file, output_file)
参数越明确,LLM 越容易生成正确调用。参数设计模糊,后续排查会非常痛苦。
4.2 输出要限制体积
MCP 服务器返回给 AI 的内容,不能是一份几万行的属性表,也不能是几兆的 GeoJSON。AI 客户端有自己的上下文窗口,返回数据过大会导致响应变慢,甚至截断。
更稳妥的方案是:
- 默认返回摘要,比如要素数量、字段名、范围。
- 只有显式传入
limit参数时,才返回前 N 行。 - 完整结果写到独立文件,返回文件路径而不是文件内容。
这样既保留了能力,又避免把对话窗口塞满。
4.3 路径访问要做白名单
MCP 服务器本质上是本地进程,具备在机器上读写文件的能力。如果不做目录限制,AI 可能会被诱导去读取或修改系统目录。
我一般会在 MCP 服务器里加一个配置项,只允许访问指定目录:
text复制allowed_paths:
- C:/Workspace/gis_data
- C:/Workspace/projects
工具在解析输入路径时,先检查这个路径是否在白名单范围内,不在就直接拒绝。这样虽然只多了一层检查,但能避免绝大多数路径穿越问题。
4.4 读操作和写操作分开
如果把“读取图层”和“删除图层文件”放在同一个工具里,权限边界就不清晰。AI 在日常对话中可能会叠加多个任务,如果某个工具既能读又能写,误操作风险会明显上升。
建议把工具按能力分成三类:
read_*:读取信息、统计、查询。write_*:创建新文件,但不覆盖已有内容。overwrite_*:允许覆盖或删除,需要额外参数确认。
演示阶段可以只暴露 read_*,跑稳定再把写操作打开。
5. 演示过程中最容易踩的五个坑
5.1 客户端报“未找到工具”
这个症状很多人都遇到过。第一次排查不是去翻 AI 客户端的代码,而是先回答三个问题:
- MCP 服务器进程到底有没有起来?
- 它启动时报了什么错?
- 配置文件里的命令和参数对不对?
我建议先手动在终端里启动服务器,确认能打印出“server started”或类似日志,再回到客户端去刷新。如果手动启动都不行,问题大概率在环境,不在客户端。
5.2 一执行工具就报 PyQGIS 模块找不到
这是 QGIS-MCP 最常见的错误。原因通常是:MCP 服务器使用的是系统 Python,而不是 QGIS 自带的 Python。
解决方式有两种:
- 用 QGIS 自带的 Python 解释器启动服务器。
- 手动设置环境变量,把 QGIS 安装目录里的 Python 路径加入
PYTHONPATH。
第二种方式在 Linux 上比较多见,但不同系统路径差异很大。我更建议在 Windows 上直接用 QGIS 安装目录里的 python-qgis.bat 或类似入口,在 macOS 上使用 QGIS.app 目录里的 Python。
5.3 图层加载了,但位置对不上
如果你加载完图层后发现图形跑到了奇怪的位置,十有八九是数据本身没带坐标系,或者带上了一个和实际位置不一致的坐标系。
MCP 工具里最好加一个步骤:读取图层的 CRS,如果为空或返回 EPSG:4326、EPSG:3857 之外的未知值,先中断执行并返回提示,让用户确认。
5.4 中文路径和编码问题
这是 Windows 下特别容易踩的坑。项目路径、图层名、属性字段如果包含中文,Python 在读取时可能出现编码错误。
建议流程:
- 第一次测试时,把所有数据放在英文目录里。
- 确认链路正常后,再切换到真实中文路径。
- 如果报错,优先看日志里的编码信息,不要直接改代码。
5.5 任务卡住不返回
MCP 工具执行时如果调用了 QGIS 的交互对话框,或者脚本因为某个输入等待确认,整个请求就会挂住。
所以工具内部不要调用带界面的方法。凡是可能阻塞的操作,都要设置超时时间,或者在客户端侧设置请求超时。
6. 从单次演示到长期使用,我的建议
6.1 值得自动化的三类工作
演示只是第一步。真正值得用 QGIS-MCP 固定下来的,是下面这些重复且规则明确的工作:
- 批量处理:对多个 Shapefile 或 GeoPackage 执行相同的投影转换、字段整理、空间计算。
- 报告生成:自动统计每个县区的要素数量、面积、重叠情况,生成摘要。
- 数据质检:检查缺失字段、几何错误、坐标系不统一、拓扑异常。
这三类工作有一个共同点:输入输出可以明确定义,判断标准也不需要太复杂。对 AI 来说,这类任务比“把这张地图做得更好看”更容易落地。
6.2 不建议交给 AI 直接操作的部分
不是所有事都适合让 AI 去做。
涉及地图制图排版、专题图配色、业务规则判断、敏感数据导出的任务,我建议让人工完成,或者让 AI 只生成脚本和方案,不直接执行。原因很简单:这些操作的结果判断依赖行业经验和审美,错误成本也高。
比如“按人口数做渐变配色”,AI 可以帮你算好分类区间,可以帮你生成 QGIS 的样式 XML,但最终是否使用、是否需要调整,应该由人确认。
6.3 我会长期保留的四个习惯
- 日志单独存文件,不混在 MCP 标准输出里。
- 所有处理结果输出到独立目录,不覆盖源数据。
- 每次跑大任务前,先跑一个小样本。
- 保留一份可靠的项目备份,方便回滚。
QGIS-MCP 这个方向,真正有价值的不是“AI 能操作 QGIS”这个噱头,而是它能把 GIS 工作中最繁琐的重复环节抽出来,变成可复用的自动化能力。演示跑通只是开始,后面更值得投入精力的,是工具边界、参数约束和数据处理安全。
