What the AI coding agent did, what it got right, and where a human had to step in.
Tool: Claude Code. Inputs: the Geoapify OpenAPI JSON specifications for Forward Geocoding, Places, Route Matrix and Routing, plus the Geoapify MCP documentation.
Prompt summary: see prompts/01-prototype.md.
What worked:
/v1 for geocoding, routing and
route matrix, /v2 for places.healthcare.hospital,
service.fire_station, emergency.ambulance_station, …).x-api-key header (documented in the specs’ securitySchemes) instead of
putting the key in the URL.traffic only applies to motorized modes.Points that needed care:
waypoints use lat,lon, while the Places filter /
bias and the Route Matrix location use lon,lat. This is the easiest place to make a
mistake. It is now handled in one place (geoapify.py) with a comment.filter=circle: radius has to be widened when there are few results.
This was a design choice, not something the spec suggests.contact.phone, then
datasource.raw.phone and contact:phone.All four live checks worked on the first try. The agent-written client needed no fixes.
traffic=approximated is accepted for drive and motorcycle, and phone numbers come through.
| Input | Type / mode | Result | Observation |
|---|---|---|---|
Mirpur 10, Dhaka |
hospital / drive | 5 results, 2 to 5 min | The closest on the map (a government dispensary) was not the fastest to reach. Several “hospitals” are diagnostic centres or an eye hospital: OSM tagging, not a Geoapify bug. |
ধানমন্ডি ২৭ (Bangla) |
pharmacy / drive | 3 results, 1 to 2 min | Bangla script with Bangla digits geocoded correctly to the Dhanmondi 27 (new Road 16) junction. |
23.7266, 90.3925 |
fire / motorcycle | Lalbagh Fire Station, 8 min | Result #3 is a mosque inside the Fire Service HQ tagged as a fire station: data-quality issue. Road distance is 2× the straight line (old Dhaka streets). |
Uttara Sector 7, Dhaka |
police / drive (JSON) | Uttara Thana, 46 s | The sector name geocoded to the nearest highway, not the sector centre: coarse result for informal sector names. |
Request: show nearby mosques and how big each one is, and do the same for every religion.
Update 2026-10-21: at Geoapify’s request, the agreed Batch #2 scope is emergency facilities only, so this feature was removed from
main. The code, tests and the recorded mosque session are kept on the branchfeature/places-of-worship. The findings below still count as feedback: the MCPsearch_placestool returns points only.
What was checked against the live API before writing code:
religion.place_of_worship with subcategories islam, hinduism,
christianity, buddhism, sikhism, judaism, multifaith, shinto.search_places call returns points only (name, address, distance, place_id).
There is no MCP tool for place details or building outlines. So an MCP-only agent cannot measure a
place’s size. The first MCP call also failed because the argument is radius_meters, not radius.
The MCP server’s error ("radius" is not allowed) was clear enough to fix it straight away.features=details,building) returns the place’s own outline (details)
and/or the building that contains it (building). The area comes from the polygon.Live results:
| Query | Result | Observation |
|---|---|---|
| Mirpur 10, mosques | 4 mosques, 1 to 2 min | 1 own outline (949 m²), 1 only a containing building (≤ 329 m²), 2 with no outline. Earlier test: “Shah Ali Plaza Market Masjid” got 2,083 m², which is the whole market building. This is why the containing building is shown as an upper bound. |
Dhakeshwari Temple, --sort size |
Dhakeshwari National Temple 7,902 m², then smaller shrines | 7,902 m² is the compound outline, not one building, so the label “mapped outline” is used instead of “floor area”. Unnamed shrines first showed the street name as their name (address_line1 fallback); this was fixed. |
| “BUET, Dhaka” | Geocoded to “BUET-TPHS Building” in Gulshan | Wrong: the BUET campus is in Palashi. Landmark and abbreviation geocoding is a benchmark item and feedback for Geoapify. |
No capacity or building:levels tags were found on any mosque tested, so the size is an area, not a headcount.
Ideas for the benchmark:
result_type) for sector or road-number style addresses.benchmark/run.py (20 locations). Results: benchmark/results.md,
manual review: benchmark/NOTES.md.rank.match_type and warn on match_by_city_or_disrict. A test was added.geometry_scope=steps without include_instructions, and forgetting traffic on calculate_route, which
made route and matrix times disagree. The agent instructions were updated after each one.Geoapify selected the project for Batch #2 (deadline 2026-11-15) and asked for a smaller first version: emergency facilities only, and a 10-location benchmark.
feature/places-of-worship and removed from main
(code, tests, docs, and the mosque agent session).3392669).claude -p, only Geoapify tools allowed), asked in Bangla
about Farmgate: agent/sessions/02-farmgate-claude-code-bangla.md.include_instructions properly, and on its own grouped diagnostic or eye
hospitals as “may not be a general hospital”.traffic, because the instructions never required it. The agent disclosed that
traffic was not counted. The instructions now require traffic: "approximated" for drive and motorcycle.agent/mcp_call.py.gpt-6-luna):
agent/sessions/03-mirpur-vscode-copilot.md. It gave the same answer as
Claude Code in session 01 and used traffic: "approximated" on both calls..vscode/mcp.json.
The Copilot agent read that file by itself, after a permission prompt, and so sent the key to the model. The local
file now uses the ${input:geoapify-key} prompt from agent/vscode-mcp.example.json, and the setup guide warns
against inline keys. The key must be replaced.docs/app/: a single static page (Leaflet + Geoapify REST from the browser) with a tap-to-call 999 button,
“use my location”, Bangla/English, map + route, and the same warnings as the CLI. Pure helpers tested with
node --test tests/app.test.js.traffic=approximated) 7.4 s. Changes: one 10 km Places call
instead of widening 2 → 5 → 10 km (in Mirpur the 2 km and 10 km calls returned the same 10 places); show the nearest
places by distance as soon as Places answers, then re-sort by travel time; with GPS, search first and fetch the
address label in parallel. Next run: first results 1.6 s, final ranking 2.1 s (the network was also faster then).service.fire_station).quarter, which would also have flagged
“Fire Service & Civil Defence Headquarter”. It was removed, and a test covers that case.^https://abdullah-atif\.github\.io$ and Referer ^https://abdullah-atif\.github\.io/. Regex anchors matter:
the dialog says a pattern can match any part of the origin, so a plain https://abdullah-atif.github.io would
also match https://abdullah-atif.github.io.evil.com.lang or filter. Fallback: Bangla text → /geocode/search.
Verified in the browser: “dhanmo” → 5 suggestions, ↓ + Enter picks one and runs the search; “ধানমন্ডি” → 2
suggestions through the fallback.name
and English name:en.name:en (the web app shows name:bn / Bangla name in the Bangla
interface); merge duplicates when one name is the start of the other (12 or more characters) and they are less than
300 m apart; never merge unnamed places. Tests added in both languages.traffic=approximated. The unnamed hospital-tagged
building is 486 m / 5.5 min. DMCH ranked #7, and the app only showed 5 results.healthcare.hospital places in Dhaka, no place has beds, 40 have emergency, and only a few
percent have healthcare:speciality, phone or opening_hours. Size and departments cannot be shown from the data.docs/app/facility_rules.json + classify.js (web app) and dhaka_emergency/classify.py (CLI) from it. They
agree on all 837 hospital/clinic places in Dhaka and share 30 test cases.emergency tag, the tag was the one that was wrong.major in classify.js / classify.py, shared test cases).--major and a
“Nearest major hospital” line. Agent instructions: same rule for MCP agents.classify.py labelled NITOR as a physio centre.agent/mcp_call.py (same MCP tools,
called over HTTP).traffic, 202 + id for
long jobs, 1 credit per 5 minutes). A live call answered synchronously in ~2-3 s with two MultiPolygons. App:
“10 / 15 min reach” button; CLI: --reach with --map. Both poll by id if the API answers 202.part-of gave the Dhaka Metropolitan polygon (admin_level 6). A dry run
listed the hospitals before spending isoline credits, and caught two classifier misses: the map spells
Shaheed Suhrawardy Medical College Hospital “Sohrawardy”/”Suhrawardi” (a large government hospital shown as
unverified private), and Dhaka Mohanagar General Hospital was missing from the government list. Fixed with shared
test cases, then 26 isolines (~130 credits, cached in benchmark/.cache/). Result: 28% of the area within 15 min
of a confirmed/government emergency department, 54% with medical college hospitals. Bangladesh Specialized Hospital
(confirmed) is absent from the map data; the script now reports such gaps itself.osm_type/osm_id; Share via the Web Share API with a WhatsApp fallback;
PWA: manifest, generated icons, a network-first service worker that never caches Geoapify calls, and the last
search kept in localStorage and shown with a “no internet” note (tested by making fetch fail).facility_rules.json with source and date. DMCH’s own site
was down (HTTP 500), and NIOH’s only number is staffed 8:00-14:30, so none is shown for them. Fire: 102 confirmed on
fireservice.gov.bd.name stays Bangla with lang=en
(English in name_international.en), checked with a live call before writing it down.lat,lon vs lon,lat) is a frequent AI mistake. A short
“coordinate order” note at the top of each spec would help.geometry option on search_places), so agents can
answer “how big is it?” with measure_geometry alone.