MCP server
Thirteen read-only tools for weather forecasts, earthquakes, severe weather warnings and region lookup. The server is hosted, so most clients need only a URL.
Connecting a client
The endpoint speaks Streamable HTTP and needs no API key: https://mcp-bmkg.dhanypedia.com/mcp
One command. Also available as a plugin: claude plugin marketplace add dhanyyudi/bmkg-api, then claude plugin install bmkg@bmkg-api.
claude mcp add --transport http bmkg https://mcp-bmkg.dhanypedia.com/mcpUsing a separate API instance
The local server reads from this public instance by default. The following environment variable points it at another one:
{
"mcpServers": {
"bmkg": {
"command": "uvx",
"args": ["bmkg-api-mcp"],
"env": { "BMKG_API_BASE_URL": "http://localhost:8099/v1" }
}
}
}Tools
Weather tools need a village-level adm4_code such as 31.74.04.1006. search_regions returns one, as does walking from provinces through regencies and districts to villages.
| Tool | Arguments | Returns |
|---|---|---|
get_latest_earthquake | none | The latest earthquake |
get_recent_earthquakes | none | The last 15 earthquakes of magnitude 5.0 or more |
get_felt_earthquakes | none | The last 15 earthquakes that were felt |
get_nearby_earthquakes | latitude, longitude, radius_km | Recent earthquakes around a point |
get_weather_forecast | adm4_code | 3-day forecast in 3-hour steps |
get_current_weather | adm4_code | Forecast for the current 3-hour slot |
get_weather_warnings | language | Active severe weather warnings by province |
check_location_warnings | location, language | Active warnings that mention a place |
search_regions | query, limit | Regions matching a name, with codes |
get_provinces | none | Provinces with two-digit codes |
get_districts | province_code | Regencies and cities of a province |
get_subdistricts | district_code | Districts of a regency or city |
get_villages | subdistrict_code | Villages, with adm4 codes |
The server also offers seven prompts: earthquake, weather, region lookup, emergency preparedness, travel, research and daily briefing.
Response format
Every tool returns the same envelope. The status value is checked first.
{
"status": "success",
"data": { "magnitude": 5.4, "region": "18 km TimurLaut Pekalongan" },
"error": null,
"meta": {
"tool_version": "2.0.0",
"operation": "get_latest_earthquake",
"trace_id": "4b1f0c7e9d2a4f6c8e3b5a7d9c1e2f30",
"latency_ms": 84.2,
"source": "BMKG (Badan Meteorologi, Klimatologi, dan Geofisika)",
"fetched_at": "2026-02-16T08:30:00Z",
"cache_ttl_seconds": 60,
"served_from_cache": false,
"details": {},
"warnings": []
}
}On failure, status is "error", data is null and error.code holds one of these codes:
| Code | Meaning |
|---|---|
INVALID_ARGUMENT | The API rejected an argument |
NOT_FOUND | No data for that input, for example a code without a forecast |
RATE_LIMITED | Too many requests. A retry works after one minute |
UPSTREAM_UNAVAILABLE | BMKG or the API is temporarily unreachable |
INTERNAL_ERROR | Any other failure. meta.trace_id identifies it in a report |
Arguments that do not match a tool's schema, such as a malformed code or a latitude out of range, are rejected by the protocol before the tool runs.
Upgrading from 1.x
- Results are wrapped in the envelope above. What used to be the whole result is now under data.
- Failures used to be {"error": "<text>"}. They are now status "error" with a code.
- The debug_ping and get_cache_stats tools were removed.
- search_regions returns 20 results by default. The limit argument raises that.