docs/health-variant-full.md
| Component | Status |
|---|---|
| Proto RPCs | 1 — ListDiseaseOutbreaks |
| Redis keys | 3 — health:disease-outbreaks:v1, health:vpd-tracker:realtime:v1, health:vpd-tracker:historical:v1 |
| Seed scripts | 2 — seed-disease-outbreaks.mjs, seed-vpd-tracker.mjs |
| MCP tool | None registered under get_health_data |
| Hostname variant | Not configured |
The disease outbreaks seeder is solid (WHO DON API + CDC + Outbreak News Today + ThinkGlobalHealth/ProMED, 150 geo-pinned alerts). VPD tracker has good historical WHO annual case data. Everything else is missing.
Current: WHO DON API + CDC RSS + ThinkGlobalHealth/ProMED
Enhancements needed:
https://www.ecdc.europa.eu/en/rss.xmlhttps://www.paho.org/en/rss.xmlhttps://www.statnews.com/feed/https://www.biorxiv.org/rss/current/microbiologyhttps://www.thelancet.com/rssfeed/laninf_current.xmlhealth:disease-outbreaks:v1What: Time-series case/death counts per disease/country — gives the chart line, not just the dot on the map.
Sources:
https://ourworldindata.org/grapher/mpox-cases-and-deaths.csv (per disease)https://ghoapi.azureedge.net/api/ — indicators like MORBIDITY_DENGUE, VACCINATIONHEPB3, etc.
https://ghoapi.azureedge.net/api/{indicator}?$filter=SpatialDim eq '{ISO3}'https://data.cdc.gov/resource/{dataset}.json (Socrata)https://nextstrain.org/charon/getDataset?prefix=/ncov/gisaid/global/6mRedis key: health:epidemic-trends:v1
Seed script: seed-epidemic-trends.mjs
Cache TTL: 86400 (24h — daily refresh)
Proto RPC: ListEpidemicTrends → returns EpidemicTrendItem[]
message EpidemicTrendItem {
string disease = 1;
string country_code = 2;
string country = 3;
repeated DataPoint weekly_cases = 4; // last 12 weeks
repeated DataPoint weekly_deaths = 5;
double r_number = 6; // reproduction number (0 = unknown)
string trend = 7; // "rising" | "falling" | "stable"
string source = 8;
}
message DataPoint {
string date = 1; // YYYY-MM-DD (week start)
int32 value = 2;
}
What: Coverage rates by vaccine and country — essential for pandemic preparedness context.
Sources:
https://immunizationdata.who.int/api/v1/coverage?ANTIGEN={antigen}&YEAR={year}
Redis key: health:vaccination-coverage:v1
Seed script: seed-vaccination-coverage.mjs
Cache TTL: 604800 (7 days — weekly; WHO updates monthly)
Proto RPC: GetVaccinationCoverage → returns coverage by country + vaccine
message VaccinationCoverageItem {
string id = 1; // "{country_code}:{vaccine}:{year}"
string country_code = 2;
string country = 3;
string vaccine = 4; // "MCV1", "DTP3", etc.
int32 year = 5;
int32 coverage_pct = 6; // 0–100
string target_population = 7; // "infants", "adolescents", etc.
bool below_threshold = 8; // < 95% herd immunity threshold
}
What: PM2.5 / AQI global readings mapped to health risk zones — direct bridge between climate/environment and health.
Sources:
OPENAQ_API_KEY): https://api.openaq.org/v3/locations?limit=1000¶meters_id=2&bbox={bbox}
https://api.openaq.org/v3/sensors/{id}/measurements/dailyhttps://api.waqi.info/map/bounds/?latlng={bbox}&token={key}
WAQI_API_KEY (free tier: 1000 req/day)Redis key: health:air-quality:v1
Seed script: seed-health-air-quality.mjs
Cache TTL: 3600 (1h — hourly data available)
Proto RPC: ListAirQualityAlerts → returns stations above WHO thresholds with health risk classification
message AirQualityAlert {
string city = 1;
string country_code = 2;
double lat = 3;
double lng = 4;
double pm25 = 5; // µg/m³
int32 aqi = 6; // 0–500 US AQI scale
string risk_level = 7; // "good" | "moderate" | "unhealthy" | "hazardous"
string pollutant = 8; // primary pollutant driving AQI
int64 measured_at = 9;
string source = 10; // "OpenAQ" | "WAQI"
}
What: Emerging pathogen/variant tracking — early warning for novel strains.
Sources:
https://nextstrain.org/charon/getDataset?prefix=/flu/seasonal/h3n2/ha/2yhttps://nextstrain.org/charon/getDataset?prefix=/mpox/all-cladeshttps://www.who.int/publications/journals/weekly-epidemiological-record/rsshttps://promedmail.org/feed/Redis key: health:pathogen-surveillance:v1
Seed script: seed-pathogen-surveillance.mjs
Cache TTL: 43200 (12h)
Proto RPC: ListPathogenAlerts → returns active variant/lineage alerts with geographic spread
message PathogenAlert {
string pathogen = 1; // "H5N1", "SARS-CoV-2 XEC", "Mpox Clade Ib"
string family = 2; // "influenza", "coronavirus", "orthopoxvirus"
string alert_type = 3; // "novel_variant" | "geographic_spread" | "severity_change"
string description = 4;
repeated string countries = 5;
string who_risk_assessment = 6; // "low" | "moderate" | "high" | "unknown"
int64 published_at = 7;
string source_url = 8;
string source = 9;
}
What: Aggregated health/medical news from authoritative sources with AI tagging.
Sources (RSS, no keys):
https://www.statnews.com/feed/https://www.who.int/rss-feeds/news-english.xmlhttps://www.nih.gov/rss/news/news.rsshttps://tools.cdc.gov/api/v2/resources/media/404952.rsshttps://www.thelancet.com/rssfeed/lancet_current.xmlhttps://www.nejm.org/action/showFeed?type=etoc&feed=rsshttps://www.biorxiv.org/rss/current/microbiologyhttps://www.globalhealthnow.org/rssRedis key: health:news-intelligence:v1
Seed script: seed-health-news.mjs (or add to ais-relay.cjs as a loop)
Cache TTL: 1800 (30min)
Proto RPC: ListHealthNews → normalized news items with disease/entity tagging
| Script | Interval | Key | TTL |
|---|---|---|---|
seed-disease-outbreaks.mjs | Every 6h (existing) | health:disease-outbreaks:v1 | 72h |
seed-vpd-tracker.mjs | Daily (existing) | health:vpd-tracker:realtime:v1 | 72h |
seed-epidemic-trends.mjs | Daily | health:epidemic-trends:v1 | 24h |
seed-vaccination-coverage.mjs | Weekly (Sunday 02:00 UTC) | health:vaccination-coverage:v1 | 7 days |
seed-health-air-quality.mjs | Every 1h | health:air-quality:v1 | 1h |
seed-pathogen-surveillance.mjs | Every 12h | health:pathogen-surveillance:v1 | 24h |
seed-health-news.mjs | Every 30min (or relay loop) | health:news-intelligence:v1 | 1h |
// service.proto additions
service HealthService {
rpc ListDiseaseOutbreaks(...) // EXISTING
rpc ListEpidemicTrends(ListEpidemicTrendsRequest) returns (ListEpidemicTrendsResponse) {
option (sebuf.http.config) = {path: "/list-epidemic-trends", method: HTTP_METHOD_GET};
}
rpc GetVaccinationCoverage(GetVaccinationCoverageRequest) returns (GetVaccinationCoverageResponse) {
option (sebuf.http.config) = {path: "/get-vaccination-coverage", method: HTTP_METHOD_GET};
}
rpc ListAirQualityAlerts(ListAirQualityAlertsRequest) returns (ListAirQualityAlertsResponse) {
option (sebuf.http.config) = {path: "/list-air-quality-alerts", method: HTTP_METHOD_GET};
}
rpc ListPathogenAlerts(ListPathogenAlertsRequest) returns (ListPathogenAlertsResponse) {
option (sebuf.http.config) = {path: "/list-pathogen-alerts", method: HTTP_METHOD_GET};
}
rpc ListHealthNews(ListHealthNewsRequest) returns (ListHealthNewsResponse) {
option (sebuf.http.config) = {path: "/list-health-news", method: HTTP_METHOD_GET};
}
}
Per AGENTS.md, adding a new seeded key requires changes in 4 files:
server/_shared/cache-keys.ts — add to BOOTSTRAP_CACHE_KEYS:epidemicTrends: 'health:epidemic-trends:v1',
vaccinationCoverage: 'health:vaccination-coverage:v1',
airQuality: 'health:air-quality:v1',
pathogenSurveillance: 'health:pathogen-surveillance:v1',
healthNews: 'health:news-intelligence:v1',
api/health.js — add each key to the BOOTSTRAP_KEYS array (startup hydration on deploy)
api/mcp.ts — add keys to the relevant MCP tool's _cacheKeys array (see MCP Tool section below)
Each seed script — must call runSeed() with the correct canonical key so it writes seed-meta:<domain>:<name> automatically. The seed-meta key is required for health monitoring (_seedMetaKey in the MCP tool).
get_health_dataRegister in api/mcp.ts:
{
name: 'get_health_data',
description: 'Global health intelligence: disease outbreaks (WHO/ProMED/CDC), epidemic case trends, vaccination coverage gaps, air quality health risk, pathogen/variant surveillance, and health news.',
inputSchema: {
type: 'object',
properties: {
layer: { type: 'string', description: '"outbreaks" | "trends" | "vaccination" | "air-quality" | "pathogens" | "news" | empty for all' },
country: { type: 'string', description: 'ISO2 country code filter' },
},
required: [],
},
_cacheKeys: [
'health:disease-outbreaks:v1',
'health:vpd-tracker:realtime:v1',
'health:epidemic-trends:v1',
'health:vaccination-coverage:v1',
'health:air-quality:v1',
'health:pathogen-surveillance:v1',
'health:news-intelligence:v1',
],
_seedMetaKey: 'seed-meta:health:disease-outbreaks',
_maxStaleMin: 360,
}
health.worldmonitor.appAdd to src/config/variant.ts:
health: {
defaultPanels: ['disease-outbreaks', 'epidemic-trends', 'pathogen-alerts', 'health-news', 'vaccination-coverage', 'air-quality'],
mapLayers: ['disease-outbreaks', 'air-quality', 'vaccination-gaps'],
theme: { primaryColor: '#0099DD', accentColor: '#E53935' },
refreshIntervals: { outbreaks: 6 * 60, news: 30, airQuality: 60 },
i18n: { title: 'Health Intelligence', subtitle: 'Global Disease & Epidemic Monitoring' },
}
| Service | Key Name | Free Tier |
|---|---|---|
| WAQI (air quality) | WAQI_API_KEY | 1000 req/day (sufficient for hourly city aggregation) |
| OpenAQ v3 (air quality) | OPENAQ_API_KEY | Required by current API docs |
| WHO GHO API | None required | Free, public |
| Our World in Data | None required | Free, public CSV |
| Nextstrain | None required | Free, public JSON |
| RSS feeds (all) | None required | Public |
At least 1 new API key is required (OPENAQ_API_KEY). WAQI_API_KEY remains optional; the seed still works with OpenAQ alone.
seed-health-news.mjs — pure RSS aggregation, no key needed, fast winseed-pathogen-surveillance.mjs — Nextstrain JSON + WHO WER RSSseed-epidemic-trends.mjs — WHO GHO API (no key, daily data)seed-health-air-quality.mjs — OpenAQ (OPENAQ_API_KEY) + optional WAQIseed-vaccination-coverage.mjs — WHO immunization API (weekly, lowest priority)get_health_datahealth.worldmonitor.app