内容
# esri-mcp
一个 [MCP](https://modelcontextprotocol.io/) 服务器,暴露 ArcGIS 数据和服务 — 包括 **ArcGIS Online** 和 **ArcGIS Enterprise** — 作为 LLM 代理的工具。
将代理指向任何 ArcGIS REST 端点,它可以发现图层,检查模式,运行属性和空间查询,地理编码地址,搜索门户内容。基于原始 ArcGIS REST 通过 `httpx` 构建(无 500 MB `arcgis` 包依赖),使用 [FastMCP](https://github.com/modelcontextprotocol/python-sdk) 通过 stdio。
**设计为只读。** 写操作(`applyEdits`,删除,发布)超出了 v1 的范围。
## 工具
| 工具 | 功能 |
|---|---|
| `list_layers` | 列出 MapServer/FeatureServer 上的所有图层和表格 |
| `get_layer_info` | 图层元数据:实际字段名称(非别名),几何类型,记录限制 |
| `query_features` | SQL `where` + 可选空间过滤;分页,日期标准化,默认关闭几何 |
| `geocode_address` | 地址/地点 → 经纬度候选(AGOL 世界地理编码器) |
| `reverse_geocode` | 经纬度 → 最近的街道地址 |
| `search_portal_items` | 在 ArcGIS Online 或企业门户中搜索关键词 |
| `get_item_details` | 通过 ID 获取门户项的完整详细信息 |
### 面向 LLM 代理设计
原始 Esri JSON 冗长,不一致且充满陷阱。该服务器在边界处进行了标准化:
- **Esri 错误实际引发。** Esri 返回 HTTP 200 和 `error` 正文;每个响应都经过检查,并作为干净的消息显示(`"图层 9 在服务 X 上未找到 — 服务包含图层:0,1,2"`),而不会出现 HTML 页面或原始回溯。
- **上下文窗口保护。** 查询默认设置为 `returnGeometry=false` 和 `outSR=4326`,通过 `exceededTransferLimit` 分页,限制在 5,000 条记录,并报告 `count` / `truncated` / `source_url`,以便代理可以引用和分页。请求的几何图形通过 `maxAllowableOffset` 简化。
- **日期为 ISO 8601 字符串**,而非 epoch 毫秒。
- **实际字段名称。** `get_layer_info` 返回可查询的字段名称及其显示别名 — `where` 子句对别名静默失败。
- **长请求自动作为 POST** 发送(企业服务器上的 IIS URL 长度限制)。
- **地理编码保持事务性**(`forStorage=false`),以保持在免费层范围内。
## 快速入门
需要 [uv](https://docs.astral.sh/uv/) (自动获取 Python 3.12+)。
```bash
git clone https://github.com/<you>/esri-mcp.git
cd esri-mcp
uv sync
uv run python -m esri_mcp # 在 stdio 上启动服务器
```
### 注册到 Claude Code
```bash
claude mcp add esri -- uv run --directory /path/to/esri-mcp python -m esri_mcp
```
### 交互式调试
```bash
npx @modelcontextprotocol/inspector uv run python -m esri_mcp
```
### 试用
公共服务不需要凭证。询问您的代理类似:
> 列出 `https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer` 上的图层,然后显示给我五个最多人口的州。
## 身份验证
**匿名访问开箱即用** — ArcGIS Online 中的开放数据,Living Atlas,公共企业服务和公共独立服务器都无需配置即可使用。无需请求或发送令牌。
**令牌具有主机范围。** 配置凭据时,它们仅附加到针对其主机的请求 — AGOL 凭据仅适用于 `*.arcgis.com`,企业/独立令牌仅适用于门户/令牌主机。其他主机都匿名查询,因此配置的凭据不会阻止访问开放数据,也不会泄露到第三方服务器(例如,指向任意服务 URL 的代理)。可以在 `ARCGIS_TOKEN_HOSTS=gis.example.com,imagery.example.com` 中选择额外的主机加入。
对于安全内容,请为以下 **一种** 模式设置环境变量(按此顺序检查;参见 [.env.example](.env.example)):
| 模式 | 环境变量 | 用途 |
|---|---|---|
| IWA / NTLM | `ARCGIS_USE_NTLM=true`,`ARCGIS_USERNAME`,`ARCGIS_PASSWORD` | ArcGIS Server 背后有集成 Windows 身份验证(Web 层身份验证,无令牌)。用户名为 `DOMAIN\user` 或 `user@domain`。需要可选额外项:`uv sync --extra iwa` |
| API 密钥 | `ARCGIS_API_KEY` | ArcGIS Online API 密钥 |
| OAuth2 | `ARCGIS_CLIENT_ID`,`ARCGIS_CLIENT_SECRET` | AGOL 应用注册(client_credentials) |
| 传统令牌 | `ARCGIS_USERNAME`,`ARCGIS_PASSWORD`,`ARCGIS_PORTAL_URL` |本地 ArcGIS Enterprise(`generateToken`) |
| 独立服务器令牌 | `ARCGIS_USERNAME`,`ARCGIS_PASSWORD`,`ARCGIS_TOKEN_URL` | **独立 ArcGIS Server** (无门户)— 将 `ARCGIS_TOKEN_URL` 指向 `https://server:6443/arcgis/tokens/generateToken` |
令牌在进程中缓存,并在过期前 5 分钟刷新。如果过期的令牌击中公共端点,则请求将退回到匿名而不是失败。设置 `ARCGIS_PORTAL_URL` 还会将 `search_portal_items` / `get_item_details` 指向您的企业门户而不是 arcgis.com。
凭证永远不会出现在日志,错误消息或工具输出中。
### 自定义地理编码器
地理编码默认使用 AGOL 世界地理编码器,但 `ARCGIS_GEOCODER_URL` 将两个地理编码工具指向任何 ArcGIS `GeocodeServer` — 企业定位器,独立服务器定位器或自定义构建的定位器:
```
ARCGIS_GEOCODER_URL=https://your-server.example.com/arcgis/rest/services/YourLocator/GeocodeServer
```
### 独立 ArcGIS Server(无门户)
图层/查询工具使用完整的服务 URL,因此它们适用于任何 ArcGIS Server REST 端点 — 联邦或独立,公共或安全。对于安全的独立服务器,使用 `ARCGIS_TOKEN_URL`(令牌身份验证)或 `ARCGIS_USE_NTLM`(IWA)。门户工具(`search_portal_items`,`get_item_details`)不适用于独立服务器 — 而是通过 REST 服务目录发现服务。
## 开发
```bash
uv sync # 安装依赖(包括开发组)
uv run pytest -q -m "not integration" # 离线单元测试(模拟传输 + 记录的 JSON)
uv run pytest -q -m integration # 针对 sampleserver6.arcgisonline.com 的实时测试
uv run pytest -q # 所有测试
uv run ruff check . && uv run ruff format --check . # 检查代码风格和格式化
```
每个 Esri REST 易错点都有回归测试:错误体为 200,HTML 而非 JSON 响应,分页拼接,截断标记,纪元日期转换,别名与字段名称,长查询的 POST 和匿名回退。
### 布局
```
esri_mcp/
server.py # FastMCP 应用,工具注册,传输(stdio;一行交换到 HTTP)
auth.py # 令牌获取 + 缓存(API 密钥 / OAuth2 / generateToken / 匿名)
client.py # httpx 包装器:重试,f=json,错误标准化,POST 回退
models.py # Pydantic 请求/响应模型
tools/
layers.py # 发现 + 元数据
query.py # 要素查询:分页,日期转换,记录上限
geocode.py # 地理编码 / 反向地理编码(AGOL 世界地理编码器)
portal.py # 项目搜索,项目详情
tests/ # 单元测试(离线)和实时集成测试(标记)
```
## 路线图
- 面向托管使用的 `streamable-http` 传输(n8n,Hermes)— 已经在 `server.py` 中进行一行交换
- 在明确的 `ESRI_MCP_ALLOW_WRITES=true` 门控后进行写操作
## 许可证
MIT