GeoOntology-MCP
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@GeoOntology-MCPIs Suzhou adjacent to Shanghai?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
GeoOntology-MCP
地理本体层 MCP 服务器——让 LLM 用地理结构推理,而非裸 SQL。
为什么需要这个项目
现有 PostGIS MCP 服务器只暴露原始 SQL 查询能力。LLM 拿到的是行和列,不是"南京属于江苏"或"苏州与上海相邻"这样的地理结构知识。
GeoOntology-MCP 暴露的是地理本体层——层级关系、拓扑邻接、语义消歧、空间运算、属性对比,以及核心创新:地理约束校验。LLM 生成地理断言后,MCP 可以校验真伪并返回结构化纠错,驱动 LLM 自我修正。
LLM: "苏州与上海不相邻"
→ check_adjacency("320500", "310000")
← { valid: false, correction: "苏州市 与 上海市 相邻,共享边界 42.3km",
evidence: { shared_boundary_length_km: 42.3 } }
LLM: "我修正之前的说法,苏州与上海实际上是相邻的……"Related MCP server: GIS Data Conversion MCP
架构
LLM Client ──MCP Protocol──► GeoOntology-MCP (TypeScript)
│
┌─────────┴─────────┐
│ Ontology Engine │
│ - 层级遍历 (KG) │
│ - 拓扑查询 (PostGIS)│
│ - 约束校验 (两层) │
│ - 空间运算 (PostGIS)│
│ - 路径搜索 (BFS) │
└─────────┬─────────┘
│
┌─────────────┴─────────────┐
│ │
Knowledge Graph (内存) PostGIS (持久化)
- 46,413 节点 - boundary geometries
- 44,956 BELONGS_TO 边 - admin_adjacency (预计算)
- 地名/拼音/英文索引 - admin_containment (预计算)
- admin_attributes (属性数据)数据策略:知识图谱 JSON 在启动时加载到内存,处理所有层级关系和地名消歧(纯图遍历,O(depth));邻接关系通过 PostGIS ST_Touches 预计算后存入 admin_adjacency 表,查询时 O(1)。
工具一览(10 个)
查询类
工具 | 参数 | 说明 |
|
| 地名消歧,支持中文、拼音、英文。返回候选列表(ID、层级位置、坐标),按相关性排序取 top-5 |
|
| 向上遍历层级链(县→市→省),返回从直接父到顶级的有序数组 |
|
| 查询同层级邻接区域,含共享边界长度 |
|
| 区域全景:层级位置、邻居、面积、类型、子区数量,可选返回简化 GeoJSON |
校验类(核心差异化)
工具 | 参数 | 说明 |
|
| 验证包含关系,KG 层级检查 + PostGIS 空间回退 |
|
| 验证邻接关系,预计算表 + 实时空间回退 |
空间运算类
工具 | 参数 | 说明 |
|
| BFS 搜索两区域间的邻接路径(如 江苏→浙江) |
|
| 两区域质心间距离 (km) |
|
| 查找半径内的同层级区域 |
属性对比类
工具 | 参数 | 说明 |
|
| 多区域属性排名对比(人口/GDP/面积) |
校验返回格式
校验通过:
{
"valid": true,
"claim": "南京市 属于 江苏省",
"explanation": "层级关系确认: 南京市(city) → 江苏省(province)"
}校验失败 + 纠错:
{
"valid": false,
"claim": "南京市 属于 浙江省",
"explanation": "南京市 不属于 浙江省,实际属于 江苏省(province)",
"correction": "实际归属: 南京市 属于 江苏省(province)",
"evidence": {
"actual_parent_id": "320000",
"actual_parent_name": "江苏省",
"actual_parent_level": "province"
}
}这种结构化返回使 LLM 能理解错误原因并自我修正——这就是地理约束驱动的推理精炼环。
地名消歧(多语言)
resolve_place 支持三级匹配:
中文精确/模糊匹配 — "南京"、"南京市"、"江宁"
拼音匹配 — "nanjing"、"jss"(首字母)、"jiangsusheng"
英文别名匹配 — "Nanjing"、"Jiangsu"、"Canton"
拼音索引使用 pinyin-pro 库在启动时构建,英文别名覆盖所有省和主要地级市(~60 个)。
Skills
GeoRiddle(地理谜题推理)
skills/geo-riddle/SKILL.md 定义地理谜题推理工作流。LLM 解谜时自动调用 MCP 工具逐步缩小候选范围:
谜题: "我是江苏省面积最小的与安徽接壤的地级市"
→ get_children("320000") → 获取江苏所有地级市
→ describe_region(每个市) → 获取面积
→ check_adjacency(每个市, "340000") → 筛选与安徽相邻的
→ 综合约束得出答案: 南京市5 道示例谜题(1-5 分难度),每道附带完整 solution trace。
GeoFactCheck(地理断言校验)
skills/geo-fact-check/SKILL.md 定义 LLM 输出中的地理断言自动扫描与校验:
扫描 — 识别包含/邻接/否定断言模式
提取 — 解析为 (subject, relation, object) 三元组
校验 — 调用
check_containment/check_adjacency报告 — verified / falsified / unverifiable 三类
支持中英文断言模式:
模式 | 示例 | 三元组 |
X属于Y | "南京属于江苏" | (南京, BELONGS_TO, 江苏) |
X与Y相邻 | "江苏与浙江相邻" | (江苏, ADJACENT_TO, 浙江) |
X不属于Y | "南京不属于浙江" | (南京, NOT_BELONGS_TO, 浙江) |
MCP Resources
mapping_knowledge/mapping_resources/ 目录下的 JSON 文件自动注册为 MCP Resource,LLM 可按需读取制图知识:
projection-advice.json— 中国地图投影选择指南(Albers / Lambert / Web Mercator)admin-level-guide.json— 行政区划层级体系及编码规则
添加新 JSON 文件即可扩展,无需代码改动。
校验机制(双层)
层级检查(KG 内存遍历):遍历
parentOf链,O(depth) ≈ 最多 4 次查找。无需数据库查询。空间回退(PostGIS
ST_Contains/ST_Touches):当 ID 不在 KG 中或需要空间验证时使用。单次查询约 16-23ms。
前置条件
Node.js 18+
PostgreSQL + PostGIS 扩展
本地 PostGIS 数据库包含中国行政区划表(
province,city,county,village)知识图谱 JSON 文件(
admin_knowledge_graph.json)
数据库表结构
项目连接的 PostGIS 数据表:
表名 | 名称列 | 区划码列 | 几何列 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
预计算表(npm run materialize 创建):
表名 | 说明 |
| 邻接关系(70 + 963 + 8134 对) |
| 包含关系闭包(预留) |
| 区域属性(人口/GDP/面积) |
安装与运行
# 安装依赖
npm install
# 首次运行:预计算邻接表(约 30 秒)
npm run materialize
# 导入属性数据(人口/GDP/面积)
npx tsx scripts/materialize-attributes.ts
# 启动 MCP 服务器
npm run dev启动后输出:
Loading knowledge graph...
Loaded 46413 nodes, 44956 edges
Connecting to PostGIS...
PostGIS connected
Registered 2 mapping resources
GeoOntology-MCP server running配置
环境变量
变量 | 默认值 | 说明 |
| (required) | 知识图谱 JSON 路径 |
|
| 制图知识资源目录 |
|
| PostgreSQL 主机 |
|
| PostgreSQL 端口 |
| (required) | 数据库名 |
| (required) | 数据库用户 |
| (required) | 数据库密码 |
Claude Desktop / Claude Code 配置
{
"mcpServers": {
"geo-ontology": {
"command": "npx",
"args": ["tsx", "/path/to/GeoOntology-MCP/src/index.ts"],
"env": {
"KG_PATH": "/path/to/admin_knowledge_graph.json",
"PGHOST": "127.0.0.1",
"PGPORT": "5432",
"PGDATABASE": "your_database",
"PGUSER": "your_user",
"PGPASSWORD": "your_password"
}
}
}
}使用示例
典型工作流:先消歧,再查询,最后校验
→ resolve_place(name="南京")
← [{ id: "320100", name: "南京市", level: "city", parentName: "江苏省", score: 104 }]
→ get_ancestors(id="320100")
← [{ id: "320000", name: "江苏省", level: "province", type: "省" }]
→ check_containment(child_id="320102", parent_id="320000")
← { valid: true, claim: "玄武区 属于 江苏省",
explanation: "层级关系确认: 玄武区(county) → 南京市(city) → 江苏省(province)" }拼音/英文搜索
→ resolve_place(name="Nanjing")
← [{ id: "320100", name: "南京市", level: "city", score: 90 }]
→ resolve_place(name="jss")
← [{ id: "320000", name: "江苏省", level: "province", score: 40 }]路径搜索
→ find_path(id_a="320000", id_b="330000")
← { found: true, path: ["320000", "330000"], pathNames: ["江苏省", "浙江省"],
hops: 1, totalBoundaryKm: 152.7 }距离与范围查询
→ measure_distance(id_a="320100", id_b="330100")
← { idA: "320100", nameA: "南京市", idB: "330100", nameB: "杭州市",
distanceKm: 267.4, method: "centroid-to-centroid" }
→ find_within(id="320100", radius_km=200)
← [{ id: "330100", name: "杭州市", distanceKm: 267.4, ... }, ...]属性对比
→ compare_regions(ids=["110000", "310000", "320000"], metric="population")
← { metric: "population", regions: [
{ id: "320000", name: "江苏省", value: 85054000, rank: 1 },
{ id: "110000", name: "北京市", value: 21893095, rank: 2 },
{ id: "310000", name: "上海市", value: 24870895, rank: 3 }
] }几何数据
→ describe_region(id="110000", include_geometry=true, geometry_tolerance_km=0.5)
← { id: "110000", name: "北京市", ..., geometry: { type: "Polygon", coordinates: [...] },
geometryToleranceKm: 0.5 }项目结构
src/
index.ts # MCP 服务器入口
data-source/
interface.ts # GeoDataSource 接口 + 所有类型定义
postgis-adapter.ts # PostGIS 实现(核心适配器)
column-map.ts # 表名/列名映射 + inferLevel()
knowledge-graph/
types.ts # KG 节点/边类型(含拼音/别名字段)
loader.ts # KG JSON 加载 + 拼音索引 + 别名合并
english-aliases.ts # 省市英文名映射 (~60个)
ontology/
hierarchy.ts # 层级遍历(getAncestors, getChildren)
resolver.ts # 地名消歧(中文/拼音/英文四级匹配)
verifier.ts # 包含关系校验(KG 层级检查)
pathfinder.ts # BFS 路径搜索(纯函数)
tools/
registration.ts # 10 个工具注册到 McpServer
resources/
registration.ts # MCP Resource 注册(mapping_knowledge)
scripts/
materialize-adjacency.ts # 邻接预计算脚本
materialize-attributes.ts # 属性数据导入脚本
skills/
geo-riddle/SKILL.md # GeoRiddle 谜题推理 skill
geo-fact-check/SKILL.md # GeoFactCheck 断言校验 skill
mapping_knowledge/
mapping_resources/ # MCP Resource JSON 文件
projection-advice.json # 投影选择指南
admin-level-guide.json # 行政区划编码规则
data/
admin-attributes.csv # 省级属性数据(人口/GDP/面积)
tests/
knowledge-graph.test.ts # KG 加载器测试
hierarchy.test.ts # 层级遍历测试
resolver.test.ts # 地名消歧测试
verifier.test.ts # 校验逻辑测试
column-map.test.ts # 列映射测试
pinyin-resolver.test.ts # 拼音消歧测试
path.test.ts # BFS 路径搜索测试
fixtures/
sample-kg.json # 测试用样本 KG可插拔数据源
所有工具通过 GeoDataSource 接口访问数据,PostGIS 只是默认实现:
interface GeoDataSource {
connect(): Promise<void>;
close(): Promise<void>;
getRegionInfo(id: string): Promise<RegionInfo | null>;
getAncestors(id: string): Promise<HierarchyNode[]>;
getChildren(id: string): Promise<HierarchyNode[]>;
getNeighbors(id: string): Promise<AdjacencyResult[]>;
checkContainment(childId: string, parentId: string): Promise<VerificationResult>;
checkAdjacency(idA: string, idB: string): Promise<VerificationResult>;
resolvePlace(name: string, context?: string): Promise<PlaceCandidate[]>;
findPath(idA: string, idB: string, maxHops?: number): Promise<PathResult>;
getRegionGeometry(id: string, toleranceKm?: number): Promise<RegionGeometry | null>;
measureDistance(idA: string, idB: string): Promise<DistanceResult | null>;
findWithin(id: string, radiusKm: number): Promise<WithinResult[]>;
getAttributes(id: string): Promise<RegionAttributes | null>;
compareRegions(ids: string[], metric: 'population' | 'gdp' | 'areaSqKm'): Promise<CompareResult>;
}测试
npm test50 个单元测试覆盖:KG 加载器、层级遍历、地名消歧(中文+拼音)、校验逻辑、列映射、BFS 路径搜索。测试使用 tests/fixtures/sample-kg.json 样本数据,无需连接数据库。
技术栈
TypeScript + ESM (
@modelcontextprotocol/sdk)pg — PostgreSQL/PostGIS 客户端
pinyin-pro — 拼音转换
zod — 工具参数校验
Vitest — 单元测试
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kevics1/GeoOntology-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server