NaijaHealth - AI Health Intelligence for Kano State, Nigeria
Agent: NaijaHealth | Dataset: DHIS2 Nigeria NHMIS Monthly Summary Form (2019) + Vaccines Utilization | State: Kano - 44 LGAs, ~800+ facilities, 12 months of data
Live app: https://naijahealth.streamlit.app
GitHub: GitHub - martynscn/NaijaHealth: Chat interface for the Nigerian Health Management Information System built with DHIS2 using Neo4j Aura Agents Β· GitHub
The Problem: Nigeria's Health Data is Trapped in Flat Tables
Nigeria's DHIS2 system collects extraordinary health data few examples include OPD attendance, ANC visits, immunization coverage, malaria cases, HIV testing, facility reporting rates for every health facility in every LGA every month. For Kano State alone, that's 44 LGAs, ~800 facilities, 80+ indicators, 12 months = over 100,000 data points.
But in its raw form, this data answers only one type of question: "What is the value of indicator X for facility Y in month Z?"
The questions health policymakers actually need to ask are relational:
- Which LGAs have high Penta 1 vaccination but suspiciously high dropout before Penta 3, and do their reporting rates suggest data quality problems?
- Which facilities have below-average ANC attendance and are also located in LGAs with poor NHMIS reporting compliance?
- Are public or private facilities more likely to report on time in Kano Municipal?
These questions cross three to four hops: from a DataValue, through an Indicator, through a Facility, through an LGA, in a single reasoning step. A relational database answers them with painful multi-join SQL. A graph answers them naturally.
The Dataset and Why a Graph is the Right Structure
Data source: DHIS2 Nigeria; Nigeria's official health management information system, which implements the WHO-aligned NHMIS Monthly Summary Form (2019 version) plus a Vaccines Utilization dataset.
Why Kano State? Kano is Nigeria's most populous state, with more health facilities than any other state and a long history of NHMIS data collection. The 44-LGA structure creates a natural three-level geographic hierarchy (State, LGA, Facility) that is inherently graph-shaped.
Why a graph and not a flat table?
In DHIS2's API, the same data record contains a data element ID, an organisation unit ID, a period string, and a numeric value. There is no explicit link between a facility and its LGA in a row; that relationship lives in a separate hierarchy API call. In a relational system, answering multi-dimensional questions requires assembling four or five joins across tables that were never designed to be queried together.
In Neo4j, the relationships are first-class citizens:
(DataValue)-[:AT_FACILITY]->(Facility)-[:IN_LGA]->(LGA)-[:IN_STATE]->(State)
(DataValue)-[:FOR_INDICATOR]->(Indicator)
(ReportingRate)-[:FOR_FACILITY]->(Facility)
(DataValue)-[:AT_LGA]->(LGA) <- for calculated LGA-level coverage percentages
A question like "which LGAs have both low BCG coverage and poor reporting compliance?" becomes a single graph traversal; no joins, no subqueries:
MATCH (l:LGA)<-[:AT_LGA]-(dv:DataValue)-[:FOR_INDICATOR]->(i:Indicator)
WHERE toLower(i.name) CONTAINS 'bcg' AND dv.period STARTS WITH '2025'
WITH l, avg(toFloat(dv.value)) AS bcg_coverage
WHERE bcg_coverage < 70
MATCH (f:Facility)-[:IN_LGA]->(l)
MATCH (r:ReportingRate)-[:FOR_FACILITY]->(f)
WHERE r.period STARTS WITH '2025'
RETURN l.name AS lga, round(bcg_coverage) AS bcg_pct,
round(avg(r.rate)) AS avg_reporting_rate
ORDER BY bcg_coverage ASC
That is a four-hop traversal that feels like a single question. This is what graphs do that tables cannot.
Graph Schema
Nodes
| Label | Key Properties | Notes |
|---|---|---|
:State |
id, name |
One node; "Kano" |
:LGA |
id, name |
44 nodes, all prefixed kn from DHIS2 |
:Facility |
id, name, type, level, ownership, embedding |
~800+ nodes; ownership derived from DHIS2 organisation groups (keywords: govt/federal/state/lga β Public; faith/ngo/mission β Private) |
:Indicator |
id, name, shortName, category, indicator_type, embedding |
80+ NHMIS data elements + calculated coverage indicators |
:DataValue |
id, value, period, period_type |
100,000+ records; period format YYYY-MM; period_type = 'monthly' |
:ReportingRate |
id, rate, rate_on_time, expected, actual, period, dataset |
dataset = 'NHMIS' or 'Vaccines' for independent compliance tracking |
Relationships
| Relationship | Direction | Meaning |
|---|---|---|
[:IN_LGA] |
Facility β LGA | Geographic containment |
[:IN_STATE] |
LGA β State | Geographic containment |
[:AT_FACILITY] |
DataValue β Facility | NHMIS facility-level monthly reading |
[:AT_LGA] |
DataValue β LGA | Calculated LGA-level coverage / dropout rates |
[:FOR_INDICATOR] |
DataValue β Indicator | What was measured |
[:FOR_FACILITY] |
ReportingRate β Facility | Compliance record per facility per month |
Vector Indexes
Two 1024-dimensional cosine similarity indexes (OpenAI text-embedding-3-small):
- facility_embeddings: enables semantic queries such as "find PHCs similar to this community health post"
- indicator_embeddings: enables queries such as "find indicators related to maternal health and skilled delivery"
The Aura Agent: 9 Tools Across All Three Tool Types
The agent (agent/naija-health.json) uses all three Aura Agent tool types: Cypher Templates, Text2Cypher, and Similarity Search. This gives it precise, reliable tools for known query patterns and the flexibility to handle completely open-ended questions.
| # | Tool | Type | What It Does |
|---|---|---|---|
| β | lga_health_overview |
Cypher Template | Top indicators for a specific LGA and year |
| β‘ | compare_lgas_by_indicator |
Cypher Template | Rank all 44 LGAs by a keyword-matched indicator |
| β’ | reporting_rates |
Cypher Template | Reporting compliance per LGA/facility, filtered by dataset |
| β£ | coverage_rates_by_lga |
Cypher Template | BCG / Penta / Measles / TT2 coverage % via [:AT_LGA] edges |
| β€ | penta_dropout_by_lga |
Cypher Template | Penta 1β3 dropout rates across all LGAs |
| β₯ | reporting_by_ownership |
Cypher Template | Public vs Private facility reporting rate breakdown |
| β¦ | health_data_query |
Text2Cypher | Ad-hoc NL β Cypher for questions outside the templates |
| β§ | find_similar_facilities |
Similarity Search | Find facilities semantically similar to a description |
| β¨ | find_similar_indicators |
Similarity Search | Find indicators semantically related to a health concept |
The agent's system prompt instructs it to always cite the LGA or facility name, explain what each indicator measures, and reason about why a number is significant; not just return it. This keeps responses anchored in public health context rather than abstract data.
A Sample Conversation: Graph-Driven Multi-Hop Reasoning
Here's a real interaction that demonstrates the kind of insight only a graph can provide:
User: Which LGAs have the highest Penta dropout rates, and do those same LGAs also have poor reporting rates?
NaijaHealth:
Looking at Penta 1β3 dropout rates across all 44 Kano LGAs for 2025β2026, the five LGAs with the highest dropout are:
LGA Penta Dropout Rate NHMIS Reporting Rate Vaccines Reporting Rate kn Rogo 38.2% 61.4% 58.9% kn Kibiya 35.7% 67.2% 63.1% kn Garun Mallam 33.1% 72.8% 70.4% kn Warawa 31.9% 79.3% 76.2% kn Bebeji 29.4% 81.1% 79.8%
A clear pattern emerges: the three LGAs with the highest Penta dropout (Rogo, Kibiya, Garun Mallam) also have the lowest facility reporting rates, below 73% on both NHMIS and Vaccines datasets. This correlation suggests the problem may not be purely vaccine hesitancy or supply chain failure; it may partly reflect reporting gaps where the second and third doses are being administered but not recorded. Rogo's 61.4% NHMIS reporting rate means almost four in ten facilities never submitted a form for that month.
Recommended next step: cross-reference the specific facilities in Rogo and Kibiya that are non-reporters. Are they predominantly public (government) or private (faith-based/NGO) facilities? Run: "Show reporting by ownership in Rogo LGA."
This answer required the agent to:
- Traverse
DataValue -[:AT_LGA]-> LGA -[:FOR_INDICATOR]-> Indicator(dropout rates) - Independently traverse
ReportingRate -[:FOR_FACILITY]-> Facility -[:IN_LGA]-> LGA(reporting compliance) - Join both result sets on LGA name
- Reason about why the correlation matters in a public health context
None of these hops are possible in a single flat query against the raw DHIS2 API response.
Architecture
What Makes This Graph-Native, Not Just Graph-Stored
Three things distinguish NaijaHealth from a system that merely loads flat data into Neo4j:
Ownership detection at load time. Facility ownership (Public or Private) is derived by matching DHIS2 organisation group names against keyword lists at load time and stored as a property on the Facility node. The reporting_by_ownership tool then traverses ReportingRate -> Facility.ownership -> LGA in a single hop, answering policy-relevant questions about public vs. private compliance that would require a separate lookup table in SQL.
Dual edge types for the same DataValue. Coverage indicators (BCG percentage, Penta percentage, dropout rates) are stored with [:AT_LGA] edges directly to LGA nodes, while NHMIS readings are stored with [:AT_FACILITY] edges. LGA-level analytical queries therefore do not need to aggregate from facilities; they traverse a pre-aggregated edge. The distinction is also carried in the dataset property on ReportingRate nodes, so NHMIS and Vaccines compliance can be compared without a join.
Vector indexes on both Facility and Indicator nodes. Semantic similarity search over health indicators means a user asking "find indicators related to skilled birth attendance" retrieves TT2 coverage, ANC visits, and institutional delivery rates without knowing the exact DHIS2 indicator names. The embedding space bridges the terminology gap between how policymakers speak and how DHIS2 names its data elements.
Current Scope and Path to National Scale
NaijaHealth currently covers Kano State; Nigeria's most populous state, with 44 LGAs and approximately 800 health facilities. The graph schema, pipeline, and agent tools are designed to be state-agnostic: loading a second state is a matter of pointing the extraction script at a different DHIS2 organisation unit ID and running the same pipeline.
When scaled to all 36 states and the FCT, NaijaHealth becomes a national health intelligence tool. At that scale it would be directly useful to:
- Nigeria Federal Ministry of Health (FMoH): for national NHMIS performance monitoring, identifying states and LGAs with persistent reporting gaps, and tracking immunization programme outcomes across the country.
- International health partners: including WHO Nigeria, UNICEF, USAID-funded programmes (like PEPFAR implementers), and the Global Fund, all of whom rely on NHMIS data for programme planning and accountability.
- Nigerian health-focused NGOs: organisations such as eHealth4everyone, Solina Centre for International Development and Research (SCIDaR), Plan International amongst others could use a national NaijaHealth instance to identify underperforming areas, target interventions, and track reporting compliance across their programme states.
- State Primary Health Care Development Agencies (SPHCDAs): each state agency could query its own LGA-level data without needing SQL or data science capacity; the agent handles the query translation.
- Researchers and journalists covering public health, maternal and child health, or disease surveillance in Nigeria.
The only engineering change required to go national is scale: more facilities, more LGAs, more DataValue nodes. The graph relationships, the agent tools, and the reasoning logic remain identical. A graph database handles this kind of hierarchical scale better than any flat schema could.
Tech Stack
| Component | Technology |
|---|---|
| Knowledge Graph | Neo4j Aura (eu-central-1) |
| Graph Agent | Neo4j Aura Agent; 6 Cypher Templates, 1 Text2Cypher, 2 Similarity Search |
| Embeddings | OpenAI text-embedding-3-small (1024 dimensions) |
| Data Source | DHIS2 Nigeria REST API |
| Pipeline | Python 3.11; requests, neo4j driver, openai, python-dotenv |
| Chat UI | Streamlit (deployed on Streamlit Community Cloud) |
| MCP Server | FastMCP (stdio); custom tools for Claude Code and Claude Desktop |
| Auth | OAuth2 client_credentials; Bearer token via HTTP Basic Auth, 30minute cache |
Aura Agent MCP endpoint: https://mcp.neo4j.io/agent?project_id=fa2ec884-6eff-48e0-bc6b-391bd018771e&agent_id=63d8f04d-9549-44c0-a38f-13ccf475847a
Data source: DHIS2 Nigeria; NHMIS Monthly Summary Form (Version 2019). Kano State, 44 LGAs, ~800+ facilities, 2025-06 to 2026-05.



