An MCP server that gives an AI agent access to provincial malaria surveillance and short-horizon outbreak forecasting for Angola — with the guardrails that make model output safe for an agent to act on.
# malaria-forecast-mcp
An MCP server that gives an AI agent access to provincial malaria surveillance and short-horizon outbreak forecasting for Angola — with the guardrails that make model output safe for an agent to act on.
Built by Joaquim Timóteo. The forecasting work it wraps is described in *Operational Malaria Forecasting in Angola Using Ensemble Models, Regional Clusters, and Epidemiological Memory Features* (ResearchGate, Feb 2026).
---
## Why MCP instead of a REST API
A REST endpoint gives a model a URL and hopes the prompt explains the rest. MCP ships the **contract** alongside the capability, and three consequences follow that matter for anything forecasting-shaped:
**Discovery is dynamic.** Tool schemas are read at connect time. Adding `compare_provinces` made it available to every connected client without a single prompt being rewritten.
**Provenance travels with the capability.** `malaria://model-card` is a resource the model can read *before* quoting a number — validation method, measured skill, known failure modes. With a REST API that context lives in a PDF somewhere, which is to say it does not reach the model at all.
**Refusals are structured.** Ask for a 20-week horizon and you get a typed error naming the validated range, not a plausible-looking wrong number:
```json
{
"error": "horizon_out_of_range",
"detail": "horizon_weeks must be between 1 and 8; got 20. The model was validated only to 8 weeks and will not extrapolate beyond it.",
"max_validated_horizon_weeks": 8
}
```
That last one is the whole argument. A forecasting model wired to an agent without guardrails will answer any question it is asked, including the ones it has no business answering.
---
## What it exposes
### Tools
| Tool | Purpose |
|---|---|
| `list_provinces` | All 18 provinces with epidemiological stratum (K-means burden clustering) |
| `get_incidence_history` | Weekly incidence and the rainfall driver, filtered by date range |
| `forecast_incidence` | 1–8 we …