@cyanheads/worldbank-mcp-server
Query 29,500+ World Bank development indicators for 200+ countries across 60+ years via MCP. STDIO or Streamable HTTP.
Tools
9 tools for browsing and querying the World Bank Open Data API:
| Tool | Description |
|---|---|
worldbank_list_topics | List all 21 World Bank thematic topics with descriptions |
worldbank_list_sources | List 70+ World Bank data sources (datasets) with pagination |
worldbank_list_countries | List countries and regional aggregates with ISO codes, region, income level, and coordinates |
worldbank_get_country | Fetch full metadata for a specific country or aggregate by ISO2, ISO3, or aggregate code |
worldbank_search_indicators | Search the 29,500+ indicator catalog by keyword, topic, or source |
worldbank_get_indicator | Fetch complete metadata for a single indicator: name, description, source, unit, and topics |
worldbank_get_data | Query indicator values for one or more countries across a time range or most-recent N values |
worldbank_get_poverty | Poverty headcount, gap, and severity at any poverty line, plus the Gini coefficient and decile shares, from the Poverty and Inequality Platform |
worldbank_search_projects | Search the World Bank lending portfolio by text, country, region, status, and board approval date |
worldbank_list_topics
List all 21 World Bank thematic topic categories.
- No input required — returns the complete fixed taxonomy
- Topics include Agriculture, Economy & Growth, Education, Energy & Mining, Environment, Financial Sector, Health, Infrastructure, Poverty, Private Sector, Public Sector, Science & Technology, Social Development, Social Protection & Labor, Trade, Urban Development, and more
- Returns topic IDs used to filter
worldbank_search_indicators
worldbank_list_sources
List the 70+ World Bank data sources (datasets).
- Paginated with configurable page size (up to 100 per page)
- Each source includes ID, name, short code, last-updated date, and data/metadata availability status
- Source IDs used to filter
worldbank_search_indicatorsby dataset origin (e.g. "2" for World Development Indicators, "6" for IDS)
worldbank_list_countries
List countries and regional aggregates with metadata.
- Returns ISO codes, World Bank region, income level, capital city, and coordinates
- Filterable by region code (EAS, ECS, LCN, MEA, NAC, SAS, SSF) and income level (LIC, LMC, UMC, HIC)
- By default returns individual countries only; set
include_aggregates=trueto include regional, income-group, and world aggregate entities - Paginated with up to 300 entries per page
worldbank_get_country
Fetch full metadata for a single country or aggregate entity.
- Accepts ISO2 (US), ISO3 (USA), or World Bank aggregate codes (EAS, HIC, WLD)
- Returns region, income level, lending type, capital city, and coordinates
- Structured error with recovery hint when code is not found
worldbank_search_indicators
Search the 29,500+ World Bank indicator catalog.
- Keyword search, topic filter, source filter — at least one required
- The upstream
searchtermparameter does not filter, so keyword matching runs locally over the full catalog (or the full selected topic/source): every term must appear in the indicator ID, name, or description, in any word order, with punctuation ignored - Exact ID or name matches rank first, then whole-phrase matches, then other ID/name matches, then description-only matches — so pasting an indicator name (
GDP (current US$)) or ID (NY.GDP.MKTP.CD) returns it as the top hit - Returns indicator IDs, names, source dataset, and thematic topics
- One row per indicator ID — the 43 indicators published under both a live source and an archived copy collapse to the live row, and
worldbank_get_indicatorresolves to the same one - Indicator IDs (e.g.
NY.GDP.PCAP.CD,SP.POP.TOTL) feed directly intoworldbank_get_data - Echoes the filters it applied (
query,topic_id,source_id) alongside the results - Paginated with up to 100 results per page
worldbank_get_indicator
Fetch complete metadata for a known indicator ID.
- Returns full description, unit of measurement, source dataset, source organization, and thematic topics
- Structured error with recovery hint when ID is not found
worldbank_get_data
Query indicator values for countries across time. The primary data-access tool.
- Single country, array of countries, regional codes (EAS, LCN, …), income codes (HIC, LMC, …), world code (WLD), or
"all"for every entry. At least one code is required — an empty value is rejected rather than read as"all" - Time filtering:
date_rangefor a period or range — annual (2020,2010:2023), quarterly (2020Q1:2021Q4), or monthly (2020M01:2020M06) — ormrvfor the N most recent available values (1–100). Mutually exclusive. - A
date_rangethe API can't apply is enforced locally over the whole series, so observations outside the requested window are never returned as matches - Returns observations with
nullvalues when data is not available for a country×year cell — common for sparse series - Includes
nullCountper page to surface data sparsity - Output grouped by country for readability;
isAggregateflag distinguishes all 78 regional, income-group, and lending-group aggregates from individual countries - Echoes the parameters it sent upstream — indicator, normalized country codes, date range or
mrv, page and page size - Paginated with up to 1000 entries per page
worldbank_get_poverty
Poverty and inequality estimates from the World Bank Poverty and Inequality Platform (PIP) — a separate dataset from the WDI series the other tools read.
- Poverty headcount ratio, poverty gap, poverty severity, and the Watts index at any poverty line, not only the published thresholds. Omit
poverty_lineto use the international line of the PIP release currently served, which is echoed back on every row - The same row carries the inequality and distribution block: Gini coefficient, mean log deviation, Wolfson polarization, and the ten decile income/consumption shares. One tool, because PIP returns poverty and inequality together and the poverty line does not affect the distribution
- Every row states how it was produced.
estimationType: "survey"rows carry the full inequality block;"interpolation","extrapolation", and"CMD estimation"rows are gap-filled estimates for years no survey covers, and theirgini,mld,polarization, anddecileSharesarenull— a gap in the source data, not an error.surveyYear,surveyAcronym, andisInterpolatedsit alongside them fill_gapsdefaults totrue, so years the surveys don't reach come back as PIP's own estimate rather than as an empty result or a hole in the series. Survey rows win wherever one exists, so a single-year batch can carry real distributions for some economies and labelled estimates for others, and a full-history query returns a row per year with the real distribution on the survey years. Setfill_gaps=falsefor survey-derived rows only- Welfare is measured in PPP dollars per person per day.
welfare_typepins results to income or consumption surveys, which are not directly comparable; thirty-five economies publish both and return a row for each - Ten economies publish an urban/rural split and return an extra row per year for it — China alone reports all three levels, the rest pair national with either urban or rural.
reporting_levelnarrows to one, and every row states its own yearaccepts a four-digit year,"all"for the full history, or"MRV"for the most recent available. Coverage starts in 1963- Individual economies only, by ISO3 code — PIP does not serve regional, income-group, or world aggregate codes through this endpoint, and a structured error says so
- Paginated locally with up to 1000 entries per page, since PIP itself has no pagination
worldbank_search_projects
The World Bank lending portfolio — the individual loans, credits, and grants the Bank finances — rather than the statistics the other tools read. A third upstream API, on its own host, with its own envelope and conventions.
- Free-text
queryacross project names, abstracts, and objectives, combined with exact filters oncountries,region,status, and a board-approval date window. Every filter is an AND, and every multi-value filter is an OR within itself - Returns the project ID, name, borrowing country and region, status, board approval and closing dates, total commitment in USD, financing windows (IBRD, IDA, Grants), major sectors, and a link to the project page
- Countries are ISO2 here (
BR,IN,ZA) — the one place this server departs from the ISO3 codes its other tools take, because the Projects API keys on ISO2. Multi-country operations carry a World Bank regional code instead (3A,4E), two characters like the rest. An ISO3 code is rejected at the schema with a message naming the difference, rather than being passed upstream where it would come back as a silent zero-hit.worldbank_get_countryreports a country'siso2field for either form statusis a closed set (Active,Closed,Dropped,Pipeline) andregionis the nine World Bank operational regions, both enforced by the schema — a typo in either can't reach upstream and read back as "no results"- An empty result says which filter emptied it. When a country filter was in force, the search re-runs it on its own: either no project carries those codes at all, or they match as a set and the remaining filters are what narrowed the result to nothing, and the notice names them
include_abstractis off by default. Abstracts run to a median of roughly 1,200 characters and are published for about half the portfolio, so a full page of them roughly doubles the response — turn it on once the result set is small enough to read- Offset-paginated with up to 1000 entries per page, which is also the most the API returns for one request
Resources
| Type | Name | Description |
|---|---|---|
| Resource | worldbank://indicator/{indicatorId} | Indicator metadata by ID — name, description, source, unit, and topics |
| Resource | worldbank://country/{countryCode} | Country metadata by ISO2, ISO3, or aggregate code — region, income level, capital, coordinates |
Both resources return a structured not-found error with a recovery hint for an unknown ID or code. An upstream outage, timeout, or 5xx keeps its own classification, so a transient failure is distinguishable from a bad identifier.
Features
Built on @cyanheads/mcp-ts-core:
- Declarative tool definitions — single file per tool, framework handles registration and validation
- Unified error handling across all tools
- Pluggable auth (
none,jwt,oauth) - Swappable storage backends:
in-memory,filesystem,Supabase,Cloudflare KV/R2/D1 - Structured logging with optional OpenTelemetry tracing
- Runs locally (stdio/HTTP) or on Cloudflare Workers from the same codebase
World Bank-specific:
- Full World Bank Open Data API v2 coverage — topics, sources, countries, indicators, and observations
- 60+ years of development data across 29,500+ indicators for 200+ countries and regional aggregates
- Client-side topic/source + keyword compound filtering (works around upstream API limitation)
- Null-value transparency —
nullobservations andnullCountsurfaced rather than silently dropped isAggregateflag on every country/data row to distinguish individual countries from aggregate entities
Agent-friendly output:
- Tool cross-references woven into descriptions — e.g.
worldbank_search_indicatorsnamesworldbank_list_topicsfor topic IDs,worldbank_get_datanamesworldbank_search_indicatorsfor indicator discovery - Structured error contracts with typed
reasoncodes and actionablerecoveryhints on every tool - Consistent pagination metadata (
page,pages,total) across all list/search/data tools
Getting started
Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"worldbank-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/worldbank-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"worldbank-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/worldbank-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.3.0 or higher (or Node.js ≥24.0.0).
- No API key required — the World Bank Open Data API is public and unauthenticated.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/worldbank-mcp-server.git
- Navigate into the directory:
cd worldbank-mcp-server
- Install dependencies:
bun install
Configuration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Environment variables:
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_HOST | HTTP server hostname | 127.0.0.1 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path where the MCP server is mounted | /mcp |
MCP_PUBLIC_URL | Public origin override for TLS-terminating reverse-proxy deployments | none |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error) | info |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1 | in-memory |
OTEL_ENABLED | Enable OpenTelemetry | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP exporter endpoint | none |
WORLDBANK_API_BASE_URL | World Bank Indicators API base URL override | https://api.worldbank.org/v2 |
WORLDBANK_PIP_BASE_URL | Poverty and Inequality Platform API base URL override | https://api.worldbank.org/pip/v1 |
WORLDBANK_PROJECTS_BASE_URL | Projects API base URL override | https://search.worldbank.org/api/v3 |
WORLDBANK_DEFAULT_PER_PAGE | Default page size for list/search/data operations | 50 |
WORLDBANK_CATALOG_CACHE_TTL_MS | Lifetime of the in-process reference caches — the indicator catalog behind keyword-only search and the aggregate-code set behind isAggregate; 0 disables both | 3600000 |
Running the server
Local development
-
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdio -
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more bun run test # Runs the test suite
Project structure
| Directory | Purpose |
|---|---|
src/mcp-server/tools | Tool definitions (*.tool.ts). Nine tools covering topics, sources, countries, indicators, data, poverty, and projects. |
src/mcp-server/resources | Resource definitions. Indicator and country metadata resources. |
src/services/worldbank | World Bank Indicators API service layer — API client and domain types. |
src/services/pip | Poverty and Inequality Platform API service layer — separate client and domain types. |
src/services/projects | Projects API service layer — separate client and domain types. |
src/config | Server-specific environment variable parsing and validation with Zod. |
tests/ | Unit and integration tests, mirroring the src/ structure. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor logging,ctx.statefor storage - Register new tools and resources in the
createApp()arrays
Contributing
Issues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.