GeoAgent
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., "@GeoAgent How far is Chennai from Bengaluru in km, and what airports are within 150 km of Mumbai?"
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.
GeoAgent
An MCP server that gives LLM agents geospatial tools, plus an eval suite that measures how reliably an agent uses them.
Status: v0.2 work in progress. The tools, the agent loop, the eval runner and first results are real and run. Next: fix what the evals found and measure run-to-run variance. See the roadmap.
Why
LLMs are bad at geometry from memory. Ask one for the distance between two cities and it will give a confident, slightly wrong number. GeoAgent gives the model real tools for coordinates, distances and proximity, then measures whether it picks the right tool and returns the right answer, instead of hoping.
Related MCP server: myosm-mcp-server
Tools
Tool | What it does |
| Look up a place by name or airport code in the bundled dataset |
| Geodesic distance (WGS84) and bearings between two points |
| Places within a radius of a point, nearest first |
| The N nearest places to a point |
| Point-in-polygon test against a GeoJSON polygon |
Plus one resource (geoagent://catalog, the dataset) and one prompt (geo_question, instructions for using the tools carefully).
Planned for v0.2: elevation_profile from an open DEM, and a larger dataset (Natural Earth / OpenStreetMap).
Quick start
git clone https://github.com/AbishekFRK/geoagent.git
cd geoagent
python -m venv .venv
.venv\Scripts\activate # Windows. On macOS/Linux: source .venv/bin/activate
pip install -e ".[dev]"
pytestRun the server and try the tools in the MCP inspector:
npx @modelcontextprotocol/inspector geoagent-serverUse it from Claude Desktop or Claude Code by adding it as a stdio server:
{
"mcpServers": {
"geoagent": { "command": "geoagent-server" }
}
}Ask it a question (no API key needed)
The bundled agent talks to any tool-calling model through the OpenAI-compatible chat API. By default it uses a local Ollama model, so it is free and private:
ollama pull qwen2.5:14b
python -m geoagent.agent -v "How far is Chennai from Bengaluru, in km?"-v prints every tool call. Pick another model with --model llama3.1:8b. To use a hosted provider instead, set GEOAGENT_BASE_URL, GEOAGENT_MODEL and GEOAGENT_API_KEY.
Web UI
A small local page shows the answer, every tool call, and a map of what the agent found:
pip install -e ".[web]"
geoagent-web # then open http://127.0.0.1:8000Pick a model, click an example or type a question. Lookups, distance lines, radius circles, polygons and results are drawn on the map; each tool call can be expanded to show its arguments and raw result.


The UI follows your system light or dark theme. It shows what the tools returned next to what the model said, so you can spot a bad answer: the stat chips come from the tool results, not from the model's text.
Notes: the map tiles come from OpenStreetMap, so the map needs an internet connection (the tools and model do not). The server binds to 127.0.0.1 only and has no login, because it can drive your model server; do not expose it to a network.
Architecture
User question
|
v
Agent (any tool-calling LLM) src/geoagent/agent.py <- also used by the web UI (web.py)
| MCP (stdio)
v
GeoAgent MCP server (src/geoagent/server.py)
|
v
Pure geo functions (src/geoagent/geo.py: pyproj, shapely)
|
v
Bundled sample dataset (src/geoagent/data/sample_places.csv)The server uses the MCP Python SDK 2.x (MCPServer, called FastMCP in 1.x). The geospatial logic lives in plain functions in geo.py with no MCP dependency, so it is unit-tested directly. server.py only wraps them.
Evals
evals/cases.jsonl holds questions with objective ground truth (distances computed independently, known nearest airports, deliberate "ambiguous question" cases). The runner will report two numbers:
Tool selection accuracy: did the agent call the right tools?
Answer accuracy: is the final answer within tolerance of the ground truth?
Run them yourself (needs Ollama with the models pulled):
python evals/run_evals.py --models qwen2.5:14b llama3.1:8b --markdown evals/RESULTS.mdTo check how stable a result is, add --repeats 3 (or more) to run every case several times. The report then shows each model's mean score with the lowest and highest run, and how many runs passed each case.
Results (18 cases x 3 runs, local models via Ollama, temperature 0, tightened prompt)
Model | Tool selection | Answer accuracy | Mean time per case |
| 94% | 100% | 9.3 s |
| 83% | 67% | 6.5 s |
The three runs were identical. Every case gave the same pass or fail in all three runs for both models, so the min and max of each score equal the mean. With a fixed model, prompt and temperature 0, these results are stable. That also means the failures below are systematic, not bad luck. Per-case pass counts: evals/RESULTS.md.
Did tightening the prompt help?
After the first results I tightened the system prompt and the tool descriptions (the geocode tool now says it returns coordinates only; the prompt has numbered rules). Because I tuned against the first 11 cases, those cases cannot show whether the change generalises. So I wrote 7 held-out cases ("split": "holdout" in cases.jsonl) before tuning and ran the old prompt on them as a baseline.
Held-out cases (7) | Tool selection, before | after | Answer accuracy, before | after |
| 100% | 100% | 100% | 100% |
| 71% | 86% | 43% | 57% |
Reading this honestly: Qwen was already perfect on the held-out cases, so there was nothing to gain. Its one new tool-selection miss, on the Atlantis case in the main set, was saying the place was not in the dataset without calling geocode to check. Llama improved a little on the held-out cases, but 7 cases is too few to call that significant, and across all 18 cases its answer accuracy did not improve: it regressed on the polygon cases and kept inventing places. On the original 11 cases, Qwen's recurring "answers distance from memory" miss is gone.
What the evals found
Qwen 2.5 14B used to skip the distance tool. For "Mysuru to Bengaluru" it geocoded both places, then answered from memory ("149 km", "152 km") instead of calling
distance_bearing(true: 127.9 km). It failed this in two runs, passed in an earlier run, and passes it after the prompt change.Llama 3.1 8B invents places and answers. For ambiguous or unknown places ("the two airports", "Gotham") it still makes up places or coordinates and reports a distance instead of asking, even with explicit rules against it.
Llama 3.1 8B sent the polygon as a JSON string, which the tool rejected. The evals exposed this; the tool now accepts either form. After the prompt change Llama's polygon answers went wrong again for a different reason (it emitted malformed tool calls as text), so small-model reliability here is not just a prompt problem.
Llama 3.1 8B often skips
geocode, answering nearest/radius questions correctly from remembered coordinates. That counts as a tool-selection miss even when the answer is right.
A gap the evals did not catch: while testing the web UI, "Which airports are within 300 km of Hyderabad?" made qwen2.5:14b answer in Chinese and list two airports that are not in the dataset (the tool had returned only one place). That question is not in the eval set, and the 3-run results above do not predict it. It is on the roadmap as a new eval case.
Caveats: 18 cases is a small sample, so read the percentages as indicative only. The before/after tables above compare single runs from earlier in development; during that work Qwen passed the Mysuru-Bengaluru case once and failed it in the next two runs with the same prompt, which I could not explain (the final 3-run measurement shows no such variation, so it may have been an Ollama state effect). Treat any one-off difference of a single case with suspicion. Answers are scored with deterministic rules (no LLM judge), documented at the top of evals/run_evals.py. Boolean answers only count if point_in_polygon actually ran, a rule added after a first run showed an error message being misread as a "no".
Roadmap
Repo, packaging, 5 tools, resource, prompt, unit tests
First eval cases and schema checks
Agent loop (
agent.py), model-agnostic, tested with a scripted fake model and a real local modelEval runner and published results, compared across models (
qwen2.5:14bvsllama3.1:8b)Fix what the evals found (the polygon argument now accepts a JSON string) and re-run
Tighten the prompt and tool descriptions, checked on held-out cases
Repeat each case 3 times to measure variance (results were identical across runs)
Add the Hyderabad 300 km case to the evals, try an "answer in English" rule, and re-run
Try a stricter tool-call format or a stronger model for the "invents places" failures
Repeat each case several times to measure run-to-run variance
Local web UI: answer, tool-call trail and a map
elevation_profileand a larger open datasetDemo gif and tagged release
Data
The bundled sample_places.csv is a small hand-made set of Indian cities and airports with approximate coordinates, for demos and tests only. Do not use it for navigation. Larger open datasets are planned.
License
MIT
Available Tools
5 toolsdistance_bearingA
Geodesic distance in km and bearings in degrees from point 1 to point 2 (WGS84).
Use this for every distance or bearing question. Never estimate a distance from memory. Get the coordinates from geocode first.
Inputs: lat1 (required), lat2 (required), lon1 (required), lon2 (required).
features_withinA
List all places within radius_km of a point, nearest first. kind can be "city" or "airport".
Use for "what is within X km of ..." questions. Get the point from geocode first.
Inputs: lat (required), lon (required), radius_km (required), kind. Returns: result
geocodeA
Look up a place by name or airport code (e.g. "Bengaluru", "BLR") in the bundled dataset.
Returns the place's name, kind, lat and lon, or an error if nothing matches. It returns coordinates only, never distances: to get a distance or bearing, call distance_bearing next. Always call this first for any place name instead of using remembered coordinates.
Inputs: place (required).
nearest_featuresA
The n nearest places to a point. kind can be "city" or "airport".
Use for "closest" or "nearest" questions. Get the point from geocode first.
Inputs: lat (required), lon (required), n, kind. Returns: result
point_in_polygonA
True if the point is inside a GeoJSON Polygon or MultiPolygon (GeoJSON is lon, lat order).
polygon_geojson is the GeoJSON object itself or a JSON string of it.
Inputs: lat (required), lon (required), polygon_geojson (required). Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool maps to a distinct geo operation, and the descriptions explicitly route agents (e.g. 'call distance_bearing next', 'get the point from geocode first'). The only mild overlap is features_within versus nearest_features, since a small radius query can approximate an n-nearest query, but the descriptions ('within X km' vs 'closest/nearest') resolve this adequately.
All names are snake_case and readable, but the grammatical conventions are mixed: geocode is a bare verb, distance_bearing and point_in_polygon are noun phrases, and features_within/nearest_features use noun+preposition forms. There is no single predictable verb_noun pattern to extrapolate from.
Five tools is a tight, well-scoped surface for a geo lookup/measurement agent, and each tool covers a genuinely different question type (geocode, measure, radius search, nearest-n, containment). Nothing feels redundant or missing from the count perspective.
The core lifecycle of place resolution, measurement, proximity search, and polygon containment is covered with no obvious dead ends in the stated workflow. The main gap is reverse geocoding (coordinates back to a place name), which agents would have to work around by scanning features_within results.
Maintenance
Related MCP Connectors
Geocoding, routing, isochrones, traffic, weather, and place search for AI agents. 19 MCP tools.
Location tools for Claude and other MCP clients, strongest in the US and Great Britain. Geocode whole lists with a confidence score per row, filter by distance, get road distances and drive-time areas, and plan multi-driver routes with Google Maps links. Check flood zones, crime and live warnings, find nearby places and UK registers (NHS, CQC, food hygiene), and keep shareable maps. Reads and writes Google Sheets. Free: sign in with Google.
Routing, geocoding, places, matrix and map tools for location-aware AI workflows.
Free GeoNames MCP: countries, cities, POIs, distance & nearby. Remote HTTP + agent token signup.
Related MCP Servers
AlicenseBqualityCmaintenanceEnables AI agents to become geospatially intelligent assistants with tools for location search, smart routing, round trip planning, reverse geocoding, isochrone analysis, route visualization, geofence management, and interactive map display.817 npm8Apache 2.0- AlicenseAqualityDmaintenanceAn OpenStreetMap MCP server providing geocoding, routing, points of interest, map tiles, public transport, and energy infrastructure tools to enhance LLM location-based capabilities.16MIT
- AlicenseNot gradedqualityDmaintenanceExposes geospatial foundation model inference as MCP tools for LLM agents, enabling building extraction, water segmentation, object detection, change detection, zero-shot scene classification, and embeddings on CPU.3GPL 3.0
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to perform geospatial analysis, spatial data processing, vector and raster operations, coordinate transformations, spatial statistics, mapping, and access to climate, satellite, and other geospatial data through MCP-compatible clients.MIT