# GeoSource MCP 🌍
A Model Context Protocol (MCP) Server for Global GIS Services & Spatial Layers
面向全球 GIS 空间服务与图层目录的 MCP 标准数据检索接口
English •
中文说明
> 🗺️ **交互与检索看板**: 访问 [viewer.html](viewer.html) 即可在纯黑白灰工程风界面中查询 11,147 条空间服务,或切换到「全球态势地图」进行多边形面交互与中国 34 省级行政区无缝下钻。
---
## 🌐 English
### Overview
**GeoSource MCP** is an open-source [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server providing structured access to an indexed catalog of **11,147 global GIS spatial services** and **56,969 sub-layers**.
When developers build maps, spatial analysis pipelines, or GIS crawlers using AI assistants (Claude Desktop, Cursor, Windsurf, etc.), large language models frequently struggle to locate real-world endpoints or generate non-functional URLs. GeoSource MCP connects AI environments directly to an indexed local SQLite database (`gis_services.db`), enabling low-latency, multi-criteria discovery of verified spatial services across OGC WMS, WFS, WMTS, XYZ Tiles, ArcGIS REST, STAC, and Open Data APIs.
### Key Engineering Features
- **Structured Discovery**: Helps AI models locate existing, functional endpoints rather than guessing URLs.
- **Interactive Global Map & Explorer**: Browse services visually via [viewer.html](viewer.html) with dark brutalist monochrome aesthetics, boundary polygon highlighting, One-China compliant provincial drilldown, and one-click URL copying.
- **Low-Overhead Retrieval**: Fetches only matching records (typically 200 ~ 500 tokens per search) on demand, avoiding the cost and latency of loading megabytes of raw files into prompts.
- **Transparent Verification**: Tracks explicit availability statuses, each backed by a recorded method in `verify_method`:
- **6,137 Verified Active**: Confirmed accessible. 5,122 of them carry protocol-level proof (a WMS that actually returns `WMS_Capabilities`, a STAC API that returns `stac_version`, an ArcGIS REST service document).
- **4,956 Pending / Unverified**: Cataloged entries whose portal is reachable but no anonymous protocol-level endpoint was found (twelve-path battery: GeoServer/CKAN/ArcGIS Hub/REST/GeoNode/Socrata + THREDDS/CSW/ERDDAP/data.json variants); each entry records the probe result in `notes`.
- **54 Deprecated / Inactive**: Documented legacy services retained for reference.
- A failed probe never downgrades an entry on its own: a single failure cannot distinguish a dead service from a blocked cross-border route or UA filtering, so the reason is recorded instead.
- **Two-Way Maintenance**: Supports Excel synchronization and provides tools for AI agents to report status updates and new endpoints.
### Catalog Contents & Protocols
| Category | Protocols Supported | Notable Data Sources |
| :--- | :--- | :--- |
| **Open Data Portals (9,832)** | CKAN, DKAN, ArcGIS Hub, REST API, HTTP download | Data.gov, Eurostat, Thai DSD, Indonesian Satu Data, county/municipal ArcGIS hubs |
| **OGC Standard Services (687)** | WMS 1.1.1/1.3.0, WFS 1.0/2.0, WMTS 1.0.0, OGC API Features/Records | PDOK (Netherlands), geo.admin.ch (Switzerland), municipal GeoServers worldwide |
| **ArcGIS Platforms (295)** | ArcGIS Server REST, FeatureServer, MapServer, ImageServer | Esri Living Atlas, federal & municipal ArcGIS REST endpoints |
| **Basemap Tiles (86)** | XYZ Slippy Maps, TMS, WMTS, PMTiles, Entwine | OpenStreetMap, Carto, Stadia, OpenTopoMap, MapTiler |
| **Remote Sensing / STAC (72)** | STAC API 1.0, OGC API Records, COG | Earth Search, Microsoft Planetary Computer, USGS Landsat |
| **3D Geospatial (20)** | 3D Tiles, CityGML, I3S, Cesium terrain | Cesium ion open assets, municipal 3D building models |
| **Transit & Routing (14)** | GTFS, GTFS-RT, OSRM, Valhalla | OpenRouteService, Transitland, city transit agencies |
Protocol strings are grouped by family and overlap slightly (e.g. an ArcGIS Hub portal is also an
open-data portal). A per-category breakdown is available from `list_categories_and_stats`; the
catalog spans **319 countries/regions** across **102 categories**.
### Available MCP Tools
| Tool | Parameters | Purpose |
| :--- | :--- | :--- |
| `search_gis_services` | `keyword`, `country`, `protocol`, `category`, `is_free`, `need_no_key`, `status`, `limit` | Searches services based on multiple criteria with case-insensitive matching. |
| `get_service_detail` | `service_id` | Retrieves full metadata (50+ fields) and child layers for a specific service ID (case-insensitive). |
| `list_categories_and_stats` | *(None)* | Returns summary statistics of categories, protocols, countries, and verification statuses. |
| `query_gis_sql` | `query` | Executes safe, read-only `SELECT` queries against the SQLite database (capped at 100 rows). |
| `probe_service_api` | `service_id`, `url`, `timeout` | **Live-probes** an entry and auto-detects its open-data platform (CKAN / DKAN / ArcGIS Hub / Socrata), returning the working JSON API entry. Use before recommending an unverified portal. |
| `update_service_status` | `service_id`, `new_url`, `status`, `notes` | Updates service availability, URLs, or notes with validated status enums. |
| `resolve_admin_region` | `region` | Resolves a Chinese name/adcode through DataV; returns candidates for ambiguous names. |
| `fetch_admin_boundary` | `region`, `output_dir`, `include_children`, `target_crs` | Downloads DataV GeoJSON and a provenance manifest, returning paths instead of inline geometry. |
| `inspect_data_url` | `url` | Inspects a public URL by content/protocol; lists layers or candidate links and explicitly hands webpages to client browser tools. |
| `fetch_data` | `url`, `output_dir`, `where`, `bbox`, `fields`, `max_features` | Unified acquisition of GeoJSON, common data files and ArcGIS FeatureServer layers, with provenance and explicit subset flags. |
Administrative-boundary acquisition and the portable agent skill are documented in [docs/data-acquisition.md](docs/data-acquisition.md). Catalog discovery alone does not download a dataset.
### Verification Scripts
Two scripts back the "verified" claims with real evidence rather than a bare HTTP 200:
```bash
# Platform detection for open-data portals -> writes the real API URL into service_url
python scripts/probe_portal_api.py --dry-run # probe only
python scripts/probe_portal_api.py --apply --workers 20 # probe and write back
# Protocol-aware service verification -> writes status / verify_method / last_verified
python scripts/verify_services.py --limit 50 # no --apply means probe only
python scripts/verify_services.py --apply --workers 20
python scripts/verify_services.py --apply --only-status 已验证 # re-audit existing claims
```
`verify_services.py` checks that a service answers *in its declared protocol* — a WMS endpoint must
return `WMS_Capabilities`, a STAC endpoint must return `stac_version`, an ArcGIS REST endpoint must
return a `?f=json` service document, and so on. Results are graded `strong` (protocol-level proof) or
`weak` (reachable but unproven), and the grade is stored in `verify_method`.
The detection rules for each platform:
| Platform | Probe | Positive signal |
| :--- | :--- | :--- |
| CKAN | `GET {root}/api/3/action/status_show` | `"ckan_version"` in JSON |
| DKAN | `GET {root}/api/3/action/package_search?rows=1` | `"result"` + `"success": true` |
| ArcGIS Hub | `GET {root}/api/v3/datasets?page[size]=1` | `"data"` array in JSON |
| Socrata | `GET {root}/api/catalog/v1` | dataset array or `resultsSetSize` |
A third script keeps the offline viewer in step with the database:
```bash
# Rebuild the inlined dataset inside viewer.html from gis_services.db -> always run after a DB change
python scripts/build_viewer.py
```
`scripts/verify_endpoints.py` is a separate, read-only reachability check (`--sample N` or `--id
WMS-0001`). It reports HTTP status only and never writes to the database.
### Chinese Government Portals Are a Different Animal
Domestic 省市"公共数据开放平台" do not run CKAN-style catalogs. `scripts/probe_cn_portals.py`
handles them separately (matches entries whose URL is under `gov.cn`):
- **DExchangeOpen** (Yinchuan, Xuzhou, …): Vue SPA; the real gateway is
`{root}/dexchangeOpen/appauth/getappid` — live, but every data call requires SSO login
and an `appId` token, so anonymous API access returns `{"code":401,"msg":"sso not login."}`.
- **Jspm platform** (Dongying, Binzhou, Weihai, Shandong, …): server-rendered pages with the
data catalog HTML delivered directly (`/{city}/catalog/`). There is no public JSON search API
to register; the site itself is the interface.
- Entries whose domain no longer resolves anywhere (checked against both the local resolver and
AliDNS DoH `223.5.5.5`) are recorded as `cn-probe:dns-dead` in `verify_method` with the evidence
in `notes` — status is left untouched, because several cities folded their portals into
provincial platforms (e.g. Guangdong's `gddata.gd.gov.cn`, Jiangsu's `data.jiangsu.gov.cn`)
and the entry should follow the new address rather than be declared dead.
Statuses after the 2026-09-30 pass over 118 domestic entries: 96 alive (13 with a concrete API or
catalog URL written into `service_url`), 12 domains confirmed gone.
```bash
python scripts/probe_cn_portals.py # probe only
python scripts/probe_cn_portals.py --apply # write back status/verify_method/notes
```
### Bulk Harvesting from a Portal Registry
The 2026-10-02 expansion admitted 8,845 services in one pass by harvesting the
[dataportals-registry](https://github.com/datenoio/dataportals-registry) (43k catalog records),
filtering to geospatial platforms, de-duplicating against `service_url` domains already in the
database, then live-probing every remaining host with the platform-appropriate request
(`/api/3/action/status_show` for CKAN, `/api/v3/datasets` for ArcGIS Hub, `/geoserver/wms` for
GeoServer, …). Roughly 35,000 real HTTP requests ran; only hosts returning a protocol-level
response were admitted as `protocol-probe:strong`, and hosts answering with a real page but no
discoverable endpoint were admitted as `protocol-probe:weak` with the reason recorded in `notes`.
Two failure modes worth knowing if you rerun this:
- Writing a URL list from Python in text mode leaves `\r` line endings, which makes every `curl`
call fail with HTTP 000 in ~0.03 ms — it looks exactly like "every host is dead". Strip them
(`tr -d '\r'`) or write with `newline='\n'`.
- CKAN returns `"success":true` with no space. Matching on `"success": true` silently drops
hundreds of live CKAN portals.
The admission scripts are kept per batch under `scripts/add_new_sources_*.py`.
### Catalog Maintenance Pass (2026-10-02)
A data-quality pass normalized the catalog after the bulk harvest:
- `verify_method` was reduced from 40 ad-hoc strings to 8 canonical values
(`protocol-probe:strong|weak|unreachable`, `metadata-only`, `cn-probe:*`); the original free-text
evidence was preserved in `notes`.
- `protocol` was normalized from 132 spellings to 40 protocol families (e.g. `OGC WMS 1.3.0` and
`ArcGIS WMS` both fold into `OGC WMS`); originals are recorded in `notes` as `[protocol归一]`.
- 5,097 portal entries that only pointed at a homepage were re-probed with six common service paths
(~30k requests). 239 entries were upgraded to `protocol-probe:strong` with the discovered endpoint
written into `service_url`; 4,668 homepages-only entries were honestly re-classified as
**未验证** with the probe result in `notes`.
- `crs` no longer claims `EPSG:4326` where it was a placeholder: platform-API portals read
`随数据集`, and WMS/WFS rows carry the CRS list parsed from their live Capabilities (254 rows);
unresolved ones are marked 待复测.
- Layers: WMS/WFS GetCapabilities responses were parsed and their layer lists inserted, growing
`layers` from 1,561 to **18,292** rows across 1,201 services.
### `viewer.html` Is a Build Artifact (2026-10-03)
`viewer.html` is a single self-contained offline browser UI — its dataset is **inlined as a JSON
literal**, not fetched at runtime. It does not track the database: through the entire bulk harvest
it still showed the 2026-09-29 snapshot (2,198 records / 1,495 verified) while the catalog held
11,054 / 5,755. Regenerate it after any database change:
```bash
python scripts/build_viewer.py
```
The script locates the `DATA = [...]` block and the `ST` stats object by marker and replaces only
those, so the page's CSS, markup and static help text survive. It is idempotent — running it twice
produces the same file. Two things it also fixes:
- The detail panel used to splice `data_description`, `layer_or_endpoint` and `notes` straight into
`innerHTML`; bulk-harvested notes legitimately contain `$` and `>`, which broke the page. All three
are escaped now.
- The stats bar gains a **protocol-level strong evidence** column (4,749) alongside the others.
Gotchas if you modify the generator: find the end of the `DATA` array with
`rindex("];", 0, )` — searching *forward* with `index` matches the `];` of
the later `chips` array and silently deletes the whole script between them. And stop the replaced
slice at `]`, not `];`, so the array's own terminator is preserved.
### Connectivity Baseline (2026-10-03)
A 500-row random sample of verified services, probed with real requests at 40-way concurrency,
returned **97.2% reachable** (p50 2.0 s, p90 2.9 s). Read that number with care: 12 of the 14
non-200 responses were `400` (endpoint requires parameters), `401`/`403` (API key required or
bot-blocking) or `412`/`418` (WAF) — all of which prove the server is alive. Only **2 endpoints
(0.4%) genuinely timed out**. Per the project rule, a single failed probe never downgrades an entry,
so no status was changed.
70 catalog rows (`TILE-*`, `HIST-*`) carry `{z}/{x}/{y}` placeholders in `service_url`. That is the
standard published form for a tile endpoint, not an error, but a probe must substitute real tile
numbers to mean anything: with `z=11` substituted, 24 of the 44 verified tile services returned a
real 200 image, and the rest answered 401/403 for want of a key — which the catalog already records.
### Re-verification & Thematic Round (2026-10-07)
Round 10 re-probed all 5,245 unverified entries with a twelve-path battery (~67k real requests) and
added 93 new sources from curated registries:
- **Battery upgrades, then an honest audit.** 216 hosts answered at protocol level on paths the
previous six-path battery never tried (`/wms`, `/ows`, `/geoserver/ows`, THREDDS, GeoNetwork,
Opendatasoft, pycsw, data.json, DKAN). A strict re-check of every hit then **reverted 94** whose
"data.json"/"THREDDS" match was just an HTML page referencing those strings; 5 hard-signature hits
(real `WMS_Capabilities`/WFS XML) whose re-check failed on transient SSL/timeout were restored.
Net: **+215 verified** (5,755 → 5,970).
- **awesome-erddap registry**: 45 servers, 32 not yet in the catalog; 22 admitted (21 with a
protocol-level `search/index.csv` response, 1 weak), 10 dead or gated.
- **STAC Index registry**: 148 catalogs, 73 public ones missing; 52 admitted after confirming
`stac_version` in the response (incl. Google Earth Engine's catalog, confirmed by full read);
2 skipped because their URLs embed an API key; 14 dead.
- **GeoNode gallery leftovers**: 24 instances still unexplored, 9 admitted with live
`WMS_Capabilities` — including 6 that earlier rounds had recorded as "unreachable from this
machine" (Thünen Atlas, Sardegna CEDOC, …), a direct payoff of re-probing stale failures.
- **Thematic depth**: GLIMS glacier WMS (54 layers incl. RGI 7.0), NOAA NCEP OpenGeo (940 layers),
BOM THREDDS, NT Geological Survey, BGS 1:50k geology, FEMA GeoServer.
- **Layers**: parsed Capabilities for every verified WMS/WFS lacking a layer list — `layers` grew
from 18,292 to **56,969** rows across 1,394 services. Parse failures were recorded in `notes`,
not used to change any status.
- Two Excel deliverables were rebuilt from the database (`scripts/build_xlsx.py`), fixing months of
drift; xlsx now also has a regenerable build script like viewer.html.
### Freshness Pass & Battery Round 2 (2026-10-07)
- **Rolling freshness re-probe**: the 2,411 entries verified before 2026-10-02 were re-probed against
their declared protocol. 1,461 reconfirmed at protocol level and had `last_verified` refreshed;
failures were recorded in `notes` only — no status was changed, per the standing rule. This is now
a repeating maintenance step: oldest first, every round.
- **Battery round 2** tried nine *new* service mounts on all 4,956 still-unverified hosts
(~45k requests): `/arcgis/rest/services`, `/server/rest/services`, `/rest/services`, WMTS
endpoints, OGC API Features `/collections`, pycsw, Lizmap. 167 upgrades, of which **162 were
ArcGIS Servers hiding at non-`/arcgis` paths** — the previous batteries only looked under
`/arcgis`. Net verified: 5,970 → **6,137**.
- **Semantic reclassification round 2**: description-keyword rules moved 137 entries out of the
generic pool (cadastral → 测绘, groundwater → 水文, orthophoto → 遥感/影像, national statistics
→ 人口/社会经济…), with a multi-domain guard that keeps country-scale portals generic. A
dataset-title classifier (fetch 20 live dataset titles per portal) was also **tested and
rejected**: portal-level titles are too noisy to justify automated moves.
- `viewer.html`'s map payload is now **self-contained**: boundaries live in `assets/`, counts and
service lists are computed from the database at build time — the build no longer depends on a
scratch file from another machine. Map verified in-browser (world choropleth, China provincial
drilldown, per-country service cards).
### Getting Started
#### 1. Requirements & Installation
Requires Python 3.10+:
```bash
git clone https://github.com/Wukeeeeee/GeoSource-MCP.git
cd GeoSource-MCP
pip install -r requirements.txt
```
#### 2. Run Self-Test
Verify that the database and all MCP tools function as expected:
```bash
python server.py --test
```
You should see: `[TEST] All tests passed! Ready for MCP clients.`
#### 3. Client Configuration
##### Claude Desktop
Add to your `claude_desktop_config.json`:
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"geosource": {
"command": "python",
"args": ["/server.py"]
}
}
}
```
##### Cursor / IDEs
In your IDE's MCP settings, add a new stdio server:
- **Command**: `python`
- **Args**: `/server.py`
---
## 🇨🇳 中文说明
### 项目简介
**GeoSource MCP** 是一个基于 [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 标准的开源数据服务程序。它为各类 AI 编程与分析助手(Claude Desktop、Cursor、Windsurf 等)提供对本地结构化索引的 **11,147 个全球真实 GIS 空间服务** 与 **56,969 个地图图层** 的检索工具。
在开发地图应用或编写空间数据处理脚本时,大模型通常无法准确掌握全球各机构公开的实时服务接口,容易推断出不可用的链接。GeoSource MCP 直接连接本地 SQLite 数据库(`gis_services.db`),帮助 AI 快速检索真实的可用服务与图层参数。
### 工程特性与设计原则
- **结构化发现,降低幻觉**:提供经过结构化收录的真实服务端点,辅助 AI 编写准确的地图调用代码。
- **集成全球态势地图与属地化下钻**:在 [viewer.html](viewer.html) 内置直角黑白灰工程风交互地图,支持国际版图多边形高亮交互,严格遵循一个中国原则并支持全国 34 省级行政区无缝下钻与服务条目筛选复制。
- **按需低开销检索**:每次仅检索返回匹配的 3~5 条记录(约 200~500 Tokens),避免将数兆字节的原始表格强行填入上下文窗口。
- **透明的可用性状态**(每条的验证方式都记录在 `verify_method` 字段,可逐条追溯):
- **已验证**(5,771 条):经探测确认为可用。其中 4,749 条带**协议级实证**——WMS 真的返回了 `WMS_Capabilities`、STAC 真的返回了 `stac_version`、ArcGIS REST 真的返回了服务文档。
- **未验证**(5,245 条):门户可达但未探到匿名可调用的协议级接口(六条常见路径探测无果),或受跨国网络连通性限制;每条的探测结果都写在 `notes`。
- **已停止**(54 条):已下线或历史归档服务,保留供查阅追溯。
- **探测失败不会直接改判为"未验证"**:单次失败无法区分"服务真下线"、"本地到该站的跨境链路被阻断"、"对脚本 UA 返回 403 但浏览器正常"这三种情况,因此只记录失败原因,保留原状态。
- **双向维护支持**:支持与 Excel 表格双向数据同步,并提供更新接口供 AI 助手汇报失效链接与新地址。
### 数据分类与协议支持
| 分类 | 协议类型 | 典型代表 |
| :--- | :--- | :--- |
| **开放数据门户 (9,832 条)** | CKAN, DKAN, ArcGIS Hub, REST API, HTTP 下载 | Data.gov、欧盟数据门户、开放广东、泰国 DSD、印尼 Satu Data、各县市开放数据门户 |
| **OGC 标准服务 (687 条)** | OGC WMS (1.1.1/1.3.0), WFS, WMTS, OGC API Features/Records | 荷兰 PDOK、瑞士联邦地图、芬兰测绘局,以及各国市政 GeoServer |
| **ArcGIS 平台 (295 条)** | ArcGIS REST (MapServer, FeatureServer, ImageServer) | Esri Living Atlas、各国政府与水务气象机构 REST 服务 |
| **底图瓦片 (86 条)** | XYZ 栅格瓦片, TMS, WMTS, PMTiles, Entwine | OpenStreetMap, Carto, Stadia Maps, OpenTopoMap |
| **遥感时空资产 (72 条)** | STAC API 1.0, OGC API Records, COG | Earth Search、行星计算机、USGS Landsat |
| **三维空间数据 (20 条)** | 3D Tiles, CityGML, I3S, Cesium terrain | Cesium ion 开放资产、城市级白模与倾斜摄影服务 |
| **交通与路网 (14 条)** | GTFS, GTFS-RT, OSRM, Valhalla | OpenRouteService、Transitland、各地铁公交调度 API |
协议按族群归类、彼此有少量重叠(例如 ArcGIS Hub 门户同时也算开放数据门户)。按细分类目的实时
分布可通过 `list_categories_and_stats` 获取;当前目录覆盖 **319 个国家/地区**、**102 个分类**。
### MCP 工具一览
| 工具名称 | 输入参数 | 功能说明 |
| :--- | :--- | :--- |
| `search_gis_services` | `keyword`, `country`, `protocol`, `category`, `is_free`, `need_no_key`, `status`, `limit` | 多条件筛选查询空间服务(支持大小写无关模糊匹配)。 |
| `get_service_detail` | `service_id` | 获取该服务的 50 余项详细元数据与关联子图层列表。 |
| `list_categories_and_stats` | 无 | 获取数据库总体量、各大类、协议分布及验证状态统计。 |
| `query_gis_sql` | `query` | 对 SQLite 数据库执行只读 `SELECT` SQL 查询(内置上限 100 行保护)。 |
| `probe_service_api` | `service_id`, `url`, `timeout` | **实时探测**条目是否真的可访问,并自动识别底层平台(CKAN / DKAN / ArcGIS Hub / Socrata),返回真正能调用的 JSON 接口地址。推荐未验证的门户前先调用它,避免给出死链。 |
| `update_service_status` | `service_id`, `new_url`, `status`, `notes` | 经枚举校验后更新服务的可用状态、新 URL 或备注信息。 |
| `resolve_admin_region` | `region` | 通过 DataV 索引匹配中国行政区名称或代码;重名返回候选项,不自动选取。 |
| `fetch_admin_boundary` | `region`, `output_dir`, `include_children`, `target_crs` | 真正下载 DataV 边界,返回 GeoJSON 路径和来源记录,不把几何正文塞进上下文。 |
| `inspect_data_url` | `url` | 统一检查公开网址,识别文件、图层服务或网页;网页返回候选链接和浏览器交接指引。 |
| `fetch_data` | `url`, `output_dir`, `where`, `bbox`, `fields`, `max_features` | 通用获取 GeoJSON、常见数据文件和 FeatureServer 要素;记录来源并明确是否仅获取子集。 |
### 统一网址入口(0.3.0)
给出公开数据网址,先用 `inspect_data_url` 判断内容,再用 `fetch_data` 获取。按格式和协议处理,不要求为每个网站编写 Python。支持 GeoJSON、ZIP/GPKG/TIFF/CSV/KML/KMZ 文件链接,以及 ArcGIS FeatureServer 具体图层。文件类型仅做基础检查;不会自动解压或认证数据集内容。
普通网页返回 `browser_required` 和候选下载链接,交给客户端已有的浏览器工具探索后,将实际数据网址交回 GeoSource。本服务没有内置浏览器控制;网页交接不代表浏览器已经执行或文件已经获取。
GeoJSON/FeatureServer 默认最多保存 1000 个要素,可设置至 10000;数量受限时明确返回 `truncated=true`。FeatureServer 支持条件、WGS84 范围和字段筛选,并检查分批返回的 ID;二进制/CSV 文件整体下载,单个响应/输出上限 30 MiB。通用入口保留文件坐标,只有 FeatureServer 明确请求 `outSR=4326`。详细流程见 [数据获取说明](docs/data-acquisition.md)。
### 数据获取:说明文档与真正执行的区别(0.2.0)
Skill 是调用说明;MCP 工具执行本机 Python 代码,通过 HTTPS 请求 DataV 公开端点并写入本地文件。这不是模型临时生成脚本,也不是模拟浏览器抓取网页。目录检索只返回线索,不代表数据已下载。
示例:调用 `fetch_admin_boundary(region="440100", include_children=true)` 获取广州各区边界;只有 `status="success"` 才表示下载成功。结果包含文件路径、原始网址、时间、SHA256 和坐标处理记录,并写入同名 `.provenance.json` 文件。
默认输出到 `~/.local/share/geosource/downloads`,可用 `output_dir` 或环境变量 `GEOSOURCE_OUTPUT_DIR` 指定目录。默认按 DataV 的 GCJ-02 约定近似转换到 WGS-84;返回结果明确记录来源坐标假设,不能视为测绘精度认证。设 `target_crs="source"` 保留来源坐标,不应直接当成 EPSG:4326 使用。
使用方式、限制和可移植 skill 见 [数据获取说明](docs/data-acquisition.md)。新增工具无需安装 skill 即可通过 MCP 调用;复制 Markdown 本身不会注册工具或加载地图。
### 数据可信度是怎么做出来的
"已验证"不是随手贴的标签,而是有脚本用真实请求跑出来的。两个脚本:
```bash
# 1) 门户接口识别:探测底层平台,把真正能调用的 API 地址写回 service_url
python scripts/probe_portal_api.py --dry-run # 只探测不写库
python scripts/probe_portal_api.py --apply --workers 20 # 探测并回写
# 2) 协议级可用性验证:写回 status / verify_method / last_verified
python scripts/verify_services.py --limit 50 # 不加 --apply 即为只探测
python scripts/verify_services.py --apply --workers 20
python scripts/verify_services.py --apply --only-status 已验证 # 复审已有的"已验证"
```
`verify_services.py` 判断的是"服务是否以它声明的协议应答",而不只是"网址能不能打开":
WMS 必须返回 `WMS_Capabilities`,STAC 必须返回 `stac_version`,ArcGIS REST 必须返回 `?f=json`
的服务文档。结果分两档——`strong`(协议级实证)和 `weak`(能连通但未证实),
档位写进 `verify_method` 字段,任何人都能查某条记录的"已验证"是怎么来的。
各平台的识别规则:
| 平台 | 探测地址 | 判定依据 |
| :--- | :--- | :--- |
| CKAN | `GET {root}/api/3/action/status_show` | JSON 中含 `"ckan_version"` |
| DKAN | `GET {root}/api/3/action/package_search?rows=1` | 含 `"result"` 且 `"success": true` |
| ArcGIS Hub | `GET {root}/api/v3/datasets?page[size]=1` | JSON 中含 `"data"` 数组 |
| Socrata | `GET {root}/api/catalog/v1` | 数据集数组或 `resultsSetSize` 字段 |
还有第三个脚本负责让离线查询页跟上数据库:
```bash
# 3) 重新生成 viewer.html 内嵌的数据 -> 改库后必须执行
python scripts/build_viewer.py
```
`scripts/verify_endpoints.py` 是另一个只读的连通性抽查工具(`--sample N` 或 `--id WMS-0001`),
它只报 HTTP 状态码,永远不写库。
### 从门户登记册批量扩库(2026-10-02)
本轮一次性入库 8,845 条,靠的是 [dataportals-registry](https://github.com/datenoio/dataportals-registry)
(4.3 万条门户记录):筛出地理空间类平台 → 与库内已有域名去重 → 对每个候选主机发**符合其平台的真实请求**
(CKAN 打 `/api/3/action/status_show`,ArcGIS Hub 打 `/api/v3/datasets`,GeoServer 打 `/geoserver/wms` 等)。
累计发出约 3.5 万次真实请求,只有返回协议级应答的才按 `protocol-probe:strong` 收录;
只返回真实页面、探不到接口的按 `protocol-probe:weak` 收录并在 `notes` 写明原因。
重跑这套流程时有两个坑:
- 用 Python 文本模式写 URL 清单会带上 `\r` 行尾,`curl` 全部报 HTTP 000、耗时 0.03ms,
看起来像"全站都死了"。要么 `tr -d '\r'` 清洗,要么写文件时加 `newline='\n'`。
- CKAN 返回的是 `"success":true`(无空格)。按 `"success": true`(带空格)匹配会静默漏掉
几百个活着的 CKAN 门户。
各批次入库脚本留档在 `scripts/add_new_sources_*.py`。
### 数据维护批次(2026-10-02)
批量扩库之后做了一轮数据质量整修:
- `verify_method` 从 40 种随手写法归一成 8 个规范枚举(`protocol-probe:strong|weak|unreachable`、
`metadata-only`、`cn-probe:*`),原始证据文本移入 `notes` 保留。
- `protocol` 从 132 种拼法归一成 40 个协议族(如 `OGC WMS 1.3.0`、`ArcGIS WMS` 都并入 `OGC WMS`),
原值以 `[protocol归一]` 标记写进 `notes`。
- 对 5,097 个只指向首页的门户条目补测六条常见服务路径(约 3 万次请求):239 条找到了真实接口、
升级为 `protocol-probe:strong` 并把接口写进 `service_url`;4,668 条只有首页可达,如实降为
**未验证**,探测结果记录在 `notes`。
- `crs` 不再拿 `EPSG:4326` 当占位符:平台型 API 门户标为 `随数据集`;WMS/WFS 按在线 Capabilities
解析真实 CRS(254 条),解析不出的标 `待复测`。
- 图层:解析 WMS/WFS GetCapabilities 并回填 `layers` 表,从 1,561 行增至 **18,292 行**,
覆盖 1,201 个服务。
### viewer.html 是生成物(2026-10-03)
`viewer.html` 是一个单文件、离线可用的浏览器查询界面,但里面的数据是**内嵌的 JSON 字面量**,
不会运行时去读数据库——整个批量扩库期间它一直显示 9/29 的快照(2,198 条 / 已验证 1,495),
而库里早已是 11,054 / 5,755。**改库之后必须重新生成**:
```bash
python scripts/build_viewer.py
```
脚本按标记定位 `DATA = [...]` 与 `ST` 统计块,只替换这两段,页面的 CSS、结构和静态说明文字不动。
可重复执行,跑两次结果一致。它同时修掉两个问题:详情面板原先把 `data_description`、
`layer_or_endpoint`、`notes` 直接拼进 `innerHTML`,而批量入库的备注里确实有 `$`、`>` 这类字符,
会破坏页面,现已统一转义;统计栏新增**协议级强证据**一栏(4,749)。
若要改这个生成器,有两个坑:DATA 数组的结尾要用 `rindex("];", 0, <"const ST" 的位置>)` 往前找,
用 `index` 往后找会撞上后面 chips 数组的 `];`,把中间整段脚本悄悄删掉;替换区间要停在 `]` 而不是
`];`,否则数组自己的收尾分号会被一并删掉。
### 联通测试基线(2026-10-03)
随机抽 500 条已验证服务、40 并发发真实请求,**可达率 97.2%**(p50 2.0 秒、p90 2.9 秒)。这个数字要
这么读:14 条非 200 里有 12 条是 `400`(端点缺必填参数)、`401`/`403`(要 API Key 或反爬)、
`412`/`418`(WAF 防护)——它们恰恰证明服务器活着。**真正超时的只有 2 条(0.4%)**。按项目铁律,
单次探测失败不改判,因此未改动任何条目的状态。
目录里有 70 条(`TILE-*`、`HIST-*`)的 `service_url` 含 `{z}/{x}/{y}` 占位符。这是瓦片服务的标准
发布形式、不是错误,但探测时必须替换成真实瓦片号才有意义:代入 `z=11` 后,44 条已验证瓦片服务里
24 条返回了真实的 200 图片,其余 401/403 是缺 Key——而这一点目录里本来就有标注。
### 复测与专题纵深轮(2026-10-07)
第十轮用十二条路径的探测电池复测了全部 5,245 条未验证条目(约 6.7 万次真实请求),
并从三个策划登记册新增 93 个源:
- **电池升级 + 如实复审**。216 台主机在上一轮没试过的路径上给出协议级应答
(`/wms`、`/ows`、`/geoserver/ows`、THREDDS、GeoNetwork、Opendatasoft、pycsw、data.json、DKAN)。
对每条命中做严格复审后**撤回 94 条**——那些 "data.json"/"THREDDS" 匹配只是 HTML 页面里引用了
这些字符串;5 条硬签名命中(真 `WMS_Capabilities`/WFS XML)复审时因瞬时 SSL/超时失败,按
"单次失败不改判"铁律恢复。净增**已验证 +215**(5,755 → 5,970)。
- **awesome-erddap 登记册**:45 台中 32 台未收录,收 22 台(21 台 search API 返回规范 CSV 头、
1 台弱证据),10 台死或受限。
- **STAC Index 登记册**:148 个目录中 73 个 public 未收录,52 个在响应中确认 `stac_version` 后收
录(含 Google Earth Engine 目录,全量读取确认);2 个因 URL 内嵌 API key 不收;14 个死。
- **GeoNode 画廊存量**:还剩 24 个实例没测,9 个以真实 `WMS_Capabilities` 入库——其中 6 个是
前几轮记录"本机不可达"的(Thünen Atlas、Sardegna CEDOC 等),复测存量直接兑现。
- **专题纵深**:GLIMS 冰川 WMS(54 图层含 RGI 7.0)、NOAA NCEP OpenGeo(940 图层)、
澳大利亚气象局 THREDDS、北领地地质调查局、BGS 1:50k 地质、FEMA GeoServer。
- **图层**:对所有缺图层的已验证 WMS/WFS 解析 Capabilities,`layers` 从 18,292 行增至
**56,969 行**(1,394 个服务)。解析失败只记 `notes`,不改状态。
- 两个 Excel 交付物从数据库重建(新增 `scripts/build_xlsx.py`),修复数月的数字漂移;
xlsx 现在也和 viewer.html 一样有了可重跑的生成脚本。
### 保鲜复测与电池二轮(2026-10-07)
- **滚动保鲜复测**:对 2026-10-02 之前验证的 2,411 条按声明协议重探,1,461 条协议级确认存活并
刷新 `last_verified`;失败只记 `notes`,状态零变动(铁律)。此后每轮固定做,最老的优先。
- **电池二轮**对全部 4,956 条仍未验证的主机试了 9 条全新挂载路径(约 4.5 万次请求):
`/arcgis/rest/services`、`/server/rest/services`、`/rest/services`、WMTS、OGC API Features
`/collections`、pycsw、Lizmap。167 条升级,其中 **162 个是把 ArcGIS Server 藏在非 `/arcgis`
路径下的站点**——此前电池只盯着 `/arcgis` 看。已验证 5,970 → **6,137**。
- **语义归类二期**:描述关键词规则把 137 条挪出泛化类(地籍→测绘、地下水→水文、正射影像→遥感、
国家统计局→人口/社会经济),并加"多域守门"——国家/大都市综合门户命中 3 类以上就留在综合类。
另试验了"拉真实数据集标题归类",**试验后否决**:门户级标题噪声太大,不足以支撑自动挪类。
- `viewer.html` 的地图 payload 改为**自包含**:边界数据入库 `assets/`,计数与服务列表构建时从
数据库现算,不再依赖另一台机器的 scratch 文件。地图已在浏览器验收(全球填色、中国省级下钻、
各国服务卡片)。
### 快速开始
#### 1. 安装依赖
```bash
git clone https://github.com/Wukeeeeee/GeoSource-MCP.git
cd GeoSource-MCP
pip install -r requirements.txt
```
#### 2. 本地快速自测
```bash
python server.py --test
```
#### 3. 配置客户端连接
##### Claude Desktop
编辑配置文件(Windows: `%APPDATA%\Claude\claude_desktop_config.json`,macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"geosource": {
"command": "python",
"args": ["E:/my_repo/GeoSource/server.py"]
}
}
}
```
##### Cursor / 其他 IDE
在 MCP 配置页面添加 stdio 服务:
- **Command**: `python`
- **Args**: `E:/my_repo/GeoSource/server.py`
---
## 📁 数据库表结构 (Database Schema)
- **`master`**:主服务表(主键 `service_id`,包含服务名、提供方、国家、协议、服务地址、格式、认证方式、验证状态等 54 个字段,建有 9 个核心索引)。
- **`layers`**:子图层表(联合主键 `(service_id, layer_idx)`,记录服务所属图层名称)。
- **`ecosystem`**:开源 GIS 软件库、SDK 与数据库工具链表。
---
## 📄 License
[MIT License](LICENSE) © 2026 GeoSource Authors