africa-api-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/index.js +428 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Africa API
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Africa API MCP Server
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server that gives Claude direct access to the [Africa API](https://africa-api.com) — comprehensive data on all 54 African nations including economic indicators, markets, trade, government, elections, and policies.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Get an API key
|
|
8
|
+
|
|
9
|
+
Sign up at [africa-api.com](https://africa-api.com) and create an API key from your dashboard.
|
|
10
|
+
|
|
11
|
+
### 2. Connect to Claude
|
|
12
|
+
|
|
13
|
+
**Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"africa-api": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "africa-api-mcp"],
|
|
21
|
+
"env": {
|
|
22
|
+
"AFRICA_API_KEY": "your-api-key-here"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Claude Code** — add to `~/.claude/settings.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"africa-api": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["-y", "africa-api-mcp"],
|
|
37
|
+
"env": {
|
|
38
|
+
"AFRICA_API_KEY": "your-api-key-here"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Restart Claude and you're ready to go. No install step needed — `npx` handles it automatically.
|
|
46
|
+
|
|
47
|
+
## What You Can Ask Claude
|
|
48
|
+
|
|
49
|
+
Once connected, Claude can answer questions like:
|
|
50
|
+
|
|
51
|
+
- "What's the GDP of Nigeria vs South Africa over the last 10 years?"
|
|
52
|
+
- "Show me current FX rates for East African currencies"
|
|
53
|
+
- "Who is the head of state of Kenya and when did they take office?"
|
|
54
|
+
- "What elections are coming up in Africa this year?"
|
|
55
|
+
- "What are Nigeria's top 5 export products?"
|
|
56
|
+
- "Show me the policy timeline for Rwanda"
|
|
57
|
+
- "Rank African countries by life expectancy"
|
|
58
|
+
- "What stocks are listed on the Nigerian Exchange?"
|
|
59
|
+
- "Compare trade flows between Kenya and Tanzania"
|
|
60
|
+
|
|
61
|
+
## Available Tools
|
|
62
|
+
|
|
63
|
+
**40 tools** across 9 domains:
|
|
64
|
+
|
|
65
|
+
| Domain | Tools | What It Covers |
|
|
66
|
+
|--------|-------|----------------|
|
|
67
|
+
| **Countries** | 4 | Country details, profiles, real-time signals for all 54 nations |
|
|
68
|
+
| **Indicators & Data** | 4 | 127+ indicators (GDP, population, health, education, etc.), time-series queries, country rankings |
|
|
69
|
+
| **Government** | 6 | Heads of state, cabinets, leadership terms — current and historical |
|
|
70
|
+
| **Elections** | 5 | Election results, upcoming elections, country overviews |
|
|
71
|
+
| **Markets** | 7 | Stock exchanges, listed securities, price history, FX rates |
|
|
72
|
+
| **Trade** | 4 | Bilateral trade flows, top partners, product breakdowns |
|
|
73
|
+
| **Policies** | 6 | Laws, regulations, policy timelines, lifecycle events |
|
|
74
|
+
| **Sources** | 2 | Data provenance — World Bank, UN, central banks, etc. |
|
|
75
|
+
| **Geographies** | 1 | Continent / region / subregion hierarchy |
|
|
76
|
+
|
|
77
|
+
All tools are **read-only** with proper MCP safety annotations.
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
| Environment Variable | Required | Default | Description |
|
|
82
|
+
|---------------------|----------|---------|-------------|
|
|
83
|
+
| `AFRICA_API_KEY` | Yes | — | Your Africa API bearer token |
|
|
84
|
+
| `AFRICA_API_BASE_URL` | No | `https://api.africa-api.com` | Override for local development |
|
|
85
|
+
|
|
86
|
+
## Development
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/africa-api/africa-api-mcp.git
|
|
90
|
+
cd africa-api-mcp
|
|
91
|
+
npm install
|
|
92
|
+
npm run build
|
|
93
|
+
|
|
94
|
+
# Run directly
|
|
95
|
+
AFRICA_API_KEY=your-key node dist/index.js
|
|
96
|
+
|
|
97
|
+
# Test with MCP inspector
|
|
98
|
+
AFRICA_API_KEY=your-key npx @modelcontextprotocol/inspector node dist/index.js
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Africa API MCP Server (Node.js)
|
|
4
|
+
* Exposes the Africa API as tools for Claude via the Model Context Protocol.
|
|
5
|
+
* Covers: Countries, Indicators, Data, Markets, Trade, Government, Elections, Policies, Sources.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Configuration
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
const BASE_URL = process.env.AFRICA_API_BASE_URL || "https://api.africa-api.com";
|
|
14
|
+
const API_KEY = process.env.AFRICA_API_KEY || "";
|
|
15
|
+
if (!API_KEY) {
|
|
16
|
+
console.error("Warning: AFRICA_API_KEY not set. Authenticated endpoints will fail.");
|
|
17
|
+
}
|
|
18
|
+
// Safety annotations — all tools are read-only
|
|
19
|
+
const READ_ONLY = {
|
|
20
|
+
readOnlyHint: true,
|
|
21
|
+
destructiveHint: false,
|
|
22
|
+
openWorldHint: true,
|
|
23
|
+
};
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// HTTP helper
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
async function apiGet(path, params) {
|
|
28
|
+
const url = new URL(path, BASE_URL);
|
|
29
|
+
if (params) {
|
|
30
|
+
for (const [key, value] of Object.entries(params)) {
|
|
31
|
+
if (value !== undefined && value !== null) {
|
|
32
|
+
url.searchParams.set(key, String(value));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const headers = { Accept: "application/json" };
|
|
37
|
+
if (API_KEY) {
|
|
38
|
+
headers["Authorization"] = `Bearer ${API_KEY}`;
|
|
39
|
+
}
|
|
40
|
+
const resp = await fetch(url.toString(), { headers });
|
|
41
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
42
|
+
return JSON.stringify({ error: "Authentication failed. Check that AFRICA_API_KEY is set and valid." }, null, 2);
|
|
43
|
+
}
|
|
44
|
+
if (!resp.ok) {
|
|
45
|
+
return JSON.stringify({ error: `API returned ${resp.status}: ${resp.statusText}` }, null, 2);
|
|
46
|
+
}
|
|
47
|
+
const data = await resp.json();
|
|
48
|
+
return JSON.stringify(data, null, 2);
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Server setup
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
const server = new McpServer({
|
|
54
|
+
name: "Africa API",
|
|
55
|
+
version: "0.1.0",
|
|
56
|
+
});
|
|
57
|
+
// ===================================================================
|
|
58
|
+
// COUNTRIES
|
|
59
|
+
// ===================================================================
|
|
60
|
+
server.tool("list_countries", "List all 54 African countries with key facts (capital, region, area, currencies, languages)", {
|
|
61
|
+
region: z.string().optional().describe('Filter by region (e.g. "Eastern Africa", "Western Africa")'),
|
|
62
|
+
sort: z.string().default("name").describe("Sort field"),
|
|
63
|
+
page: z.number().int().min(1).optional().describe("Page number for pagination"),
|
|
64
|
+
per_page: z.number().int().min(1).max(100).default(20).describe("Items per page"),
|
|
65
|
+
}, READ_ONLY, async ({ region, sort, page, per_page }) => ({
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: await apiGet("/v1/countries", {
|
|
70
|
+
region,
|
|
71
|
+
sort,
|
|
72
|
+
...(page !== undefined ? { paginate: true, page } : {}),
|
|
73
|
+
per_page,
|
|
74
|
+
}),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
}));
|
|
78
|
+
server.tool("get_country", "Get detailed information about a specific African country including coordinates, borders, currencies, and languages", {
|
|
79
|
+
country_code: z.string().length(2).describe('ISO 3166-1 alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
80
|
+
}, READ_ONLY, async ({ country_code }) => ({
|
|
81
|
+
content: [{ type: "text", text: await apiGet(`/v1/countries/${country_code}`) }],
|
|
82
|
+
}));
|
|
83
|
+
server.tool("get_country_profile", "Get a curated profile for a country — key macro, demographic, and economic indicators", {
|
|
84
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
85
|
+
}, READ_ONLY, async ({ country_code }) => ({
|
|
86
|
+
content: [{ type: "text", text: await apiGet(`/v1/countries/${country_code}/profile`) }],
|
|
87
|
+
}));
|
|
88
|
+
server.tool("get_country_signals", "Get real-time signals for a country — macro snapshot, market data, FX rates, power status, humanitarian alerts", {
|
|
89
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
90
|
+
}, READ_ONLY, async ({ country_code }) => ({
|
|
91
|
+
content: [{ type: "text", text: await apiGet(`/v1/countries/${country_code}/signals`) }],
|
|
92
|
+
}));
|
|
93
|
+
// ===================================================================
|
|
94
|
+
// INDICATORS & DATA
|
|
95
|
+
// ===================================================================
|
|
96
|
+
server.tool("list_indicators", "List all available data indicators (127+) across categories like GDP, population, health, education, agriculture, energy, climate", {
|
|
97
|
+
category: z.string().optional().describe('Filter by category (e.g. "economy", "health", "education")'),
|
|
98
|
+
source: z.string().optional().describe("Filter by data source code"),
|
|
99
|
+
has_data: z.boolean().optional().describe("If true, only return indicators that have observations"),
|
|
100
|
+
}, READ_ONLY, async ({ category, source, has_data }) => ({
|
|
101
|
+
content: [{ type: "text", text: await apiGet("/v1/indicators", { category, source, has_data }) }],
|
|
102
|
+
}));
|
|
103
|
+
server.tool("get_indicator", "Get detailed metadata about a specific indicator including description, unit, source, and available years", {
|
|
104
|
+
metric_key: z.string().describe('Indicator key (e.g. "gdp_current_usd", "population_total", "life_expectancy")'),
|
|
105
|
+
}, READ_ONLY, async ({ metric_key }) => ({
|
|
106
|
+
content: [{ type: "text", text: await apiGet(`/v1/indicators/${metric_key}`) }],
|
|
107
|
+
}));
|
|
108
|
+
server.tool("get_indicator_rankings", "Rank African countries by a specific indicator for a given year — great for comparisons", {
|
|
109
|
+
metric_key: z.string().describe('Indicator key (e.g. "gdp_current_usd", "population_total")'),
|
|
110
|
+
year: z.number().int().min(1900).max(2200).describe("Year to rank by"),
|
|
111
|
+
source: z.string().optional().describe("Source code filter"),
|
|
112
|
+
limit: z.number().int().min(1).max(100).default(10).describe("Number of results"),
|
|
113
|
+
order: z.enum(["asc", "desc"]).default("desc").describe("Sort order"),
|
|
114
|
+
}, READ_ONLY, async ({ metric_key, year, source, limit, order }) => ({
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: await apiGet(`/v1/indicators/${metric_key}/rankings`, { year, source, limit, order }),
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
}));
|
|
122
|
+
server.tool("query_data", "Query time-series observations for any combination of countries and indicators", {
|
|
123
|
+
country_code: z.string().optional().describe('Single ISO alpha-2 code (e.g. "NG")'),
|
|
124
|
+
country_codes: z.string().optional().describe('Comma-separated codes (e.g. "NG,KE,ZA")'),
|
|
125
|
+
metric_key: z.string().optional().describe('Single indicator key (e.g. "gdp_current_usd")'),
|
|
126
|
+
metric_keys: z.string().optional().describe("Comma-separated indicator keys"),
|
|
127
|
+
category: z.string().optional().describe("Filter by indicator category"),
|
|
128
|
+
source: z.string().optional().describe("Filter by source code"),
|
|
129
|
+
year: z.number().int().optional().describe("Exact year (cannot combine with start_year/end_year)"),
|
|
130
|
+
start_year: z.number().int().optional().describe("Start of year range"),
|
|
131
|
+
end_year: z.number().int().optional().describe("End of year range"),
|
|
132
|
+
latest: z.boolean().default(false).describe("Only return most recent observation per country+indicator"),
|
|
133
|
+
limit: z.number().int().min(1).max(1000).default(100).describe("Max results"),
|
|
134
|
+
}, READ_ONLY, async (params) => ({
|
|
135
|
+
content: [{ type: "text", text: await apiGet("/v1/data", params) }],
|
|
136
|
+
}));
|
|
137
|
+
// ===================================================================
|
|
138
|
+
// GEOGRAPHIES
|
|
139
|
+
// ===================================================================
|
|
140
|
+
server.tool("list_geographies", "List geographical entities — continents, regions, subregions, and countries in a hierarchy", {
|
|
141
|
+
type: z.string().optional().describe("Filter by geography type"),
|
|
142
|
+
parent_key: z.string().optional().describe("Filter by parent geography key"),
|
|
143
|
+
country_code: z.string().optional().describe("Filter by country code"),
|
|
144
|
+
q: z.string().optional().describe("Search query"),
|
|
145
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
146
|
+
}, READ_ONLY, async (params) => ({
|
|
147
|
+
content: [{ type: "text", text: await apiGet("/v1/geographies", params) }],
|
|
148
|
+
}));
|
|
149
|
+
// ===================================================================
|
|
150
|
+
// GOVERNMENT
|
|
151
|
+
// ===================================================================
|
|
152
|
+
server.tool("get_government_overview", "Get a government overview — current head of state, head of government, and cabinet summary", {
|
|
153
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
154
|
+
}, READ_ONLY, async ({ country_code }) => ({
|
|
155
|
+
content: [{ type: "text", text: await apiGet(`/v1/government/overview/${country_code}`) }],
|
|
156
|
+
}));
|
|
157
|
+
server.tool("search_leaders", "Search African heads of state and government — current and historical", {
|
|
158
|
+
q: z.string().optional().describe("Search by name"),
|
|
159
|
+
country_code: z.string().optional().describe("Filter by country (ISO alpha-2)"),
|
|
160
|
+
current_only: z.boolean().optional().describe("Only current leaders"),
|
|
161
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
162
|
+
}, READ_ONLY, async (params) => ({
|
|
163
|
+
content: [{ type: "text", text: await apiGet("/v1/government/leaders", params) }],
|
|
164
|
+
}));
|
|
165
|
+
server.tool("get_leader", "Get detailed information about a specific leader including biography, terms, and political party", {
|
|
166
|
+
leader_wikidata_id: z.string().describe('Wikidata ID of the leader (e.g. "Q7939")'),
|
|
167
|
+
}, READ_ONLY, async ({ leader_wikidata_id }) => ({
|
|
168
|
+
content: [{ type: "text", text: await apiGet(`/v1/government/leaders/${leader_wikidata_id}`) }],
|
|
169
|
+
}));
|
|
170
|
+
server.tool("list_government_terms", "List leadership terms — who governed which country and when", {
|
|
171
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
172
|
+
leader_wikidata_id: z.string().optional().describe("Filter by leader"),
|
|
173
|
+
role_type: z.enum(["head_of_state", "head_of_government"]).optional().describe("Role type filter"),
|
|
174
|
+
current_only: z.boolean().optional().describe("Only current terms"),
|
|
175
|
+
start_date_from: z.string().optional().describe("Filter terms from date (YYYY-MM-DD)"),
|
|
176
|
+
end_date_to: z.string().optional().describe("Filter terms to date (YYYY-MM-DD)"),
|
|
177
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
178
|
+
}, READ_ONLY, async (params) => ({
|
|
179
|
+
content: [{ type: "text", text: await apiGet("/v1/government/terms", params) }],
|
|
180
|
+
}));
|
|
181
|
+
server.tool("get_cabinet", "Get the current cabinet for a country — ministers, deputy ministers, and key officials", {
|
|
182
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
183
|
+
}, READ_ONLY, async ({ country_code }) => ({
|
|
184
|
+
content: [{ type: "text", text: await apiGet(`/v1/government/cabinet/${country_code}`) }],
|
|
185
|
+
}));
|
|
186
|
+
server.tool("list_cabinet_members", "Search cabinet members across African governments", {
|
|
187
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
188
|
+
q: z.string().optional().describe("Search by name"),
|
|
189
|
+
role_category: z
|
|
190
|
+
.enum(["minister", "vice_president", "deputy_prime_minister", "attorney_general", "cabinet_secretary", "executive_officeholder", "other"])
|
|
191
|
+
.optional()
|
|
192
|
+
.describe("Filter by role"),
|
|
193
|
+
current_only: z.boolean().default(true).describe("Only current members"),
|
|
194
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
195
|
+
}, READ_ONLY, async (params) => ({
|
|
196
|
+
content: [{ type: "text", text: await apiGet("/v1/government/cabinet-members", params) }],
|
|
197
|
+
}));
|
|
198
|
+
// ===================================================================
|
|
199
|
+
// ELECTIONS
|
|
200
|
+
// ===================================================================
|
|
201
|
+
server.tool("list_elections", "List elections across Africa — filter by country, scope, status, and year range", {
|
|
202
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
203
|
+
election_scope: z.enum(["presidential", "parliamentary", "general", "other"]).optional().describe("Election scope"),
|
|
204
|
+
status: z.enum(["upcoming", "completed", "unknown"]).optional().describe("Election status"),
|
|
205
|
+
start_year: z.number().int().optional().describe("From year"),
|
|
206
|
+
end_year: z.number().int().optional().describe("To year"),
|
|
207
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
208
|
+
}, READ_ONLY, async (params) => ({
|
|
209
|
+
content: [{ type: "text", text: await apiGet("/v1/elections", params) }],
|
|
210
|
+
}));
|
|
211
|
+
server.tool("get_upcoming_elections", "Get upcoming elections across Africa sorted by date", {
|
|
212
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
213
|
+
}, READ_ONLY, async ({ limit }) => ({
|
|
214
|
+
content: [{ type: "text", text: await apiGet("/v1/elections/upcoming", { limit }) }],
|
|
215
|
+
}));
|
|
216
|
+
server.tool("get_country_elections", "Get an election overview for a country — recent and upcoming elections with results", {
|
|
217
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
218
|
+
top_limit: z.number().int().min(1).max(20).default(5).describe("Top results per category"),
|
|
219
|
+
}, READ_ONLY, async ({ country_code, top_limit }) => ({
|
|
220
|
+
content: [{ type: "text", text: await apiGet(`/v1/elections/country/${country_code}`, { top_limit }) }],
|
|
221
|
+
}));
|
|
222
|
+
server.tool("get_election", "Get detailed information about a specific election", {
|
|
223
|
+
election_wikidata_id: z.string().describe("Wikidata ID of the election"),
|
|
224
|
+
}, READ_ONLY, async ({ election_wikidata_id }) => ({
|
|
225
|
+
content: [{ type: "text", text: await apiGet(`/v1/elections/${election_wikidata_id}`) }],
|
|
226
|
+
}));
|
|
227
|
+
server.tool("get_election_results", "Get results for a specific election — candidates, parties, and vote counts", {
|
|
228
|
+
election_wikidata_id: z.string().describe("Wikidata ID of the election"),
|
|
229
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
230
|
+
}, READ_ONLY, async ({ election_wikidata_id, limit }) => ({
|
|
231
|
+
content: [{ type: "text", text: await apiGet(`/v1/elections/results/${election_wikidata_id}`, { limit }) }],
|
|
232
|
+
}));
|
|
233
|
+
// ===================================================================
|
|
234
|
+
// MARKETS
|
|
235
|
+
// ===================================================================
|
|
236
|
+
server.tool("list_exchanges", "List African stock exchanges — NGX, JSE, BRVM, Casablanca, etc.", {
|
|
237
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
238
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
239
|
+
}, READ_ONLY, async (params) => ({
|
|
240
|
+
content: [{ type: "text", text: await apiGet("/v1/markets/exchanges", params) }],
|
|
241
|
+
}));
|
|
242
|
+
server.tool("get_exchange", "Get details about a specific stock exchange", {
|
|
243
|
+
exchange_code: z.string().describe('Exchange code (e.g. "NGX", "JSE", "BRVM")'),
|
|
244
|
+
}, READ_ONLY, async ({ exchange_code }) => ({
|
|
245
|
+
content: [{ type: "text", text: await apiGet(`/v1/markets/exchanges/${exchange_code}`) }],
|
|
246
|
+
}));
|
|
247
|
+
server.tool("list_tickers", "Search listed securities (equities) across African exchanges", {
|
|
248
|
+
exchange_code: z.string().optional().describe('Filter by exchange (e.g. "NGX")'),
|
|
249
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
250
|
+
instrument_type: z.string().optional().describe("Filter by instrument type"),
|
|
251
|
+
q: z.string().optional().describe("Search by name or symbol"),
|
|
252
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
253
|
+
}, READ_ONLY, async (params) => ({
|
|
254
|
+
content: [{ type: "text", text: await apiGet("/v1/markets/tickers", params) }],
|
|
255
|
+
}));
|
|
256
|
+
server.tool("get_ticker", "Get details about a specific listed security", {
|
|
257
|
+
exchange_code: z.string().describe('Exchange code (e.g. "NGX")'),
|
|
258
|
+
symbol: z.string().describe('Ticker symbol (e.g. "DANGCEM")'),
|
|
259
|
+
}, READ_ONLY, async ({ exchange_code, symbol }) => ({
|
|
260
|
+
content: [{ type: "text", text: await apiGet(`/v1/markets/tickers/${exchange_code}/${symbol}`) }],
|
|
261
|
+
}));
|
|
262
|
+
server.tool("get_ticker_history", "Get price history (OHLC + volume) for a listed security", {
|
|
263
|
+
exchange_code: z.string().describe("Exchange code"),
|
|
264
|
+
symbol: z.string().describe("Ticker symbol"),
|
|
265
|
+
start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
266
|
+
end_date: z.string().optional().describe("End date (YYYY-MM-DD)"),
|
|
267
|
+
limit: z.number().int().min(1).max(5000).default(365).describe("Max data points"),
|
|
268
|
+
}, READ_ONLY, async ({ exchange_code, symbol, start_date, end_date, limit }) => ({
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: await apiGet(`/v1/markets/tickers/${exchange_code}/${symbol}/history`, { start_date, end_date, limit }),
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
}));
|
|
276
|
+
server.tool("get_fx_rates", "Get current FX rates for African currencies against a base currency", {
|
|
277
|
+
base_currency: z.string().length(3).default("USD").describe("Base currency (3-letter code)"),
|
|
278
|
+
quote_currencies: z.string().optional().describe('Comma-separated quote currencies (e.g. "NGN,KES,ZAR")'),
|
|
279
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
280
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
281
|
+
}, READ_ONLY, async (params) => ({
|
|
282
|
+
content: [{ type: "text", text: await apiGet("/v1/markets/fx-rates", params) }],
|
|
283
|
+
}));
|
|
284
|
+
server.tool("get_fx_rate_history", "Get historical FX rate data for a currency pair", {
|
|
285
|
+
base_currency: z.string().length(3).describe('Base currency (e.g. "USD")'),
|
|
286
|
+
quote_currency: z.string().length(3).describe('Quote currency (e.g. "NGN", "KES", "ZAR")'),
|
|
287
|
+
start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
288
|
+
end_date: z.string().optional().describe("End date (YYYY-MM-DD)"),
|
|
289
|
+
limit: z.number().int().min(1).max(5000).default(365).describe("Max data points"),
|
|
290
|
+
}, READ_ONLY, async ({ base_currency, quote_currency, start_date, end_date, limit }) => ({
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: await apiGet(`/v1/markets/fx-rates/${base_currency}/${quote_currency}/history`, { start_date, end_date, limit }),
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
}));
|
|
298
|
+
// ===================================================================
|
|
299
|
+
// TRADE
|
|
300
|
+
// ===================================================================
|
|
301
|
+
server.tool("get_trade_overview", "Get a trade overview — total exports/imports, top partners, top products, and trends", {
|
|
302
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
303
|
+
start_year: z.number().int().optional().describe("Start year"),
|
|
304
|
+
end_year: z.number().int().optional().describe("End year"),
|
|
305
|
+
top_limit: z.number().int().min(1).max(20).default(5).describe("Top items per category"),
|
|
306
|
+
}, READ_ONLY, async ({ country_code, start_year, end_year, top_limit }) => ({
|
|
307
|
+
content: [
|
|
308
|
+
{
|
|
309
|
+
type: "text",
|
|
310
|
+
text: await apiGet(`/v1/trade/overview/${country_code}`, { start_year, end_year, top_limit }),
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
}));
|
|
314
|
+
server.tool("get_trade_flows", "Query bilateral trade flows between countries — exports and imports with values", {
|
|
315
|
+
reporter_country_code: z.string().optional().describe("Reporting country (ISO alpha-2)"),
|
|
316
|
+
partner_country_code: z.string().optional().describe("Partner country code"),
|
|
317
|
+
product_code: z.string().optional().describe("HS product code"),
|
|
318
|
+
flow_type: z.enum(["export", "import"]).optional().describe("Flow type"),
|
|
319
|
+
year: z.number().int().optional().describe("Exact year"),
|
|
320
|
+
start_year: z.number().int().optional().describe("Start year"),
|
|
321
|
+
end_year: z.number().int().optional().describe("End year"),
|
|
322
|
+
limit: z.number().int().min(1).max(1000).default(100).describe("Max results"),
|
|
323
|
+
}, READ_ONLY, async (params) => ({
|
|
324
|
+
content: [{ type: "text", text: await apiGet("/v1/trade/flows", params) }],
|
|
325
|
+
}));
|
|
326
|
+
server.tool("get_trade_partners", "Get top trading partners for a country", {
|
|
327
|
+
reporter_country_code: z.string().length(2).describe("Reporting country (required)"),
|
|
328
|
+
flow_type: z.enum(["export", "import"]).optional().describe("Flow type"),
|
|
329
|
+
year: z.number().int().optional().describe("Exact year"),
|
|
330
|
+
start_year: z.number().int().optional().describe("Start year"),
|
|
331
|
+
end_year: z.number().int().optional().describe("End year"),
|
|
332
|
+
limit: z.number().int().min(1).max(1000).default(100).describe("Max results"),
|
|
333
|
+
}, READ_ONLY, async (params) => ({
|
|
334
|
+
content: [{ type: "text", text: await apiGet("/v1/trade/partners", params) }],
|
|
335
|
+
}));
|
|
336
|
+
server.tool("get_trade_products", "Get top traded products for a country — what it exports and imports", {
|
|
337
|
+
reporter_country_code: z.string().length(2).describe("Reporting country (required)"),
|
|
338
|
+
flow_type: z.enum(["export", "import"]).optional().describe("Flow type"),
|
|
339
|
+
year: z.number().int().optional().describe("Exact year"),
|
|
340
|
+
start_year: z.number().int().optional().describe("Start year"),
|
|
341
|
+
end_year: z.number().int().optional().describe("End year"),
|
|
342
|
+
limit: z.number().int().min(1).max(1000).default(100).describe("Max results"),
|
|
343
|
+
}, READ_ONLY, async (params) => ({
|
|
344
|
+
content: [{ type: "text", text: await apiGet("/v1/trade/products", params) }],
|
|
345
|
+
}));
|
|
346
|
+
// ===================================================================
|
|
347
|
+
// POLICIES
|
|
348
|
+
// ===================================================================
|
|
349
|
+
server.tool("list_policies", "Search government policies, laws, and regulations across Africa", {
|
|
350
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
351
|
+
document_type: z
|
|
352
|
+
.enum(["constitution", "law", "policy", "strategy", "regulation", "bill", "decree", "other"])
|
|
353
|
+
.optional()
|
|
354
|
+
.describe("Document type"),
|
|
355
|
+
status: z.enum(["active", "repealed", "draft", "unknown"]).optional().describe("Policy status"),
|
|
356
|
+
q: z.string().optional().describe("Search by title or content"),
|
|
357
|
+
start_year: z.number().int().optional().describe("From year"),
|
|
358
|
+
end_year: z.number().int().optional().describe("To year"),
|
|
359
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
360
|
+
}, READ_ONLY, async (params) => ({
|
|
361
|
+
content: [{ type: "text", text: await apiGet("/v1/policies", params) }],
|
|
362
|
+
}));
|
|
363
|
+
server.tool("get_policy", "Get detailed information about a specific policy, law, or regulation", {
|
|
364
|
+
policy_wikidata_id: z.string().describe("Wikidata ID of the policy"),
|
|
365
|
+
}, READ_ONLY, async ({ policy_wikidata_id }) => ({
|
|
366
|
+
content: [{ type: "text", text: await apiGet(`/v1/policies/${policy_wikidata_id}`) }],
|
|
367
|
+
}));
|
|
368
|
+
server.tool("get_country_policies", "Get a policy overview for a country — recent and notable policies grouped by type", {
|
|
369
|
+
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
370
|
+
top_limit: z.number().int().min(1).max(20).default(5).describe("Top items per category"),
|
|
371
|
+
}, READ_ONLY, async ({ country_code, top_limit }) => ({
|
|
372
|
+
content: [{ type: "text", text: await apiGet(`/v1/policies/country/${country_code}`, { top_limit }) }],
|
|
373
|
+
}));
|
|
374
|
+
server.tool("get_country_policy_timeline", "Get a chronological policy timeline for a country", {
|
|
375
|
+
country_code: z.string().length(2).describe("ISO alpha-2 code"),
|
|
376
|
+
top_limit: z.number().int().min(1).max(100).default(20).describe("Number of events"),
|
|
377
|
+
}, READ_ONLY, async ({ country_code, top_limit }) => ({
|
|
378
|
+
content: [
|
|
379
|
+
{ type: "text", text: await apiGet(`/v1/policies/country/${country_code}/timeline`, { top_limit }) },
|
|
380
|
+
],
|
|
381
|
+
}));
|
|
382
|
+
server.tool("list_policy_events", "List policy lifecycle events — when policies were announced, adopted, amended, repealed", {
|
|
383
|
+
country_code: z.string().optional().describe("Filter by country"),
|
|
384
|
+
event_type: z
|
|
385
|
+
.enum(["announced", "adopted", "implemented", "amended", "repealed", "suspended"])
|
|
386
|
+
.optional()
|
|
387
|
+
.describe("Event type"),
|
|
388
|
+
start_year: z.number().int().optional().describe("From year"),
|
|
389
|
+
end_year: z.number().int().optional().describe("To year"),
|
|
390
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
391
|
+
}, READ_ONLY, async (params) => ({
|
|
392
|
+
content: [{ type: "text", text: await apiGet("/v1/policies/events", params) }],
|
|
393
|
+
}));
|
|
394
|
+
server.tool("get_policy_events", "Get all lifecycle events for a specific policy", {
|
|
395
|
+
policy_wikidata_id: z.string().describe("Wikidata ID of the policy"),
|
|
396
|
+
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
397
|
+
}, READ_ONLY, async ({ policy_wikidata_id, limit }) => ({
|
|
398
|
+
content: [{ type: "text", text: await apiGet(`/v1/policies/${policy_wikidata_id}/events`, { limit }) }],
|
|
399
|
+
}));
|
|
400
|
+
// ===================================================================
|
|
401
|
+
// SOURCES
|
|
402
|
+
// ===================================================================
|
|
403
|
+
server.tool("list_sources", "List all data sources — World Bank, UN agencies, central banks, exchanges, etc.", {}, READ_ONLY, async () => ({
|
|
404
|
+
content: [{ type: "text", text: await apiGet("/v1/sources") }],
|
|
405
|
+
}));
|
|
406
|
+
server.tool("get_source", "Get details about a specific data source — description, URL, coverage, and update frequency", {
|
|
407
|
+
source_code: z.string().describe("Source code identifier"),
|
|
408
|
+
}, READ_ONLY, async ({ source_code }) => ({
|
|
409
|
+
content: [{ type: "text", text: await apiGet(`/v1/sources/${source_code}`) }],
|
|
410
|
+
}));
|
|
411
|
+
// ===================================================================
|
|
412
|
+
// PLATFORM
|
|
413
|
+
// ===================================================================
|
|
414
|
+
server.tool("get_platform_info", "Get Africa API platform version and status", {}, READ_ONLY, async () => ({
|
|
415
|
+
content: [{ type: "text", text: await apiGet("/v1/platform/version") }],
|
|
416
|
+
}));
|
|
417
|
+
// ---------------------------------------------------------------------------
|
|
418
|
+
// Start server
|
|
419
|
+
// ---------------------------------------------------------------------------
|
|
420
|
+
async function main() {
|
|
421
|
+
const transport = new StdioServerTransport();
|
|
422
|
+
await server.connect(transport);
|
|
423
|
+
}
|
|
424
|
+
main().catch((err) => {
|
|
425
|
+
console.error("Fatal error:", err);
|
|
426
|
+
process.exit(1);
|
|
427
|
+
});
|
|
428
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "africa-api-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Africa API — access data on all 54 African countries through Claude",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"africa-api-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"africa",
|
|
20
|
+
"api",
|
|
21
|
+
"claude",
|
|
22
|
+
"ai",
|
|
23
|
+
"data"
|
|
24
|
+
],
|
|
25
|
+
"author": "Africa API <team@startuplist.africa>",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/africa-api/africa-api-mcp.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://africa-api.com",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/africa-api/africa-api-mcp/issues"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/**/*.js",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"typescript": "^5.7.0",
|
|
48
|
+
"@types/node": "^22.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|