Geo Explorer MCP
Uses Wikidata as a data source for regional statistics: population and area values (normalised via P2046), capitals, subdivision types, ISO 3166-2 subdivision lookups, and per-region links. The server queries Wikidata's SPARQL endpoint to resolve administrative subdivisions and enrich each region with its own population, area, capital city and identifiers.
Returns per-region and per-country Wikipedia links (alongside Wikidata IDs) so an AI assistant can point users to the corresponding articles for the country, its regions and their capitals.
Click on "Deploy 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., "@Geo Explorer MCPShow Slovakia's regions shaded by population in German"
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.
geo-explorer-mcp
An MCP server that serves country facts, administrative boundaries and regional statistics as structured data, so an AI assistant can build interactive geography instead of reciting it.
The server deliberately returns data, not prose. It hands back populations, polygons and coordinates; the model decides how to present them — as a clickable map, a comparison table, or a lesson in Slovak, German or Hungarian. Nothing in this server knows how to translate, and it does not need to.

Tools
Tool | Arguments | Returns |
|
| Capitals with coordinates, population, area, region, currencies, languages (English and native names), bordering countries, flag, Wikipedia link |
|
| Region names and a browser-fetchable GeoJSON URL, plus source, licence, vintage and data-quality warnings |
|
| Per-region population, area, capital city (with its own population and coordinates), native names, subdivision type, Wikidata and Wikipedia links |
country accepts a name in almost any language — Slovakia, Slovensko,
Magyarország, Magyarorszag (no diacritics), Deutschland, Ungarn.
The two boundary and statistics tools are designed to be joined:
get_region_details returns a match_key per region, and a join_hint
telling the model how to normalise get_map_data's names to match it.
Related MCP server: bamwor-mcp-server
Quick start
git clone https://github.com/arnienemeth/geo-explorer-mcp
cd geo-explorer-mcp
uv syncCopy .env.example to .env and fill it in:
RESTCOUNTRIES_API_KEY=rc_live_... # free: https://restcountries.com/sign-up
GEO_EXPLORER_CONTACT=https://github.com/you/your-repoGEO_EXPLORER_CONTACT goes into the User-Agent sent to Wikidata. Wikimedia
requires a contact URL or email and answers anything else with 403.
Once published to PyPI it also installs with no clone at all:
uvx geo-explorer-mcpTry it in the MCP Inspector:
uv run fastmcp dev inspector server.pyClaude Desktop
Add this to claude_desktop_config.json (on Windows,
%APPDATA%\Claude\claude_desktop_config.json), adjusting the paths:
{
"mcpServers": {
"geo-explorer": {
"command": "C:\\path\\to\\geo-explorer-mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\geo-explorer-mcp\\server.py"]
}
}
}Pointing at the project's own .venv interpreter means Claude runs exactly what
you tested, with no second environment to keep in sync. Restart Claude Desktop
fully afterwards — the config is only read at startup.
Then ask it something like:
Show me an interactive map of Slovakia's regions, shaded by population, in German.
Demo page
demo/index.html renders any of four countries from the same two sources the
server uses, joined in the browser: shading by population, area or density,
capital markers, a sortable table, and per-region links to Wikipedia and
Wikidata. One page, parameterised by country — because the server is generic.
It must be served over http, not opened as a file — a file:// page has a
null origin and the browser blocks its cross-origin fetches:
cd demo
python -m http.server 8000Regions | Joined to statistics | |
8 kraje | 8 | |
16 Bundesländer | 16 | |
4 countries | 4 | |
56 states and territories | 55 |
The single US miss is the Virgin Islands, which has no ISO 3166-2 entry in Wikidata. Regions without a match render grey rather than being dropped.
Layout
server.py entry point kept at the root (a shim)
src/geo_explorer_mcp/server.py the implementation
demo/index.html the browser demo
server.json MCP registry metadataserver.py at the root re-exports the server object, so the Claude Desktop
config, fastmcp dev inspector server.py and the probe scripts all keep working
while the package underneath stays publishable.
Tests
uv run python probe2.py # all three tools, plus the boundary/statistics join
uv run python probe_names.py # country-name resolution across languages and spellingsprobe2.py runs all four demo countries and asserts two things that previously
broke: that geojson_url is the resolved Git LFS media URL, and that at least
90% of boundary regions join to their statistics. Germany sat at 50% before
native-name matching landed.
Field notes
Things that cost real debugging time, written down so they don't cost yours.
REST Countries v3.1 is gone. Nearly every tutorial online still uses it. v5
lives at a different host, requires a bearer token, and renamed every field
(area → area.kilometers, cca3 → codes.alpha_3, and capitals is now an
array of objects, not strings).
geoBoundaries GeoJSON is stored in Git LFS, and only
media.githubusercontent.com serves it to a browser:
raw.githubusercontent.com/...returns a 131-byte LFS pointer file, not geometry.The
github.com/.../raw/...URL the API publishes 302-redirects with an emptyaccess-control-allow-origin. Browsers enforce CORS on every hop of a redirect, so a page fetching it fails withTypeError: Failed to fetcheven though the final response sendsaccess-control-allow-origin: *.
Either mistake produces a blank map with no error. get_map_data resolves the
redirect and returns the URL that actually works.
Verifying with curl does not verify browser behaviour. curl -L followed
that redirect happily and reported the permissive header on the final response.
It ignores CORS entirely. Test cross-origin fetches with an Origin header, or
in a browser.
Wikidata's area property mixes units. P2046 values are entered in square
kilometres, hectares or square metres, and the raw number carries no hint
which. Reading it directly gave Békéscsaba an area of 193,930,000 km² — it is
193.9 km², recorded in m². Use the normalised value
(p:P2046/psn:P2046), which Wikidata converts to SI base units.
ISO 3166-2 mixes administrative levels. Hungary returns 43 subdivisions: 19
counties, Budapest, and 23 cities with county rights. The cities sit inside the
counties, so their areas and populations must not be summed, and there are far
fewer boundary shapes than entries. get_region_details returns
subdivision_types and warns via data_quality_notes.
Country-name matching folds case but not diacritics. Magyarország resolves;
Magyarorszag does not. Translations (Ungarn, Hongrie) are excluded from the
name aggregate on purpose. The server works around both with a three-step
fallback: the name aggregate, then the translations endpoint, then a locally
built diacritic-folded index.
A Wikidata query that works for a small country can time out for a large
one. Resolving subdivisions via ?i wdt:P17 ?country makes Wikidata scan
everything in that country: fine for Slovakia, HTTP 504 for the United States.
Filtering on the ISO 3166-2 prefix instead — FILTER(STRSTARTS(?iso, "US-")) —
returns the same 56 rows in 0.9 seconds, because those codes are defined as
<alpha-2>-<subdivision>, so the prefix already is the country filter.
The same region has different names in each source. geoBoundaries says
Bayern, Sachsen, Thüringen; Wikidata's English labels are Bavaria,
Saxony, Thuringia. Matching on the English label alone joined 8 of Germany's
16 states. Building a key from every name variant, native names included,
joins all 16 — which is why get_region_details returns match_keys (plural).
A bounding box lies about shapes that cross the antimeridian. Alaska's
Aleutian Islands run past 180°, so its box reads -179.15 .. 179.78 — a
359-degree span. fitBounds on that zooms out to the whole globe and shrinks
the mainland to a smudge, with no error. Russia, Fiji and New Zealand share the
trap. The demo refuses any box wider than 180° and falls back to a fixed view.
The hidden attribute loses to any author display rule. It works through
the browser's default stylesheet, the weakest source there is, so an element
styled display:grid stays visible when you set hidden. Symptom: a loading
overlay that never goes away, covering a map that loaded perfectly. Guard it
with [hidden]{display:none !important}.
Some upstream region names are damaged. geoBoundaries' ADM2 names for
Slovakia are truncated and mistransliterated (Prešov → Predov, Dolný Kubín
→ Dolne Kub). The geometry is fine. get_map_data detects this and returns a
data_quality_note so a model does not present corrupted names as fact.
No # comments inside a SPARQL query that gets collapsed to one line. In
SPARQL # comments out the rest of the line; after collapsing, that is the
entire query. Symptom: HTTP 400.
Data sources
Source | Used for | Licence |
Country facts | Free tier, API key required | |
Administrative boundaries | CC BY 4.0 / ODbL, varies per country — the tool returns the actual licence per request | |
Regional statistics, capitals, links | CC0 1.0 |
Boundary licences differ by country because geoBoundaries aggregates national
sources. Slovakia's are OpenStreetMap-derived and therefore ODbL, which has
share-alike obligations that CC BY does not. get_map_data returns the licence
that applies to the data it just gave you; use that, not a hardcoded string.
Roadmap
Publish to PyPI so the server installs with
uvx geo-explorer-mcpRepair corrupted ADM2 names by joining to Wikidata
compare_countriesfor side-by-side statisticsCache boundary metadata to disk rather than in memory
Licence
MIT — see LICENSE. The data this server returns carries its own licences; see the table above.
Available Tools
3 toolsget_country_profileGet Country ProfileA
Get a factual profile of a country: capital cities with coordinates, population, area, region, currencies, languages (English and native names), bordering countries, flag and a Wikipedia link. Accepts a full or partial country name in any language, e.g. 'Slovakia', 'Deutschland' or 'ger'.
| Name | Required | Description | Default |
|---|---|---|---|
| country | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It discloses that the tool returns a factual profile and accepts full or partial country names in any language, but it omits error behavior, ambiguity resolution for partial matches, and an explicit read-only/no-side-effect statement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the tool's purpose and followed by input semantics. Every element, including the field list and examples, contributes useful information without padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read tool with an output schema, the description covers purpose and input handling adequately. It is slightly incomplete regarding partial-name resolution and sibling routing, but it gives an agent enough to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It does so well by stating that the single country parameter accepts full or partial names in any language and by giving concrete examples, though it does not clarify how ambiguous partial matches are resolved.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Get a factual profile of a country') and enumerates the returned fields, making the tool's scope clear. However, it does not explicitly distinguish this from sibling tools like get_region_details or get_map_data, so sibling differentiation is left to inference from the resource name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains accepted input forms and gives examples, but it provides no when-to-use guidance, no when-not-to-use guidance, and no comparison to sibling tools. An agent must infer usage solely from the tool name and resource.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_map_dataGet Map DataA
Get administrative boundary data for a country, for drawing an interactive map. Returns the names of each sub-national region plus a URL to simplified GeoJSON that a map library (Leaflet, D3, Mapbox) can load directly in the browser. Use level 'ADM0' for the country outline, 'ADM1' for states/provinces/regions (e.g. Slovak kraje, German Bundeslander), or 'ADM2' for counties/districts. Not every country has ADM2 data.
| Name | Required | Description | Default |
|---|---|---|---|
| level | No | ADM1 | |
| country | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden, and it does well: it explains the exact return shape (region names plus a simplified GeoJSON URL usable by Leaflet/D3/Mapbox) and warns about missing ADM2 coverage. It omits auth/rate-limit details and any note on data freshness, so it is not a full 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the first sentence, followed by return shape and then level semantics. Slightly overlong with the parenthetical map-library list and multiple regional examples, but every sentence adds usable information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read tool with an output schema, the agent has everything needed: what the call returns, what each level means, and the edge case where ADM2 is unavailable. No critical calling information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: the 'level' parameter is fully explained with accepted values and example regions, and a default of ADM1 exists in the schema. The 'country' parameter is left implicit, which is the only gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb+resource (get administrative boundary data for a country) and states the intended use (drawing an interactive map) plus what is returned. It does not explicitly differentiate itself from the siblings get_country_profile or get_region_details, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete when-to-use guidance via the level codes ('ADM0' for country outline, 'ADM1' for states/provinces, 'ADM2' for counties) with real examples, and flags a limitation ('Not every country has ADM2 data'). No explicit routing away from sibling tools is provided, which keeps it at 4.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_region_detailsGet Region DetailsA
Get population, area, capital city and reference links for each first-level region (ADM1) of a country, from Wikidata.
Complements get_map_data, which supplies the geometry but carries no statistics. Join the two on the 'match_key' field, which normalises the different naming conventions the two sources use.
Each region returns its ISO 3166-2 code, English and native names, population, area in km2, its capital city (with that city's own population and coordinates), and Wikidata plus Wikipedia URLs for further reading. Coverage is best for ADM1; many countries do not publish ISO 3166-2 codes below that level.
| Name | Required | Description | Default |
|---|---|---|---|
| country | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden; it discloses the data source (Wikidata), the returned fields, the join key, and a coverage caveat. It does not mention auth requirements, rate limits, or latency, but for a read-only lookup the disclosure is substantial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight paragraphs, front-loaded with the core purpose, then sibling differentiation, then return contents and coverage caveat. No sentence is filler and each adds distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values needn't be spelled out, yet the description covers them anyway plus the join semantics and coverage limits. The only material omission is the format of the 'country' input.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the single 'country' parameter is undocumented in the schema. The description implies it identifies a country whose ADM1 regions are fetched, but never states the expected format (ISO code vs. name vs. Wikidata ID) — a real gap given that the join key discussion shows naming conventions matter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (get statistics for ADM1 regions) and enumerates exactly what is returned: population, area, capital, ISO 3166-2 code, names, coordinates, and reference links. It also explicitly distinguishes itself from sibling get_map_data, which supplies geometry but no statistics.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing guidance: use this for statistics, use get_map_data for geometry, and join them on 'match_key' with an explanation of why that field exists (normalising naming conventions). It also states coverage limits (best for ADM1; many countries lack sub-ADM1 ISO codes).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
get_country_profile - First observed
get_map_data - First observed
get_region_details
TDQS
Scored across 3 tools
Each tool targets a distinct layer: country-level facts, map geometry, and regional statistics. The overlap between get_map_data and get_region_details is explicitly resolved through the match_key join and a clear geometry-vs-statistics separation.
All tools follow a consistent get_ + noun_phrase pattern: get_country_profile, get_map_data, get_region_details. This is predictable and easy to parse.
Three tools are well-scoped for a read-only geo explorer, each covering a core need (country profile, map data, region stats). No redundant or trivial tools are present.
Covers country profiles, ADM0/1/2 map geometry, and ADM1 statistics. Minor gaps: no direct query for a single specific region's details (returns all), and no statistics for ADM2 despite map support.
Maintenance
Related MCP Connectors
Geopolitical grounding for AI agents: country risk, forecasts, chokepoints, sanctions. Free tier.
geoBoundaries MCP — open database of political administrative boundaries.
Resolve any government entity worldwide and submit service requests. Open civic data for AI agents.
Address validation & geocoding for AI agents: 240+ countries, UK PAF, free US/CA enrichment
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables exploration of geographical data including countries, cities, states/provinces, and regions through a SQLite database. Supports searches by name, location coordinates, currency, and regional groupings with comprehensive statistical queries.6-
- AlicenseAqualityFmaintenanceMCP server providing structured geographic data for AI agents. Access 261 countries and millions of cities via Bamwor API.538 npm2MIT
- AlicenseAqualityDmaintenanceGeographic data (250+ countries, 150K+ cities), live exchange rates with 25 base currencies, and IP geolocation for AI assistants. Powered by ApogeoAPI.852 npm1MIT
- AlicenseNot gradedqualityAmaintenanceProvides complete world location data (countries, states, cities) as an MCP server for AI assistants, enabling search and retrieval of geographic information through 11 tools and 5 resources.182 npm2MIT