Continent Economics
by moha-55
README.md
# Continent Economics - MCP Server
An MCP server that answers quantitative questions about the economies of any
continent, reconciled **live** from three public APIs that do not agree with
each other.
```
Countries GraphQL ──┐
├──► reconcile (join on ISO codes, flag what fails) ──► MCP tools
World Bank ─────────┤
Frankfurter FX ─────┘
```
No API keys. No accounts. No hosted dependency. Everything is fetched at query
time from the live services - there are no cached or hardcoded figures in this
repository.
---
## Quickstart
Requires **Python 3.10+** (developed on 3.12) and an internet connection.
```bash
python -m venv .venv
```
Activate it:
```bash
# macOS / Linux
source .venv/bin/activate
```
```powershell
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
```
Install and verify:
```bash
pip install -r requirements.txt
```
```bash
python -m scripts.smoke_test
```
`smoke_test` calls all three live APIs and checks known GDP, FX and
per-capita values plus the reconciliation behaviour (country count, weekend FX
snapping, unsupported-currency refusal, name-mismatch join). If every check
passes - it prints `N/N checks passed` with no `FAIL` lines and exits 0 - the
server will work.
Optionally, run the offline tests (no network needed; covers parameter
validation, the HTTP retry policy, and ranking provenance):
```bash
python -m unittest discover -s tests
```
Start the server:
```bash
python -m src.server
```
It speaks MCP over **stdio** and will appear to hang - that is correct. It is
waiting for a client on stdin. Connect one of the clients below.
---
## Connect a client
### Option A - MCP Inspector (fastest, no config file)
In a separate terminal, from the project root. **Point it at the venv's Python
explicitly** - `npx` does not inherit an activated venv reliably, and a bare
`python` will start an interpreter that has no `mcp` installed, so the server
exits immediately and Connect fails:
```bash
npx @modelcontextprotocol/inspector .venv/Scripts/python.exe -m src.server
```
```bash
# macOS / Linux
npx @modelcontextprotocol/inspector .venv/bin/python -m src.server
```
Open the URL it prints (usually `http://localhost:6274`), click **Connect**,
then **List Tools**. Pick `country_metric`, set `country=France`, `year=2022`,
`currency=USD`, and click **Run Tool**.
### Option B - Claude Desktop
Edit the config file:
- **macOS** - `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows** - `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"continent-economics": {
"command": "/ABSOLUTE/PATH/TO/project/.venv/bin/python",
"args": ["-m", "src.server"],
"cwd": "/ABSOLUTE/PATH/TO/project"
}
}
}
```
On Windows use `"command": "C:\\ABSOLUTE\\PATH\\TO\\project\\.venv\\Scripts\\python.exe"`
with escaped backslashes.
Replace both paths with the real location of this folder, then **fully quit and
reopen Claude Desktop**. The tools appear under the tools icon in the chat box.
> Both paths must be absolute, and `cwd` must be the project root so that
> `-m src.server` resolves. There are no secrets in this config.
### Option C - Claude Code
```bash
claude mcp add continent-economics -- /ABSOLUTE/PATH/TO/project/.venv/bin/python -m src.server
```
### Option D - any other client
Command `python`, args `["-m", "src.server"]`, working directory = project root,
transport **stdio**.
---
## Questions it answers
Ask a connected AI client these in plain language:
**Exactly one correct answer**
- *"What was France's GDP in 2022 in US dollars?"* → `2,794,788,137,066.94`
- *"Convert that to euros using the 30 December 2022 rate."* → `2,620,281,565,788.48` (rate `0.93756`)
- *"What was France's GDP per capita in 2022?"* → `40,988.64` USD
- *"How many countries does the source list for Europe?"* → `44` sovereign, `48` including territories, `58` under the World Bank region
- *"What was Czechia's population in 2022?"* → `10,672,118` (the sources call it *Czechia* and *Czech Republic*; the join uses ISO codes)
**Matters of judgment** - the server answers with its assumption attached
- *"What is Europe's total GDP in euros?"* → **€22.88tn** by default (44 sovereign states, FX 2022‑12‑31→2022‑12‑30), and it will tell you that `all_listed` gives €22.90tn and `worldbank_region` gives €24.24tn.
- *"Which 5 European countries have the highest GDP per capita?"*
- *"Top 10 Asian economies by GDP in euros."*
- *"What did you exclude, and why?"*
Full live output is in [`samples/sample_run.md`](samples/sample_run.md).
---
## Tools
| Tool | Purpose |
|---|---|
| `list_continents` | Continent codes and the meaning of every policy parameter |
| `list_countries` | The countries counted for a continent, and how many |
| `country_metric` | GDP / population / GDP per capita for one country |
| `region_total` | Continent-wide GDP or population total, any currency |
| `rank_countries` | Top-N or bottom-N by a chosen metric |
| `fx_convert` | Currency conversion at a pinned historical date |
| `fx_supported_currencies` | What the FX source actually covers |
| `list_reconciliation_issues` | Everything excluded or flagged, with reasons |
| `describe_methodology` | Sources, join key, and every assumption |
Every judgment call is a parameter, not a hardcoded choice:
| Parameter | Default | Options |
|---|---|---|
| `include` | `sovereign` | `sovereign` (UN members) · `all_listed` (+ territories) · `worldbank_region` (World Bank grouping) |
| `missing_policy` | `skip` | `skip` (exclude and report) · `walk_back` (nearest earlier year) · `error` |
| `year` | `2022` | any year, or `"latest"` |
| `currency` | `USD` | ~30 ECB currencies |
| `fx_date` | 31 Dec of `year` (latest rate for `"latest"` or a year not yet over) | any `YYYY-MM-DD` from 1999-01-04 |
Works for **any** continent: `EU`, `AS`, `AF`, `NA`, `SA`, `OC`, `AN`.
---
## Assumptions in brief
- **Join on ISO 3166-1 alpha-2**, never on names - the sources disagree on
*Czechia*/*Czech Republic*, *Russia*/*Russian Federation*,
*Slovakia*/*Slovak Republic*, and a name join would silently drop Russia from
every European total.
- **78 World Bank "Aggregates" rows are always excluded** - *World*, *Euro
area*, *High income* are not countries. Counting them inflates a world total
roughly **7.5×**.
- **"Europe" defaults to the 44 UN member states.** Two other definitions are
one parameter away.
- **Missing years are never zero-filled** - the country is dropped from the
total and named in the response.
- **FX defaults to 31 December of the requested year**, snapping back to the
last business day - the same rule in every tool. `latest`, or a year that
has not ended, uses the most recent published rate instead of a future date.
The response always reports the date actually used and why.
Full reasoning, including production concerns and how this would point at real
internal systems, is in **[WRITEUP.md](WRITEUP.md)**.
---
## Repository layout
```
src/sources.py three API clients: timeouts, retries, caching
src/reconcile.py the join and the issue list ← the core of the project
src/analytics.py all arithmetic; provenance on every result
src/validation.py parameter checks with errors that name the valid options
src/server.py MCP tool definitions
scripts/smoke_test.py live end-to-end checks
scripts/generate_samples.py regenerates samples/ from a live run
tests/test_offline.py offline tests: validation, retries, provenance
samples/ reference output from an actual run
```
## Regenerating the samples
```bash
python -m scripts.generate_samples
```
## Troubleshooting
| Symptom | Cause |
|---|---|
| Server "hangs" on start | Correct - stdio servers wait for a client. |
| **Inspector "Connect" fails** | The command must point at the **venv's** Python (`.venv/Scripts/python.exe` on Windows, `.venv/bin/python` elsewhere). A bare `python` starts an interpreter without `mcp`, so the server exits before the client can connect. Check the Inspector's stderr pane for `ModuleNotFoundError: No module named 'mcp'`. |
| Claude Desktop shows no tools | Paths must be absolute; `cwd` must be the project root; fully quit and reopen the app. |
| `ModuleNotFoundError: src` | Run from the project root, or set `cwd` in the client config. |
| `ModuleNotFoundError: mcp` | You are running the system Python, not the venv's. See above. |
| `SourceError: ... failed after 3 attempts` | An upstream API is down. Each call is attempted three times (backoff, or the server's `Retry-After` on a 429); the tools return the error as data rather than crashing. |
| `SourceError: ... is rate-limiting requests` | The API asked for a wait longer than 30s. The server fails fast rather than blocking the client; try again later. |
| `ValidationError: ...` | A parameter was invalid (e.g. `top_n=0`, unknown `metric`). The message lists the valid options. |
| Slow first call | Cold cache - the World Bank country table (295 rows) is fetched once, then cached for 24h. |
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues