io.github.cyanheads/worldbank-mcp-server
Query 29,500+ World Bank development indicators for 200+ countries across 60+ years.
Open source Repository Open in the app JSON README (API)
About
Query 29,500+ World Bank development indicators for 200+ countries across 60+ years.
Details
- Kind
- MCP servers
- Topic
- Finance & crypto
- Publisher
- cyanheads
- Origin
- official
- Category
- ferramentas
- Transport
- http
- Version
- 0.3.2
- Stars
- 3
- Last push
- 2026-08-25T07:06:31Z
- Repository state
- ativo
- Language
- TypeScript
- License
- Apache-2.0
- Added
- 2026-08-29 03:02:39
- Updated
- 2026-08-29 03:02:39
- Origin id
io.github.cyanheads/worldbank-mcp-server
README
<div align="center">
<h1>@cyanheads/worldbank-mcp-server</h1>
<p><b>Query 29,500+ World Bank development indicators for 200+ countries across 60+ years via MCP. STDIO or Streamable HTTP.</b>
<div>9 Tools • 2 Resources</div>
</p>
</div>
<div align="center">
[](./CHANGELOG.md) [](./LICENSE) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/worldbank-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
</div>
<div align="center">
[](https://github.com/cyanheads/worldbank-mcp-server/releases/latest/download/worldbank-mcp-server.mcpb) [](https://cursor.com/en/install-mcp?name=worldbank-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvd29ybGRiYW5rLW1jcC1zZXJ2ZXIiXX0=) [](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22worldbank-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads%2Fworldbank-mcp-server%22%5D%7D)
[](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
**Public Hosted Server:** [https://worldbank.caseyjhand.com/mcp](https://worldbank.caseyjhand.com/mcp)
</div>
---
## 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_indicators` by 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=true` to 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 `searchterm` parameter 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_indicator` resolves to the same one
- Indicator IDs (e.g. `NY.GDP.PCAP.CD`, `SP.POP.TOTL`) feed directly into `worldbank_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_range` for a period or range — annual (`2020`, `2010:2023`), quarterly (`2020Q1:2021Q4`), or monthly (`2020M01:2020M06`) — or `mrv` for the N most recent available values (1–100). Mutually exclusive.
- A `date_range` the 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 `null` values when data is not available for a country×year cell — common for sparse series
- Includes `nullCount` per page to surface data sparsity
- Output grouped by country for readability; `isAggregate` flag 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](https://pip.worldbank.org/) (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_line` to 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 their `gini`, `mld`, `polarization`, and `decileShares` are `null` — a gap in the source data, not an error. `surveyYear`, `surveyAcronym`, and `isInterpolated` sit alongside them
- `fill_gaps` defaults to `true`, 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. Set `fill_gaps=false` for survey-derived rows only
- Welfare is measured in PPP dollars per person per day. `welfare_type` pins 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_level` narrows to one, and every row states its own
- `year` accepts 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](https://projects.worldbank.org/) — 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 `query` across project names, abstracts, and objectives, combined with exact filters on `countries`, `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_country` reports a country's `iso2` field for either form
- `status` is a closed set (`Active`, `Closed`, `Dropped`, `Pipeline`) and `region` is 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_abstract` is 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`](https://github.com/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 — `null` observations and `nullCount` surfaced rather than silently dropped
- `isAggregate` flag 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_indicators` names `worldbank_list_topics` for topic IDs, `worldbank_get_data` names `worldbank_search_indicators` for indicator discovery
- Structured error contracts with typed `reason` codes and actionable `recovery` hints 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.
```json
{
"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):
```json
{
"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:
```sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
```
### Prerequisites
- [Bun v1.3.0](https://bun.sh/) or higher (or Node.js ≥24.0.0).
- No API key required — the World Bank Open Data API is public and unauthenticated.
### Installation
1. **Clone the repository:**
```sh
git clone https://github.com/cyanheads/worldbank-mcp-server.git
```
2. **Navigate into the directory:**
```sh
cd worldbank-mcp-server
```
3. **Install dependencies:**
```sh
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_SESSION_MODE` | HTTP session handling: `stateful`, `stateless`, or `auto`. This server holds no per-session state; `.env.example` and the Docker image both set `stateless` | `auto` (resolves to `stateful`) |
| `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**:
```sh
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
```
- **Run checks and tests**:
```sh
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`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no `try/catch` in tool logic
- Use `ctx.log` for logging, `ctx.state` for storage
- Register new tools and resources in the `createApp()` arrays
## Contributing
Issues and pull requests are welcome. Run checks and tests before submitting:
```sh
bun run devcheck
bun run test
```
## License
This project is licensed under the Apache 2.0 License. See the [LICENSE](./LICENSE) file for details.