@pipeworx/mcp-data360 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pipeworx
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,55 @@
1
+ # mcp-data360
2
+
3
+ World Bank Data360 MCP — the World Bank's modern unified data platform.
4
+
5
+ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 250+ live data sources.
6
+
7
+ ## Tools
8
+
9
+ | Tool | Description |
10
+ |------|-------------|
11
+
12
+ ## Quick Start
13
+
14
+ Add to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "data360": {
20
+ "url": "https://gateway.pipeworx.io/data360/mcp"
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ Or connect to the full Pipeworx gateway for access to all 250+ data sources:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "pipeworx": {
32
+ "url": "https://gateway.pipeworx.io/mcp"
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Using with ask_pipeworx
39
+
40
+ Instead of calling tools directly, you can ask questions in plain English:
41
+
42
+ ```
43
+ ask_pipeworx({ question: "your question about Data360 data" })
44
+ ```
45
+
46
+ The gateway picks the right tool and fills the arguments automatically.
47
+
48
+ ## More
49
+
50
+ - [All tools and guides](https://github.com/pipeworx-io/examples)
51
+ - [pipeworx.io](https://pipeworx.io)
52
+
53
+ ## License
54
+
55
+ MIT
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@pipeworx/mcp-data360",
3
+ "version": "0.1.0",
4
+ "description": "World Bank Data360 MCP — the World Bank's modern unified data platform.",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "data360"],
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/pipeworx-io/mcp-data360"
13
+ },
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.0"
19
+ }
20
+ }
package/server.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.pipeworx-io/data360",
4
+ "title": "Data360",
5
+ "description": "World Bank Data360 MCP — the World Bank's modern unified data platform.",
6
+ "version": "0.1.0",
7
+ "websiteUrl": "https://pipeworx.io/packs/data360",
8
+ "repository": {
9
+ "url": "https://github.com/pipeworx-io/mcp-data360",
10
+ "source": "github"
11
+ },
12
+ "remotes": [
13
+ {
14
+ "type": "streamable-http",
15
+ "url": "https://gateway.pipeworx.io/data360/mcp"
16
+ }
17
+ ]
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,176 @@
1
+ interface McpToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: 'object';
6
+ properties: Record<string, unknown>;
7
+ required?: string[];
8
+ };
9
+ }
10
+
11
+ interface McpToolExport {
12
+ tools: McpToolDefinition[];
13
+ callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
14
+ meter?: { credits: number };
15
+ cost?: Record<string, unknown>;
16
+ provider?: string;
17
+ }
18
+
19
+ /**
20
+ * World Bank Data360 MCP — the World Bank's modern unified data platform.
21
+ *
22
+ * Data360 (data360api.worldbank.org) aggregates 90+ international databases —
23
+ * World Development Indicators (WB_WDI), IMF Balance of Payments (IMF_BOP),
24
+ * WHO, ILO, UNESCO Education Stats, Gender Stats, Global Findex, and more —
25
+ * behind ONE SDMX-style API. A series is addressed by (DATABASE_ID, INDICATOR,
26
+ * REF_AREA): DATABASE_ID picks the source database, INDICATOR is the code from
27
+ * search_indicators, REF_AREA is an ISO3 country code. This is the newer/broader
28
+ * successor to the legacy World Bank v2 indicators API.
29
+ */
30
+
31
+
32
+ const BASE = 'https://data360api.worldbank.org/data360';
33
+ const UA = 'pipeworx-mcp-data360/1.0 (+https://pipeworx.io)';
34
+
35
+ const tools: McpToolExport['tools'] = [
36
+ {
37
+ name: 'data360_list_databases',
38
+ description:
39
+ 'List the source databases aggregated by World Bank Data360 (e.g. WB_WDI = World Development Indicators, IMF_BOP = Balance of Payments, WB_EDSTATS = Education Statistics). Returns each DATABASE_ID with its full name and indicator count. Use a DATABASE_ID here to scope data360_search_indicators and data360_get_data.',
40
+ inputSchema: {
41
+ type: 'object',
42
+ properties: {
43
+ top: { type: 'number', description: 'Max databases to return (default 200, the full list).' },
44
+ },
45
+ },
46
+ },
47
+ {
48
+ name: 'data360_search_indicators',
49
+ description:
50
+ 'Full-text search across all Data360 indicators (every WDI/IMF/WHO/ILO/education series in one index). Returns indicator codes (use as INDICATOR in data360_get_data), names, owning DATABASE_ID/name, units, and definitions. Optionally restrict to one database with database_id.',
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {
54
+ query: { type: 'string', description: 'Keyword(s), e.g. "GDP per capita" or "maternal mortality". Use "*" for all.' },
55
+ database_id: { type: 'string', description: 'Optional: restrict to one source database, e.g. "WB_WDI" or "IMF_BOP" (from data360_list_databases).' },
56
+ top: { type: 'number', description: 'Max results (default 20).' },
57
+ skip: { type: 'number', description: 'Offset for pagination (default 0).' },
58
+ },
59
+ required: ['query'],
60
+ },
61
+ },
62
+ {
63
+ name: 'data360_get_data',
64
+ description:
65
+ 'Fetch observations for one Data360 series. DATABASE_ID selects the source database (e.g. WB_WDI), INDICATOR is the code from data360_search_indicators (e.g. WB_WDI_SP_POP_TOTL), REF_AREA is an ISO3 country code (e.g. BRA, USA). Returns SDMX-style records with OBS_VALUE, TIME_PERIOD, UNIT_MEASURE and disaggregation attributes (SEX, AGE, etc.). Omit TIME_PERIOD for the full series.',
66
+ inputSchema: {
67
+ type: 'object',
68
+ properties: {
69
+ DATABASE_ID: { type: 'string', description: 'Source database ID, e.g. "WB_WDI".' },
70
+ INDICATOR: { type: 'string', description: 'Indicator code, e.g. "WB_WDI_SP_POP_TOTL".' },
71
+ REF_AREA: { type: 'string', description: 'ISO3 country/region code, e.g. "BRA", "USA", "WLD".' },
72
+ TIME_PERIOD: { type: 'string', description: 'Optional single year, e.g. "2020".' },
73
+ skip: { type: 'number', description: 'Offset for pagination (default 0).' },
74
+ top: { type: 'number', description: 'Max observations to return (default 100).' },
75
+ },
76
+ required: ['DATABASE_ID', 'INDICATOR', 'REF_AREA'],
77
+ },
78
+ },
79
+ ];
80
+
81
+ async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
82
+ switch (name) {
83
+ case 'data360_list_databases': {
84
+ const top = numArg(args.top, 200);
85
+ const body = {
86
+ count: true,
87
+ search: '*',
88
+ top: 0,
89
+ facets: [`series_description/database_id,count:${top}`, `series_description/database_name,count:${top}`],
90
+ };
91
+ const res = await searchPost(body);
92
+ const facets = (res as any)?.['@search.facets'] ?? {};
93
+ const ids: Array<{ value: string; count: number }> = facets['series_description/database_id'] ?? [];
94
+ const names: Array<{ value: string; count: number }> = facets['series_description/database_name'] ?? [];
95
+ const nameById = new Map<number, string>();
96
+ names.forEach((n, i) => nameById.set(i, n.value));
97
+ const databases = ids.map((d, i) => ({
98
+ database_id: d.value,
99
+ database_name: names[i]?.value ?? null,
100
+ indicator_count: d.count,
101
+ }));
102
+ return { count: databases.length, databases };
103
+ }
104
+
105
+ case 'data360_search_indicators': {
106
+ const query = reqStr(args, 'query', '"GDP per capita"');
107
+ const databaseId = optStr(args.database_id);
108
+ const top = numArg(args.top, 20);
109
+ const skip = numArg(args.skip, 0);
110
+ const body: Record<string, unknown> = { count: true, search: query, top, skip };
111
+ if (databaseId) body.filter = `series_description/database_id eq '${databaseId.replace(/'/g, "''")}'`;
112
+ const res = (await searchPost(body)) as any;
113
+ const indicators = (res?.value ?? []).map((v: any) => {
114
+ const sd = v.series_description ?? {};
115
+ return {
116
+ indicator: sd.idno ?? null,
117
+ name: sd.name ?? null,
118
+ database_id: sd.database_id ?? null,
119
+ database_name: sd.database_name ?? null,
120
+ measurement_unit: sd.measurement_unit ?? null,
121
+ periodicity: sd.periodicity ?? null,
122
+ definition: sd.definition_long ?? sd.definition_short ?? null,
123
+ };
124
+ });
125
+ return { count: res?.['@odata.count'] ?? indicators.length, indicators };
126
+ }
127
+
128
+ case 'data360_get_data': {
129
+ const params = new URLSearchParams();
130
+ params.set('DATABASE_ID', reqStr(args, 'DATABASE_ID', '"WB_WDI"'));
131
+ params.set('INDICATOR', reqStr(args, 'INDICATOR', '"WB_WDI_SP_POP_TOTL"'));
132
+ params.set('REF_AREA', reqStr(args, 'REF_AREA', '"BRA"'));
133
+ const period = optStr(args.TIME_PERIOD);
134
+ if (period) params.set('TIME_PERIOD', period);
135
+ params.set('skip', String(numArg(args.skip, 0)));
136
+ params.set('top', String(numArg(args.top, 100)));
137
+ return get(`/data?${params.toString()}`);
138
+ }
139
+
140
+ default:
141
+ throw new Error(`Unknown tool: ${name}`);
142
+ }
143
+ }
144
+
145
+ async function searchPost(body: unknown): Promise<unknown> {
146
+ const res = await fetch(`${BASE}/searchv2`, {
147
+ method: 'POST',
148
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'User-Agent': UA },
149
+ body: JSON.stringify(body),
150
+ });
151
+ if (!res.ok) throw new Error(`Data360: ${res.status} ${await res.text().then((t) => t.slice(0, 200))}`);
152
+ return res.json();
153
+ }
154
+
155
+ async function get(path: string): Promise<unknown> {
156
+ const res = await fetch(`${BASE}${path}`, { headers: { Accept: 'application/json', 'User-Agent': UA } });
157
+ if (!res.ok) throw new Error(`Data360: ${res.status} ${await res.text().then((t) => t.slice(0, 200))}`);
158
+ return res.json();
159
+ }
160
+
161
+ function reqStr(args: Record<string, unknown>, key: string, example: string): string {
162
+ const v = args[key];
163
+ if (typeof v !== 'string' || !v.trim()) throw new Error(`Required argument "${key}" is missing. Pass a string like ${example}.`);
164
+ return v.trim();
165
+ }
166
+
167
+ function optStr(v: unknown): string | undefined {
168
+ return typeof v === 'string' && v.trim() ? v.trim() : undefined;
169
+ }
170
+
171
+ function numArg(v: unknown, fallback: number): number {
172
+ const n = typeof v === 'number' ? v : typeof v === 'string' ? Number(v) : NaN;
173
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
174
+ }
175
+
176
+ export default { tools, callTool, meter: { credits: 1 } } satisfies McpToolExport;
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }