amr-climate-atlas-mcp
README.md
# AMR-Climate Atlas — Causal Attribution of Climate to Antimicrobial Resistance
Project 2 of a three-project climate-health portfolio (see
[`../harvard-dataverse-climate-health-projects.md`](../harvard-dataverse-climate-health-projects.md)).
Scoping: [`../amr-climate-causal-attribution-scoping.md`](../amr-climate-causal-attribution-scoping.md) ·
Build plan: [`../amr-climate-causal-attribution-phases.md`](../amr-climate-causal-attribution-phases.md) ·
Full write-up: [`docs/paper.md`](docs/paper.md).
**Status: all four phases complete.** EU/EEA panel (29 countries, 2000–2023, 4 pathogen-antibiotic
combinations), a properly-tested causal model, a genomic cross-check, a live Streamlit app, an MCP
server exposing the live data sources, and an automated eval-gated validation pipeline.

## The finding
A two-way (country + year) fixed-effects panel regression, with a **mandatory 200+ permutation
placebo test** on every result before it's allowed to be called significant:
| Combo | n | Coef | Parametric p | Permutation p |
|---|---|---|---|---|
| E. coli / Fluoroquinolones | 642 | +0.11 | 0.77 | 0.62 |
| E. coli / 3rd-gen cephalosporins | 642 | +0.21 | 0.54 | 0.31 |
| **K. pneumoniae / Carbapenems (2010-2022, consumption-adjusted)** | 355 | **−1.73** | **0.008** | **0.000** |
| S. aureus / MRSA | 667 | −0.13 | 0.89 | 0.69 |
**3 of 4 combinations show no weather effect anywhere.** The one exception — K. pneumoniae
carbapenem resistance, in the ESAC-Net-consumption-adjusted 2010–2022 subsample — is real
(leave-one-country-out stable, not COVID-driven) and **directionally confirmed by independent
genomic sequencing data** (NCBI Pathogen Detection/MicroBIGG-E, same negative sign, r=+0.38
correlation with the phenotypic rate), but does **not** appear in the full 2000–2023 panel, and
antibiotic consumption itself isn't significant in the model that's supposed to control for it.
Read as: a real, narrow, sample-window-specific pattern, not a confirmed causal climate→AMR link.
This result survived a real QA process — an independent testing pass caught a missing permutation
test on the consumption-adjusted model, and a later robustness check caught a confounder
column-selection bug that meant antibiotic consumption was never actually in the model as first
reported. Both are disclosed plainly in `docs/paper.md` rather than smoothed over.
## What's in this repo
```
app.py Streamlit atlas — combo explorer, causal-test caveat, rankings
mcp_server/ live-data MCP server (NASA POWER + BigQuery, not the frozen CSV)
data/
eu_eea_pilot_panel.csv joined country-year-combo panel (join_panel.py's output)
validation_baseline.json accepted "known good" snapshot for the eval-gate pipeline
raw/ gitignored raw pulls (ECDC, NASA POWER, ESAC-Net, EPI)
validation_runs/ timestamped JSON logs from validate_pipeline.py runs
docs/
phase1_access_check.md access-verification memo
phase3_scoping.md causal-design scoping, grounded in the real pilot data
phase4_scoping.md scale-up fork (WHO GLASS vs. BigQuery vs. deepen EU/EEA) + genomic cross-check
paper.md full write-up: methods, bugs found and fixed, results
src/amr_climate_atlas/
config.py country lists, centroids, DOIs, ECDC/NASA POWER/BigQuery constants
fetch_ears_net.py EARS-Net resistance rates via ECDC Atlas (fully scriptable, see below)
fetch_esac_net.py antibiotic-consumption confounder (static XLSX annexes, 2010-2022)
fetch_weather_anomaly.py daily weather per EU/EEA country centroid (NASA POWER)
fetch_confounders.py EPI 2024 tabular confounder from Harvard Dataverse
join_panel.py joins resistance + weather + EPI into the country-year panel
stage3_identification.py two-way FE model + mandatory permutation test + lag check, per combo
compare_genomic_phenotypic.py genomic (BigQuery) vs. phenotypic (EARS-Net) cross-check
validate_pipeline.py automated eval-gate: rerun + placebo-sanity-check + baseline drift check
.github/workflows/validate.yml ready-to-run CI (fires once this repo is pushed to GitHub)
```
## Two findings worth flagging up front
1. **ECDC's Surveillance Atlas export is fully scriptable, not manual-UI-only.** The export fires a
plain parameterized GET, and its `downloadTokenId` param is **not actually validated
server-side** — any value works once a session cookie is bootstrapped. `fetch_ears_net.py` pulls
the full country×year grid for a given pathogen-antibiotic combo in two HTTP requests.
2. **Dataverse's API 403s the default `python-requests` User-Agent** — undocumented anywhere, a
browser-like UA is required (see `config.DATAVERSE_REQUEST_HEADERS`).
## Quickstart
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
PYTHONPATH=src python -m amr_climate_atlas.fetch_ears_net # ECDC EARS-Net resistance rates
PYTHONPATH=src python -m amr_climate_atlas.fetch_esac_net # antibiotic consumption confounder
PYTHONPATH=src python -m amr_climate_atlas.fetch_weather_anomaly # NASA POWER, EU/EEA centroids
PYTHONPATH=src python -m amr_climate_atlas.fetch_confounders # EPI 2024 from Dataverse
PYTHONPATH=src python -m amr_climate_atlas.join_panel # join panel
PYTHONPATH=src python -m amr_climate_atlas.stage3_identification # causal model + permutation test
streamlit run app.py # the live atlas
```
## MCP server — live data, not just the frozen CSV
`mcp_server/` exposes two of this project's data sources over the Model Context Protocol, so any
MCP-compatible client (Claude Desktop, a custom agent) can query live weather or live genomic AMR
data on demand, rather than only reading `eu_eea_pilot_panel.csv`. Built on Anthropic's official
`mcp` Python SDK (not the fabricated "MCP 2.0"/"Agent Plugins" terminology an earlier draft of this
scope used — see `mcp_server/README.md` for the full terminology correction and a real API-drift
finding: the `FastMCP` class most tutorials use has been removed from the current SDK). Tested with
a genuine external-process client (`mcp_server/test_external_client.py`), not just an in-process
call — real weather data for Paris, real carbapenem-gene prevalence (16.83%) for French E. coli
isolates. See `mcp_server/README.md` for setup and a documented naming quirk (NCBI groups E. coli
under `"E.coli and Shigella"` in its own schema, not `"Escherichia coli"`).
## Automated eval-gated validation
`validate_pipeline.py` automates `stage3_identification.py`'s fit + permutation test into a
rerunnable gate: it fails if the permutation test's null distribution looks degenerate (a real
check for the shuffle logic silently breaking) or if results drift meaningfully from a stored
baseline. This is designed to have caught the confounder bug above automatically, not just when a
human happened to look closely. A GitHub Actions workflow (`.github/workflows/validate.yml`) is
ready to run this on every push, once the repo is on GitHub.
## Deferred / not fetched by default
GRDI v1.10 (raster GeoTIFFs, 932MB) and EnvClim (2.6GB geopackage) — gridded/sub-national products
disproportionate to a country-year panel. `fetch_confounders.py` prints their file listings so a
future subnational build can decide if the GIS-tooling investment is worth it.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues