africa-api-mcp 0.1.1 → 0.3.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/dist/index.js +102 -46
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -15,10 +15,11 @@ const API_KEY = process.env.AFRICA_API_KEY || "";
|
|
|
15
15
|
if (!API_KEY) {
|
|
16
16
|
console.error("Warning: AFRICA_API_KEY not set. Authenticated endpoints will fail.");
|
|
17
17
|
}
|
|
18
|
-
// Safety annotations —
|
|
18
|
+
// Safety annotations — every tool is a read-only, idempotent GET against the API.
|
|
19
19
|
const READ_ONLY = {
|
|
20
20
|
readOnlyHint: true,
|
|
21
21
|
destructiveHint: false,
|
|
22
|
+
idempotentHint: true,
|
|
22
23
|
openWorldHint: true,
|
|
23
24
|
};
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
@@ -37,27 +38,69 @@ async function apiGet(path, params) {
|
|
|
37
38
|
if (API_KEY) {
|
|
38
39
|
headers["Authorization"] = `Bearer ${API_KEY}`;
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
+
// Throw on failure so the SDK surfaces it as a tool error (isError: true)
|
|
42
|
+
// rather than the agent mistaking an error payload for a successful result.
|
|
43
|
+
let resp;
|
|
44
|
+
try {
|
|
45
|
+
resp = await fetch(url.toString(), { headers });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
49
|
+
throw new Error(`Could not reach the Africa API at ${BASE_URL}: ${detail}. Check network connectivity.`);
|
|
50
|
+
}
|
|
41
51
|
if (resp.status === 401 || resp.status === 403) {
|
|
42
|
-
|
|
52
|
+
throw new Error(`Authentication failed (HTTP ${resp.status}). Set a valid AFRICA_API_KEY — get a free key at https://africa-api.com/dashboard.`);
|
|
43
53
|
}
|
|
44
54
|
if (!resp.ok) {
|
|
45
|
-
|
|
55
|
+
let body = "";
|
|
56
|
+
try {
|
|
57
|
+
body = (await resp.text()).slice(0, 300);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// ignore: body is best-effort context for the error message
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Africa API returned HTTP ${resp.status} ${resp.statusText} for ${path}${body ? `: ${body}` : ""}`);
|
|
46
63
|
}
|
|
64
|
+
// Compact JSON keeps responses token-efficient for the model's context.
|
|
47
65
|
const data = await resp.json();
|
|
48
|
-
return JSON.stringify(data
|
|
66
|
+
return JSON.stringify(data);
|
|
49
67
|
}
|
|
50
68
|
// ---------------------------------------------------------------------------
|
|
51
69
|
// Server setup
|
|
52
70
|
// ---------------------------------------------------------------------------
|
|
53
71
|
const server = new McpServer({
|
|
54
72
|
name: "Africa API",
|
|
55
|
-
version: "0.
|
|
73
|
+
version: "0.3.0",
|
|
56
74
|
});
|
|
75
|
+
// Register tools through a wrapper that attaches `structuredContent` (the parsed
|
|
76
|
+
// JSON object) alongside the text response, so clients that support structured
|
|
77
|
+
// output get machine-readable data without re-parsing a string. No outputSchema
|
|
78
|
+
// is declared (the SDK then skips validation and passes structuredContent
|
|
79
|
+
// through as-is), which keeps it robust across heterogeneous API responses.
|
|
80
|
+
// Handler errors throw and bypass this, surfacing as isError via the SDK.
|
|
81
|
+
const registerRaw = server.tool.bind(server);
|
|
82
|
+
function tool(name, description, paramsSchema, annotations, cb) {
|
|
83
|
+
registerRaw(name, description, paramsSchema, annotations, async (args, extra) => {
|
|
84
|
+
const res = (await cb(args, extra));
|
|
85
|
+
const first = res.content?.[0];
|
|
86
|
+
if (first?.type === "text" && typeof first.text === "string" && !res.isError) {
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(first.text);
|
|
89
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
90
|
+
res.structuredContent = parsed;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Response is not JSON (shouldn't happen for API data) — leave text-only.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return res;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
57
100
|
// ===================================================================
|
|
58
101
|
// COUNTRIES
|
|
59
102
|
// ===================================================================
|
|
60
|
-
|
|
103
|
+
tool("list_countries", "List all 54 African countries with key facts (capital, region, area, currencies, languages)", {
|
|
61
104
|
region: z.string().optional().describe('Filter by region (e.g. "Eastern Africa", "Western Africa")'),
|
|
62
105
|
sort: z.string().default("name").describe("Sort field"),
|
|
63
106
|
page: z.number().int().min(1).optional().describe("Page number for pagination"),
|
|
@@ -75,17 +118,17 @@ server.tool("list_countries", "List all 54 African countries with key facts (cap
|
|
|
75
118
|
},
|
|
76
119
|
],
|
|
77
120
|
}));
|
|
78
|
-
|
|
121
|
+
tool("get_country", "Get detailed information about a specific African country including coordinates, borders, currencies, and languages", {
|
|
79
122
|
country_code: z.string().length(2).describe('ISO 3166-1 alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
80
123
|
}, READ_ONLY, async ({ country_code }) => ({
|
|
81
124
|
content: [{ type: "text", text: await apiGet(`/v1/countries/${country_code}`) }],
|
|
82
125
|
}));
|
|
83
|
-
|
|
126
|
+
tool("get_country_profile", "Get a curated profile for a country — key macro, demographic, and economic indicators", {
|
|
84
127
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
85
128
|
}, READ_ONLY, async ({ country_code }) => ({
|
|
86
129
|
content: [{ type: "text", text: await apiGet(`/v1/countries/${country_code}/profile`) }],
|
|
87
130
|
}));
|
|
88
|
-
|
|
131
|
+
tool("get_country_signals", "Get real-time signals for a country — macro snapshot, market data, FX rates, power status, humanitarian alerts", {
|
|
89
132
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
90
133
|
}, READ_ONLY, async ({ country_code }) => ({
|
|
91
134
|
content: [{ type: "text", text: await apiGet(`/v1/countries/${country_code}/signals`) }],
|
|
@@ -93,19 +136,19 @@ server.tool("get_country_signals", "Get real-time signals for a country — macr
|
|
|
93
136
|
// ===================================================================
|
|
94
137
|
// INDICATORS & DATA
|
|
95
138
|
// ===================================================================
|
|
96
|
-
|
|
139
|
+
tool("list_indicators", "List all available data indicators (127+) across categories like GDP, population, health, education, agriculture, energy, climate", {
|
|
97
140
|
category: z.string().optional().describe('Filter by category (e.g. "economy", "health", "education")'),
|
|
98
141
|
source: z.string().optional().describe("Filter by data source code"),
|
|
99
142
|
has_data: z.boolean().optional().describe("If true, only return indicators that have observations"),
|
|
100
143
|
}, READ_ONLY, async ({ category, source, has_data }) => ({
|
|
101
144
|
content: [{ type: "text", text: await apiGet("/v1/indicators", { category, source, has_data }) }],
|
|
102
145
|
}));
|
|
103
|
-
|
|
146
|
+
tool("get_indicator", "Get detailed metadata about a specific indicator including description, unit, source, and available years", {
|
|
104
147
|
metric_key: z.string().describe('Indicator key (e.g. "gdp_current_usd", "population_total", "life_expectancy")'),
|
|
105
148
|
}, READ_ONLY, async ({ metric_key }) => ({
|
|
106
149
|
content: [{ type: "text", text: await apiGet(`/v1/indicators/${metric_key}`) }],
|
|
107
150
|
}));
|
|
108
|
-
|
|
151
|
+
tool("get_indicator_rankings", "Rank African countries by a specific indicator for a given year — great for comparisons", {
|
|
109
152
|
metric_key: z.string().describe('Indicator key (e.g. "gdp_current_usd", "population_total")'),
|
|
110
153
|
year: z.number().int().min(1900).max(2200).describe("Year to rank by"),
|
|
111
154
|
source: z.string().optional().describe("Source code filter"),
|
|
@@ -119,7 +162,7 @@ server.tool("get_indicator_rankings", "Rank African countries by a specific indi
|
|
|
119
162
|
},
|
|
120
163
|
],
|
|
121
164
|
}));
|
|
122
|
-
|
|
165
|
+
tool("query_data", "Query time-series observations for any combination of countries and indicators", {
|
|
123
166
|
country_code: z.string().optional().describe('Single ISO alpha-2 code (e.g. "NG")'),
|
|
124
167
|
country_codes: z.string().optional().describe('Comma-separated codes (e.g. "NG,KE,ZA")'),
|
|
125
168
|
metric_key: z.string().optional().describe('Single indicator key (e.g. "gdp_current_usd")'),
|
|
@@ -137,7 +180,7 @@ server.tool("query_data", "Query time-series observations for any combination of
|
|
|
137
180
|
// ===================================================================
|
|
138
181
|
// GEOGRAPHIES
|
|
139
182
|
// ===================================================================
|
|
140
|
-
|
|
183
|
+
tool("list_geographies", "List geographical entities — continents, regions, subregions, and countries in a hierarchy", {
|
|
141
184
|
type: z.string().optional().describe("Filter by geography type"),
|
|
142
185
|
parent_key: z.string().optional().describe("Filter by parent geography key"),
|
|
143
186
|
country_code: z.string().optional().describe("Filter by country code"),
|
|
@@ -149,12 +192,12 @@ server.tool("list_geographies", "List geographical entities — continents, regi
|
|
|
149
192
|
// ===================================================================
|
|
150
193
|
// GOVERNMENT
|
|
151
194
|
// ===================================================================
|
|
152
|
-
|
|
195
|
+
tool("get_government_overview", "Get a government overview — current head of state, head of government, and cabinet summary", {
|
|
153
196
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
154
197
|
}, READ_ONLY, async ({ country_code }) => ({
|
|
155
198
|
content: [{ type: "text", text: await apiGet(`/v1/government/overview/${country_code}`) }],
|
|
156
199
|
}));
|
|
157
|
-
|
|
200
|
+
tool("search_leaders", "Search African heads of state and government — current and historical", {
|
|
158
201
|
q: z.string().optional().describe("Search by name"),
|
|
159
202
|
country_code: z.string().optional().describe("Filter by country (ISO alpha-2)"),
|
|
160
203
|
current_only: z.boolean().optional().describe("Only current leaders"),
|
|
@@ -162,12 +205,12 @@ server.tool("search_leaders", "Search African heads of state and government —
|
|
|
162
205
|
}, READ_ONLY, async (params) => ({
|
|
163
206
|
content: [{ type: "text", text: await apiGet("/v1/government/leaders", params) }],
|
|
164
207
|
}));
|
|
165
|
-
|
|
208
|
+
tool("get_leader", "Get detailed information about a specific leader including biography, terms, and political party", {
|
|
166
209
|
leader_wikidata_id: z.string().describe('Wikidata ID of the leader (e.g. "Q7939")'),
|
|
167
210
|
}, READ_ONLY, async ({ leader_wikidata_id }) => ({
|
|
168
211
|
content: [{ type: "text", text: await apiGet(`/v1/government/leaders/${leader_wikidata_id}`) }],
|
|
169
212
|
}));
|
|
170
|
-
|
|
213
|
+
tool("list_government_terms", "List leadership terms — who governed which country and when", {
|
|
171
214
|
country_code: z.string().optional().describe("Filter by country"),
|
|
172
215
|
leader_wikidata_id: z.string().optional().describe("Filter by leader"),
|
|
173
216
|
role_type: z.enum(["head_of_state", "head_of_government"]).optional().describe("Role type filter"),
|
|
@@ -178,12 +221,12 @@ server.tool("list_government_terms", "List leadership terms — who governed whi
|
|
|
178
221
|
}, READ_ONLY, async (params) => ({
|
|
179
222
|
content: [{ type: "text", text: await apiGet("/v1/government/terms", params) }],
|
|
180
223
|
}));
|
|
181
|
-
|
|
224
|
+
tool("get_cabinet", "Get the current cabinet for a country — ministers, deputy ministers, and key officials", {
|
|
182
225
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
183
226
|
}, READ_ONLY, async ({ country_code }) => ({
|
|
184
227
|
content: [{ type: "text", text: await apiGet(`/v1/government/cabinet/${country_code}`) }],
|
|
185
228
|
}));
|
|
186
|
-
|
|
229
|
+
tool("list_cabinet_members", "Search cabinet members across African governments", {
|
|
187
230
|
country_code: z.string().optional().describe("Filter by country"),
|
|
188
231
|
q: z.string().optional().describe("Search by name"),
|
|
189
232
|
role_category: z
|
|
@@ -198,7 +241,7 @@ server.tool("list_cabinet_members", "Search cabinet members across African gover
|
|
|
198
241
|
// ===================================================================
|
|
199
242
|
// ELECTIONS
|
|
200
243
|
// ===================================================================
|
|
201
|
-
|
|
244
|
+
tool("list_elections", "List elections across Africa — filter by country, scope, status, and year range", {
|
|
202
245
|
country_code: z.string().optional().describe("Filter by country"),
|
|
203
246
|
election_scope: z.enum(["presidential", "parliamentary", "general", "other"]).optional().describe("Election scope"),
|
|
204
247
|
status: z.enum(["upcoming", "completed", "unknown"]).optional().describe("Election status"),
|
|
@@ -208,23 +251,23 @@ server.tool("list_elections", "List elections across Africa — filter by countr
|
|
|
208
251
|
}, READ_ONLY, async (params) => ({
|
|
209
252
|
content: [{ type: "text", text: await apiGet("/v1/elections", params) }],
|
|
210
253
|
}));
|
|
211
|
-
|
|
254
|
+
tool("get_upcoming_elections", "Get upcoming elections across Africa sorted by date", {
|
|
212
255
|
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
213
256
|
}, READ_ONLY, async ({ limit }) => ({
|
|
214
257
|
content: [{ type: "text", text: await apiGet("/v1/elections/upcoming", { limit }) }],
|
|
215
258
|
}));
|
|
216
|
-
|
|
259
|
+
tool("get_country_elections", "Get an election overview for a country — recent and upcoming elections with results", {
|
|
217
260
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
218
261
|
top_limit: z.number().int().min(1).max(20).default(5).describe("Top results per category"),
|
|
219
262
|
}, READ_ONLY, async ({ country_code, top_limit }) => ({
|
|
220
263
|
content: [{ type: "text", text: await apiGet(`/v1/elections/country/${country_code}`, { top_limit }) }],
|
|
221
264
|
}));
|
|
222
|
-
|
|
265
|
+
tool("get_election", "Get detailed information about a specific election", {
|
|
223
266
|
election_wikidata_id: z.string().describe("Wikidata ID of the election"),
|
|
224
267
|
}, READ_ONLY, async ({ election_wikidata_id }) => ({
|
|
225
268
|
content: [{ type: "text", text: await apiGet(`/v1/elections/${election_wikidata_id}`) }],
|
|
226
269
|
}));
|
|
227
|
-
|
|
270
|
+
tool("get_election_results", "Get results for a specific election — candidates, parties, and vote counts", {
|
|
228
271
|
election_wikidata_id: z.string().describe("Wikidata ID of the election"),
|
|
229
272
|
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
230
273
|
}, READ_ONLY, async ({ election_wikidata_id, limit }) => ({
|
|
@@ -233,18 +276,18 @@ server.tool("get_election_results", "Get results for a specific election — can
|
|
|
233
276
|
// ===================================================================
|
|
234
277
|
// MARKETS
|
|
235
278
|
// ===================================================================
|
|
236
|
-
|
|
279
|
+
tool("list_exchanges", "List African stock exchanges — NGX, JSE, BRVM, Casablanca, etc.", {
|
|
237
280
|
country_code: z.string().optional().describe("Filter by country"),
|
|
238
281
|
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
239
282
|
}, READ_ONLY, async (params) => ({
|
|
240
283
|
content: [{ type: "text", text: await apiGet("/v1/markets/exchanges", params) }],
|
|
241
284
|
}));
|
|
242
|
-
|
|
285
|
+
tool("get_exchange", "Get details about a specific stock exchange", {
|
|
243
286
|
exchange_code: z.string().describe('Exchange code (e.g. "NGX", "JSE", "BRVM")'),
|
|
244
287
|
}, READ_ONLY, async ({ exchange_code }) => ({
|
|
245
288
|
content: [{ type: "text", text: await apiGet(`/v1/markets/exchanges/${exchange_code}`) }],
|
|
246
289
|
}));
|
|
247
|
-
|
|
290
|
+
tool("list_tickers", "Search listed securities (equities) across African exchanges", {
|
|
248
291
|
exchange_code: z.string().optional().describe('Filter by exchange (e.g. "NGX")'),
|
|
249
292
|
country_code: z.string().optional().describe("Filter by country"),
|
|
250
293
|
instrument_type: z.string().optional().describe("Filter by instrument type"),
|
|
@@ -253,13 +296,13 @@ server.tool("list_tickers", "Search listed securities (equities) across African
|
|
|
253
296
|
}, READ_ONLY, async (params) => ({
|
|
254
297
|
content: [{ type: "text", text: await apiGet("/v1/markets/tickers", params) }],
|
|
255
298
|
}));
|
|
256
|
-
|
|
299
|
+
tool("get_ticker", "Get details about a specific listed security", {
|
|
257
300
|
exchange_code: z.string().describe('Exchange code (e.g. "NGX")'),
|
|
258
301
|
symbol: z.string().describe('Ticker symbol (e.g. "DANGCEM")'),
|
|
259
302
|
}, READ_ONLY, async ({ exchange_code, symbol }) => ({
|
|
260
303
|
content: [{ type: "text", text: await apiGet(`/v1/markets/tickers/${exchange_code}/${symbol}`) }],
|
|
261
304
|
}));
|
|
262
|
-
|
|
305
|
+
tool("get_ticker_history", "Get price history (OHLC + volume) for a listed security", {
|
|
263
306
|
exchange_code: z.string().describe("Exchange code"),
|
|
264
307
|
symbol: z.string().describe("Ticker symbol"),
|
|
265
308
|
start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
@@ -273,7 +316,20 @@ server.tool("get_ticker_history", "Get price history (OHLC + volume) for a liste
|
|
|
273
316
|
},
|
|
274
317
|
],
|
|
275
318
|
}));
|
|
276
|
-
|
|
319
|
+
tool("get_index_history", "Get the main stock-index level history for an exchange (e.g. NGX All-Share, JSE All Share, BRVM Composite) — index value plus total deals, volume, traded value and market cap", {
|
|
320
|
+
exchange_code: z.string().describe('Exchange code (e.g. "NGX", "JSE", "BRVM")'),
|
|
321
|
+
start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
322
|
+
end_date: z.string().optional().describe("End date (YYYY-MM-DD)"),
|
|
323
|
+
limit: z.number().int().min(1).max(5000).default(365).describe("Max data points"),
|
|
324
|
+
}, READ_ONLY, async ({ exchange_code, start_date, end_date, limit }) => ({
|
|
325
|
+
content: [
|
|
326
|
+
{
|
|
327
|
+
type: "text",
|
|
328
|
+
text: await apiGet(`/v1/markets/exchanges/${exchange_code}/index/history`, { start_date, end_date, limit }),
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
}));
|
|
332
|
+
tool("get_fx_rates", "Get current FX rates for African currencies against a base currency", {
|
|
277
333
|
base_currency: z.string().length(3).default("USD").describe("Base currency (3-letter code)"),
|
|
278
334
|
quote_currencies: z.string().optional().describe('Comma-separated quote currencies (e.g. "NGN,KES,ZAR")'),
|
|
279
335
|
country_code: z.string().optional().describe("Filter by country"),
|
|
@@ -281,7 +337,7 @@ server.tool("get_fx_rates", "Get current FX rates for African currencies against
|
|
|
281
337
|
}, READ_ONLY, async (params) => ({
|
|
282
338
|
content: [{ type: "text", text: await apiGet("/v1/markets/fx-rates", params) }],
|
|
283
339
|
}));
|
|
284
|
-
|
|
340
|
+
tool("get_fx_rate_history", "Get historical FX rate data for a currency pair", {
|
|
285
341
|
base_currency: z.string().length(3).describe('Base currency (e.g. "USD")'),
|
|
286
342
|
quote_currency: z.string().length(3).describe('Quote currency (e.g. "NGN", "KES", "ZAR")'),
|
|
287
343
|
start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
@@ -298,7 +354,7 @@ server.tool("get_fx_rate_history", "Get historical FX rate data for a currency p
|
|
|
298
354
|
// ===================================================================
|
|
299
355
|
// TRADE
|
|
300
356
|
// ===================================================================
|
|
301
|
-
|
|
357
|
+
tool("get_trade_overview", "Get a trade overview — total exports/imports, top partners, top products, and trends", {
|
|
302
358
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
303
359
|
start_year: z.number().int().optional().describe("Start year"),
|
|
304
360
|
end_year: z.number().int().optional().describe("End year"),
|
|
@@ -311,7 +367,7 @@ server.tool("get_trade_overview", "Get a trade overview — total exports/import
|
|
|
311
367
|
},
|
|
312
368
|
],
|
|
313
369
|
}));
|
|
314
|
-
|
|
370
|
+
tool("get_trade_flows", "Query bilateral trade flows between countries — exports and imports with values", {
|
|
315
371
|
reporter_country_code: z.string().optional().describe("Reporting country (ISO alpha-2)"),
|
|
316
372
|
partner_country_code: z.string().optional().describe("Partner country code"),
|
|
317
373
|
product_code: z.string().optional().describe("HS product code"),
|
|
@@ -323,7 +379,7 @@ server.tool("get_trade_flows", "Query bilateral trade flows between countries
|
|
|
323
379
|
}, READ_ONLY, async (params) => ({
|
|
324
380
|
content: [{ type: "text", text: await apiGet("/v1/trade/flows", params) }],
|
|
325
381
|
}));
|
|
326
|
-
|
|
382
|
+
tool("get_trade_partners", "Get top trading partners for a country", {
|
|
327
383
|
reporter_country_code: z.string().length(2).describe("Reporting country (required)"),
|
|
328
384
|
flow_type: z.enum(["export", "import"]).optional().describe("Flow type"),
|
|
329
385
|
year: z.number().int().optional().describe("Exact year"),
|
|
@@ -333,7 +389,7 @@ server.tool("get_trade_partners", "Get top trading partners for a country", {
|
|
|
333
389
|
}, READ_ONLY, async (params) => ({
|
|
334
390
|
content: [{ type: "text", text: await apiGet("/v1/trade/partners", params) }],
|
|
335
391
|
}));
|
|
336
|
-
|
|
392
|
+
tool("get_trade_products", "Get top traded products for a country — what it exports and imports", {
|
|
337
393
|
reporter_country_code: z.string().length(2).describe("Reporting country (required)"),
|
|
338
394
|
flow_type: z.enum(["export", "import"]).optional().describe("Flow type"),
|
|
339
395
|
year: z.number().int().optional().describe("Exact year"),
|
|
@@ -346,7 +402,7 @@ server.tool("get_trade_products", "Get top traded products for a country — wha
|
|
|
346
402
|
// ===================================================================
|
|
347
403
|
// POLICIES
|
|
348
404
|
// ===================================================================
|
|
349
|
-
|
|
405
|
+
tool("list_policies", "Search government policies, laws, and regulations across Africa", {
|
|
350
406
|
country_code: z.string().optional().describe("Filter by country"),
|
|
351
407
|
document_type: z
|
|
352
408
|
.enum(["constitution", "law", "policy", "strategy", "regulation", "bill", "decree", "other"])
|
|
@@ -360,18 +416,18 @@ server.tool("list_policies", "Search government policies, laws, and regulations
|
|
|
360
416
|
}, READ_ONLY, async (params) => ({
|
|
361
417
|
content: [{ type: "text", text: await apiGet("/v1/policies", params) }],
|
|
362
418
|
}));
|
|
363
|
-
|
|
419
|
+
tool("get_policy", "Get detailed information about a specific policy, law, or regulation", {
|
|
364
420
|
policy_wikidata_id: z.string().describe("Wikidata ID of the policy"),
|
|
365
421
|
}, READ_ONLY, async ({ policy_wikidata_id }) => ({
|
|
366
422
|
content: [{ type: "text", text: await apiGet(`/v1/policies/${policy_wikidata_id}`) }],
|
|
367
423
|
}));
|
|
368
|
-
|
|
424
|
+
tool("get_country_policies", "Get a policy overview for a country — recent and notable policies grouped by type", {
|
|
369
425
|
country_code: z.string().length(2).describe('ISO alpha-2 code (e.g. "NG", "KE", "ZA")'),
|
|
370
426
|
top_limit: z.number().int().min(1).max(20).default(5).describe("Top items per category"),
|
|
371
427
|
}, READ_ONLY, async ({ country_code, top_limit }) => ({
|
|
372
428
|
content: [{ type: "text", text: await apiGet(`/v1/policies/country/${country_code}`, { top_limit }) }],
|
|
373
429
|
}));
|
|
374
|
-
|
|
430
|
+
tool("get_country_policy_timeline", "Get a chronological policy timeline for a country", {
|
|
375
431
|
country_code: z.string().length(2).describe("ISO alpha-2 code"),
|
|
376
432
|
top_limit: z.number().int().min(1).max(100).default(20).describe("Number of events"),
|
|
377
433
|
}, READ_ONLY, async ({ country_code, top_limit }) => ({
|
|
@@ -379,7 +435,7 @@ server.tool("get_country_policy_timeline", "Get a chronological policy timeline
|
|
|
379
435
|
{ type: "text", text: await apiGet(`/v1/policies/country/${country_code}/timeline`, { top_limit }) },
|
|
380
436
|
],
|
|
381
437
|
}));
|
|
382
|
-
|
|
438
|
+
tool("list_policy_events", "List policy lifecycle events — when policies were announced, adopted, amended, repealed", {
|
|
383
439
|
country_code: z.string().optional().describe("Filter by country"),
|
|
384
440
|
event_type: z
|
|
385
441
|
.enum(["announced", "adopted", "implemented", "amended", "repealed", "suspended"])
|
|
@@ -391,7 +447,7 @@ server.tool("list_policy_events", "List policy lifecycle events — when policie
|
|
|
391
447
|
}, READ_ONLY, async (params) => ({
|
|
392
448
|
content: [{ type: "text", text: await apiGet("/v1/policies/events", params) }],
|
|
393
449
|
}));
|
|
394
|
-
|
|
450
|
+
tool("get_policy_events", "Get all lifecycle events for a specific policy", {
|
|
395
451
|
policy_wikidata_id: z.string().describe("Wikidata ID of the policy"),
|
|
396
452
|
limit: z.number().int().min(1).max(500).default(100).describe("Max results"),
|
|
397
453
|
}, READ_ONLY, async ({ policy_wikidata_id, limit }) => ({
|
|
@@ -400,10 +456,10 @@ server.tool("get_policy_events", "Get all lifecycle events for a specific policy
|
|
|
400
456
|
// ===================================================================
|
|
401
457
|
// SOURCES
|
|
402
458
|
// ===================================================================
|
|
403
|
-
|
|
459
|
+
tool("list_sources", "List all data sources — World Bank, UN agencies, central banks, exchanges, etc.", {}, READ_ONLY, async () => ({
|
|
404
460
|
content: [{ type: "text", text: await apiGet("/v1/sources") }],
|
|
405
461
|
}));
|
|
406
|
-
|
|
462
|
+
tool("get_source", "Get details about a specific data source — description, URL, coverage, and update frequency", {
|
|
407
463
|
source_code: z.string().describe("Source code identifier"),
|
|
408
464
|
}, READ_ONLY, async ({ source_code }) => ({
|
|
409
465
|
content: [{ type: "text", text: await apiGet(`/v1/sources/${source_code}`) }],
|
|
@@ -411,7 +467,7 @@ server.tool("get_source", "Get details about a specific data source — descript
|
|
|
411
467
|
// ===================================================================
|
|
412
468
|
// PLATFORM
|
|
413
469
|
// ===================================================================
|
|
414
|
-
|
|
470
|
+
tool("get_platform_info", "Get Africa API platform version and status", {}, READ_ONLY, async () => ({
|
|
415
471
|
content: [{ type: "text", text: await apiGet("/v1/platform/version") }],
|
|
416
472
|
}));
|
|
417
473
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "africa-api-mcp",
|
|
3
3
|
"mcpName": "io.github.africa-api/africa-api-mcp",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "MCP server for Africa API — access data on all 54 African countries through Claude",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"node": ">=18"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"typescript": "^5.7.0",
|