data-aggregator-mcp 1.0.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/README.md +336 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +333 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/exchange.d.ts +15 -0
- package/dist/tools/exchange.d.ts.map +1 -0
- package/dist/tools/exchange.js +195 -0
- package/dist/tools/exchange.js.map +1 -0
- package/dist/tools/news.d.ts +20 -0
- package/dist/tools/news.d.ts.map +1 -0
- package/dist/tools/news.js +175 -0
- package/dist/tools/news.js.map +1 -0
- package/dist/tools/public-data.d.ts +24 -0
- package/dist/tools/public-data.d.ts.map +1 -0
- package/dist/tools/public-data.js +262 -0
- package/dist/tools/public-data.js.map +1 -0
- package/dist/tools/scraper.d.ts +19 -0
- package/dist/tools/scraper.d.ts.map +1 -0
- package/dist/tools/scraper.js +185 -0
- package/dist/tools/scraper.js.map +1 -0
- package/dist/tools/stocks.d.ts +14 -0
- package/dist/tools/stocks.d.ts.map +1 -0
- package/dist/tools/stocks.js +172 -0
- package/dist/tools/stocks.js.map +1 -0
- package/dist/tools/weather.d.ts +15 -0
- package/dist/tools/weather.d.ts.map +1 -0
- package/dist/tools/weather.js +172 -0
- package/dist/tools/weather.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cache.d.ts +45 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +88 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/http.d.ts +39 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +103 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +34 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +95 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/package.json +42 -0
- package/src/index.ts +461 -0
- package/src/tools/exchange.ts +241 -0
- package/src/tools/news.ts +238 -0
- package/src/tools/public-data.ts +325 -0
- package/src/tools/scraper.ts +217 -0
- package/src/tools/stocks.ts +205 -0
- package/src/tools/weather.ts +216 -0
- package/src/types.ts +184 -0
- package/src/utils/cache.ts +103 -0
- package/src/utils/http.ts +156 -0
- package/src/utils/rate-limiter.ts +114 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# Data Aggregator MCP Server
|
|
2
|
+
|
|
3
|
+
A unified MCP (Model Context Protocol) server that replaces 5-10 separate data servers with one installation. Instead of juggling separate servers for financial data, weather, news, web scraping, and more, install **one server** that handles all your data needs.
|
|
4
|
+
|
|
5
|
+
**Solves "MCP server sprawl."**
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
### Install and run with npx
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx data-aggregator-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Or clone and build
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git clone <repo-url>
|
|
19
|
+
cd data-aggregator
|
|
20
|
+
npm install
|
|
21
|
+
npm run build
|
|
22
|
+
npm start
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Claude Desktop Configuration
|
|
26
|
+
|
|
27
|
+
Add to your `claude_desktop_config.json`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"data-aggregator": {
|
|
33
|
+
"command": "node",
|
|
34
|
+
"args": ["/path/to/data-aggregator/dist/index.js"],
|
|
35
|
+
"env": {
|
|
36
|
+
"NEWS_API_KEY": "your-optional-newsapi-key",
|
|
37
|
+
"ALPHA_VANTAGE_KEY": "your-optional-alpha-vantage-key"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### HTTP/SSE Transport (Remote Deployment)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Start with HTTP transport on port 3000 (default)
|
|
48
|
+
npm run start:sse
|
|
49
|
+
|
|
50
|
+
# Or specify a custom port
|
|
51
|
+
node dist/index.js --sse --port=8080
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Tools
|
|
55
|
+
|
|
56
|
+
### 1. `query_stocks` - Market Data
|
|
57
|
+
|
|
58
|
+
Fetch real-time stock and cryptocurrency prices.
|
|
59
|
+
|
|
60
|
+
**Sources:** Yahoo Finance (stocks), CoinGecko (crypto) -- both free, no API key required. Optional Alpha Vantage support with `ALPHA_VANTAGE_KEY` env variable.
|
|
61
|
+
|
|
62
|
+
| Parameter | Type | Required | Description |
|
|
63
|
+
|-----------|------|----------|-------------|
|
|
64
|
+
| `symbol` | string | Yes | Ticker symbol (e.g., `AAPL`, `BTC`, `ETH`) |
|
|
65
|
+
| `type` | `"stock"` \| `"crypto"` \| `"auto"` | No | Asset type detection (default: `auto`) |
|
|
66
|
+
|
|
67
|
+
**Example requests:**
|
|
68
|
+
```
|
|
69
|
+
"Get me the current price of AAPL"
|
|
70
|
+
"What's Bitcoin trading at?"
|
|
71
|
+
"Fetch SOL crypto price"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Example response:**
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"symbol": "AAPL",
|
|
78
|
+
"price": 198.52,
|
|
79
|
+
"currency": "USD",
|
|
80
|
+
"change": 2.34,
|
|
81
|
+
"changePercent": 1.19,
|
|
82
|
+
"volume": 54321000,
|
|
83
|
+
"high": 199.10,
|
|
84
|
+
"low": 196.80,
|
|
85
|
+
"source": "Yahoo Finance"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### 2. `fetch_webpage` - Web Scraping
|
|
92
|
+
|
|
93
|
+
Extract structured data from any web page.
|
|
94
|
+
|
|
95
|
+
| Parameter | Type | Required | Description |
|
|
96
|
+
|-----------|------|----------|-------------|
|
|
97
|
+
| `url` | string (URL) | Yes | Page to scrape |
|
|
98
|
+
| `selector` | string | No | CSS selector (`#id`, `.class`, or tag name) |
|
|
99
|
+
|
|
100
|
+
**Example requests:**
|
|
101
|
+
```
|
|
102
|
+
"Scrape the main content from https://example.com"
|
|
103
|
+
"Get the article text from this URL using selector .post-content"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Returns:** Title, meta description, headings, main content (text only), and links.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### 3. `search_news` - News Aggregation
|
|
111
|
+
|
|
112
|
+
Search and aggregate news from multiple sources.
|
|
113
|
+
|
|
114
|
+
**Sources:** Google News RSS (free, no key) or NewsAPI.org (optional key via `NEWS_API_KEY` env).
|
|
115
|
+
|
|
116
|
+
| Parameter | Type | Required | Description |
|
|
117
|
+
|-----------|------|----------|-------------|
|
|
118
|
+
| `query` | string | No | Search keyword |
|
|
119
|
+
| `category` | string | No | `business`, `technology`, `science`, etc. (NewsAPI only) |
|
|
120
|
+
| `source` | string | No | Source ID (e.g., `bbc-news`) |
|
|
121
|
+
| `language` | string | No | Language code (default: `en`) |
|
|
122
|
+
| `from` | string | No | Start date `YYYY-MM-DD` (NewsAPI only) |
|
|
123
|
+
| `to` | string | No | End date `YYYY-MM-DD` (NewsAPI only) |
|
|
124
|
+
| `maxResults` | number | No | 1-100 (default: 10) |
|
|
125
|
+
|
|
126
|
+
**Example requests:**
|
|
127
|
+
```
|
|
128
|
+
"Search news about artificial intelligence"
|
|
129
|
+
"Get top technology news"
|
|
130
|
+
"Find news about climate change from the last week"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### 4. `query_weather` - Weather Data
|
|
136
|
+
|
|
137
|
+
Current conditions and 7-day forecasts.
|
|
138
|
+
|
|
139
|
+
**Source:** Open-Meteo API (100% free, no API key required).
|
|
140
|
+
|
|
141
|
+
| Parameter | Type | Required | Description |
|
|
142
|
+
|-----------|------|----------|-------------|
|
|
143
|
+
| `city` | string | No* | City name (e.g., `"London"`) |
|
|
144
|
+
| `latitude` | number | No* | Latitude (-90 to 90) |
|
|
145
|
+
| `longitude` | number | No* | Longitude (-180 to 180) |
|
|
146
|
+
| `units` | `"celsius"` \| `"fahrenheit"` | No | Temperature units (default: celsius) |
|
|
147
|
+
|
|
148
|
+
*Provide either `city` or both `latitude`/`longitude`.
|
|
149
|
+
|
|
150
|
+
**Example requests:**
|
|
151
|
+
```
|
|
152
|
+
"What's the weather in Tokyo?"
|
|
153
|
+
"Get the 7-day forecast for New York in Fahrenheit"
|
|
154
|
+
"Weather at coordinates 51.5, -0.1"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Example response:**
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"location": "London, England, United Kingdom",
|
|
161
|
+
"current": {
|
|
162
|
+
"temperature": 15.2,
|
|
163
|
+
"feelsLike": 13.8,
|
|
164
|
+
"humidity": 72,
|
|
165
|
+
"windSpeed": 12.5,
|
|
166
|
+
"weatherDescription": "Partly cloudy"
|
|
167
|
+
},
|
|
168
|
+
"forecast": [
|
|
169
|
+
{
|
|
170
|
+
"date": "2026-03-31",
|
|
171
|
+
"temperatureMax": 17.1,
|
|
172
|
+
"temperatureMin": 8.3,
|
|
173
|
+
"precipitationProbability": 20,
|
|
174
|
+
"weatherDescription": "Mainly clear"
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### 5. `query_exchange_rates` - Currency Exchange
|
|
183
|
+
|
|
184
|
+
Real-time and historical exchange rates with conversion.
|
|
185
|
+
|
|
186
|
+
**Sources:** Frankfurter/ECB (fiat, free), CoinGecko (crypto, free).
|
|
187
|
+
|
|
188
|
+
| Parameter | Type | Required | Description |
|
|
189
|
+
|-----------|------|----------|-------------|
|
|
190
|
+
| `base` | string | Yes | Base currency (`USD`, `EUR`, `BTC`, etc.) |
|
|
191
|
+
| `target` | string | Yes | Target currency |
|
|
192
|
+
| `amount` | number | No | Amount to convert (default: 1) |
|
|
193
|
+
| `date` | string | No | Historical date `YYYY-MM-DD` (fiat only) |
|
|
194
|
+
|
|
195
|
+
**Supported flows:** Fiat-to-fiat, fiat-to-crypto, crypto-to-fiat, crypto-to-crypto.
|
|
196
|
+
|
|
197
|
+
**Example requests:**
|
|
198
|
+
```
|
|
199
|
+
"Convert 100 USD to EUR"
|
|
200
|
+
"What's the BTC to USD rate?"
|
|
201
|
+
"Historical EUR/GBP rate on 2025-01-15"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### 6. `query_public_data` - Multi-Purpose API
|
|
207
|
+
|
|
208
|
+
Access various public data APIs through sub-commands.
|
|
209
|
+
|
|
210
|
+
| Parameter | Type | Required | Description |
|
|
211
|
+
|-----------|------|----------|-------------|
|
|
212
|
+
| `command` | string | Yes | Sub-command (see below) |
|
|
213
|
+
|
|
214
|
+
**Sub-commands:**
|
|
215
|
+
|
|
216
|
+
#### `wikipedia` - Article Summaries
|
|
217
|
+
| Parameter | Description |
|
|
218
|
+
|-----------|-------------|
|
|
219
|
+
| `query` | Search term (e.g., `"Theory of relativity"`) |
|
|
220
|
+
| `language` | Wiki language code (default: `en`) |
|
|
221
|
+
|
|
222
|
+
#### `ip_geolocation` - IP Location Lookup
|
|
223
|
+
| Parameter | Description |
|
|
224
|
+
|-----------|-------------|
|
|
225
|
+
| `ip` | IP address (omit for your own IP) |
|
|
226
|
+
|
|
227
|
+
#### `dns_lookup` - DNS Records
|
|
228
|
+
| Parameter | Description |
|
|
229
|
+
|-----------|-------------|
|
|
230
|
+
| `domain` | Domain name (e.g., `"example.com"`) |
|
|
231
|
+
| `recordType` | `A`, `AAAA`, `MX`, `TXT`, `NS`, `CNAME`, `SOA` (default: `A`) |
|
|
232
|
+
|
|
233
|
+
#### `expand_url` - URL Expander
|
|
234
|
+
| Parameter | Description |
|
|
235
|
+
|-----------|-------------|
|
|
236
|
+
| `url` | Shortened URL to follow to its destination |
|
|
237
|
+
|
|
238
|
+
**Example requests:**
|
|
239
|
+
```
|
|
240
|
+
"Look up the Wikipedia article on quantum computing"
|
|
241
|
+
"What's the geolocation of IP 8.8.8.8?"
|
|
242
|
+
"Get MX records for gmail.com"
|
|
243
|
+
"Expand this shortened URL: https://bit.ly/xyz"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Environment Variables
|
|
247
|
+
|
|
248
|
+
| Variable | Required | Description |
|
|
249
|
+
|----------|----------|-------------|
|
|
250
|
+
| `NEWS_API_KEY` | No | NewsAPI.org key for enhanced news search. Falls back to Google News RSS without it. |
|
|
251
|
+
| `ALPHA_VANTAGE_KEY` | No | Alpha Vantage key for stock data fallback. Yahoo Finance is used by default. |
|
|
252
|
+
|
|
253
|
+
## Architecture
|
|
254
|
+
|
|
255
|
+
### Caching
|
|
256
|
+
|
|
257
|
+
Built-in in-memory cache with per-category TTLs:
|
|
258
|
+
|
|
259
|
+
| Data Type | Cache Duration |
|
|
260
|
+
|-----------|---------------|
|
|
261
|
+
| Market data (stocks/crypto) | 5 minutes |
|
|
262
|
+
| Exchange rates | 30 minutes |
|
|
263
|
+
| News articles | 15 minutes |
|
|
264
|
+
| Weather | 1 hour |
|
|
265
|
+
| Web scraping | 10 minutes |
|
|
266
|
+
| Public data (Wikipedia, DNS, etc.) | 1 hour |
|
|
267
|
+
|
|
268
|
+
### Rate Limiting
|
|
269
|
+
|
|
270
|
+
Per-source sliding-window rate limiters prevent API abuse:
|
|
271
|
+
|
|
272
|
+
| Source | Limit |
|
|
273
|
+
|--------|-------|
|
|
274
|
+
| CoinGecko | 30 req/min |
|
|
275
|
+
| Open-Meteo | 60 req/min |
|
|
276
|
+
| Frankfurter (exchange) | 60 req/min |
|
|
277
|
+
| NewsAPI | 50 req/min |
|
|
278
|
+
| Google News RSS | 60 req/min |
|
|
279
|
+
| Wikipedia | 100 req/min |
|
|
280
|
+
| Generic HTTP | 120 req/min |
|
|
281
|
+
|
|
282
|
+
### Error Handling
|
|
283
|
+
|
|
284
|
+
- Automatic retries with exponential back-off (up to 3 attempts)
|
|
285
|
+
- Graceful degradation when APIs are unavailable
|
|
286
|
+
- User-friendly error messages
|
|
287
|
+
- Fallback sources (Yahoo Finance -> Alpha Vantage, NewsAPI -> Google News RSS)
|
|
288
|
+
|
|
289
|
+
## Pricing
|
|
290
|
+
|
|
291
|
+
### Free Tier
|
|
292
|
+
- **100 queries/day** across all tools
|
|
293
|
+
- No credit card required
|
|
294
|
+
- All 6 tools included
|
|
295
|
+
|
|
296
|
+
### Pay-Per-Query
|
|
297
|
+
- **$0.01 - $0.05 per query** depending on data source
|
|
298
|
+
- No monthly commitment
|
|
299
|
+
- Volume discounts available
|
|
300
|
+
|
|
301
|
+
### Pro Plan - $16/month
|
|
302
|
+
- **Unlimited queries**
|
|
303
|
+
- Priority rate limits
|
|
304
|
+
- Email support
|
|
305
|
+
- Via [MCPize](https://mcpize.com)
|
|
306
|
+
|
|
307
|
+
### Premium Plan - $32/month
|
|
308
|
+
- Everything in Pro
|
|
309
|
+
- Dedicated rate limit pools
|
|
310
|
+
- Custom API key management
|
|
311
|
+
- Priority support
|
|
312
|
+
- Historical data access
|
|
313
|
+
- Via [MCPize](https://mcpize.com)
|
|
314
|
+
|
|
315
|
+
## Development
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Install dependencies
|
|
319
|
+
npm install
|
|
320
|
+
|
|
321
|
+
# Build TypeScript
|
|
322
|
+
npm run build
|
|
323
|
+
|
|
324
|
+
# Watch mode for development
|
|
325
|
+
npm run dev
|
|
326
|
+
|
|
327
|
+
# Run the server (stdio)
|
|
328
|
+
npm start
|
|
329
|
+
|
|
330
|
+
# Run the server (HTTP/SSE)
|
|
331
|
+
npm run start:sse
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Data Aggregator MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A unified data server that replaces 5-10 separate MCP servers.
|
|
6
|
+
* Provides stocks, weather, news, web scraping, exchange rates,
|
|
7
|
+
* and public data queries through a single installation.
|
|
8
|
+
*
|
|
9
|
+
* Transports:
|
|
10
|
+
* - stdio (default): for Claude Desktop, CLI tools
|
|
11
|
+
* - HTTP/SSE (--sse or --http flag): for remote deployments
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Data Aggregator MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A unified data server that replaces 5-10 separate MCP servers.
|
|
6
|
+
* Provides stocks, weather, news, web scraping, exchange rates,
|
|
7
|
+
* and public data queries through a single installation.
|
|
8
|
+
*
|
|
9
|
+
* Transports:
|
|
10
|
+
* - stdio (default): for Claude Desktop, CLI tools
|
|
11
|
+
* - HTTP/SSE (--sse or --http flag): for remote deployments
|
|
12
|
+
*/
|
|
13
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { queryStocks } from "./tools/stocks.js";
|
|
17
|
+
import { fetchWebpage } from "./tools/scraper.js";
|
|
18
|
+
import { searchNews } from "./tools/news.js";
|
|
19
|
+
import { queryWeather } from "./tools/weather.js";
|
|
20
|
+
import { queryExchangeRates } from "./tools/exchange.js";
|
|
21
|
+
import { queryPublicData } from "./tools/public-data.js";
|
|
22
|
+
import { cache } from "./utils/cache.js";
|
|
23
|
+
// ─── Server Setup ──────────────────────────────────────────────────────────
|
|
24
|
+
const server = new McpServer({
|
|
25
|
+
name: "data-aggregator",
|
|
26
|
+
version: "1.0.0",
|
|
27
|
+
}, {
|
|
28
|
+
capabilities: {
|
|
29
|
+
logging: {},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
// ─── Tool Registration ────────────────────────────────────────────────────
|
|
33
|
+
// 1. query_stocks
|
|
34
|
+
server.tool("query_stocks", "Fetch real-time stock and cryptocurrency market data. Supports major stocks (AAPL, MSFT, GOOGL) and crypto (BTC, ETH, SOL). Returns price, daily change, volume, and market cap. Data from Yahoo Finance (stocks) and CoinGecko (crypto).", {
|
|
35
|
+
symbol: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("Ticker symbol (e.g., AAPL, MSFT, GOOGL for stocks; BTC, ETH, SOL for crypto)"),
|
|
38
|
+
type: z
|
|
39
|
+
.enum(["stock", "crypto", "auto"])
|
|
40
|
+
.optional()
|
|
41
|
+
.default("auto")
|
|
42
|
+
.describe('Asset type. "auto" detects based on symbol. Use "stock" or "crypto" to force.'),
|
|
43
|
+
}, async (args) => queryStocks(args));
|
|
44
|
+
// 2. fetch_webpage
|
|
45
|
+
server.tool("fetch_webpage", "Scrape and extract structured data from any web page. Returns title, meta description, headings, main text content, and links. Optionally extract specific content using a CSS selector (#id, .class, or tag name).", {
|
|
46
|
+
url: z.string().url().describe("The URL to scrape"),
|
|
47
|
+
selector: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('Optional CSS-like selector to extract specific content (e.g., "#main", ".article-body", "article")'),
|
|
51
|
+
}, async (args) => fetchWebpage(args));
|
|
52
|
+
// 3. search_news
|
|
53
|
+
server.tool("search_news", "Search and aggregate news articles from multiple sources. Uses NewsAPI.org (if API key configured) or Google News RSS (free, no key). Filter by keyword, category, source, date range, and language.", {
|
|
54
|
+
query: z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Search keyword or phrase (e.g., 'artificial intelligence', 'climate change')"),
|
|
58
|
+
category: z
|
|
59
|
+
.enum([
|
|
60
|
+
"business",
|
|
61
|
+
"entertainment",
|
|
62
|
+
"general",
|
|
63
|
+
"health",
|
|
64
|
+
"science",
|
|
65
|
+
"sports",
|
|
66
|
+
"technology",
|
|
67
|
+
])
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("News category (only with NewsAPI key, ignored with Google News RSS)"),
|
|
70
|
+
source: z
|
|
71
|
+
.string()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe('Specific news source ID (e.g., "bbc-news", "cnn", "the-verge")'),
|
|
74
|
+
language: z
|
|
75
|
+
.string()
|
|
76
|
+
.optional()
|
|
77
|
+
.default("en")
|
|
78
|
+
.describe("Language code (default: en)"),
|
|
79
|
+
from: z
|
|
80
|
+
.string()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("Start date for articles (YYYY-MM-DD format, NewsAPI only)"),
|
|
83
|
+
to: z
|
|
84
|
+
.string()
|
|
85
|
+
.optional()
|
|
86
|
+
.describe("End date for articles (YYYY-MM-DD format, NewsAPI only)"),
|
|
87
|
+
maxResults: z
|
|
88
|
+
.number()
|
|
89
|
+
.int()
|
|
90
|
+
.min(1)
|
|
91
|
+
.max(100)
|
|
92
|
+
.optional()
|
|
93
|
+
.default(10)
|
|
94
|
+
.describe("Maximum number of articles to return (1-100, default: 10)"),
|
|
95
|
+
}, async (args) => searchNews(args));
|
|
96
|
+
// 4. query_weather
|
|
97
|
+
server.tool("query_weather", "Get current weather conditions and 7-day forecast. Uses Open-Meteo API (free, no API key required). Accepts city name or coordinates. Returns temperature, humidity, wind, precipitation, and daily forecasts.", {
|
|
98
|
+
city: z
|
|
99
|
+
.string()
|
|
100
|
+
.optional()
|
|
101
|
+
.describe('City name (e.g., "London", "New York", "Tokyo")'),
|
|
102
|
+
latitude: z
|
|
103
|
+
.number()
|
|
104
|
+
.min(-90)
|
|
105
|
+
.max(90)
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Latitude (-90 to 90). Use with longitude as alternative to city name."),
|
|
108
|
+
longitude: z
|
|
109
|
+
.number()
|
|
110
|
+
.min(-180)
|
|
111
|
+
.max(180)
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("Longitude (-180 to 180). Use with latitude as alternative to city name."),
|
|
114
|
+
units: z
|
|
115
|
+
.enum(["celsius", "fahrenheit"])
|
|
116
|
+
.optional()
|
|
117
|
+
.default("celsius")
|
|
118
|
+
.describe("Temperature units (default: celsius)"),
|
|
119
|
+
}, async (args) => queryWeather(args));
|
|
120
|
+
// 5. query_exchange_rates
|
|
121
|
+
server.tool("query_exchange_rates", "Get currency exchange rates and perform conversions. Supports all major fiat currencies (USD, EUR, GBP, JPY, etc.) and popular cryptocurrencies (BTC, ETH, SOL, etc.). Can fetch historical rates by date.", {
|
|
122
|
+
base: z
|
|
123
|
+
.string()
|
|
124
|
+
.describe('Base currency code (e.g., "USD", "EUR", "BTC")'),
|
|
125
|
+
target: z
|
|
126
|
+
.string()
|
|
127
|
+
.describe('Target currency code (e.g., "GBP", "JPY", "ETH")'),
|
|
128
|
+
amount: z
|
|
129
|
+
.number()
|
|
130
|
+
.positive()
|
|
131
|
+
.optional()
|
|
132
|
+
.default(1)
|
|
133
|
+
.describe("Amount to convert (default: 1)"),
|
|
134
|
+
date: z
|
|
135
|
+
.string()
|
|
136
|
+
.optional()
|
|
137
|
+
.describe("Historical date in YYYY-MM-DD format (fiat only). Omit for current rates."),
|
|
138
|
+
}, async (args) => queryExchangeRates(args));
|
|
139
|
+
// 6. query_public_data
|
|
140
|
+
server.tool("query_public_data", "Access miscellaneous public data APIs: Wikipedia summaries, IP geolocation, DNS lookups, and URL expansion. Select the sub-command and provide the relevant parameters.", {
|
|
141
|
+
command: z
|
|
142
|
+
.enum(["wikipedia", "ip_geolocation", "dns_lookup", "expand_url"])
|
|
143
|
+
.describe("Sub-command to run: wikipedia (article summary), ip_geolocation (IP location), dns_lookup (DNS records), expand_url (follow redirects)"),
|
|
144
|
+
query: z
|
|
145
|
+
.string()
|
|
146
|
+
.optional()
|
|
147
|
+
.describe('Search term for Wikipedia (e.g., "Theory of relativity")'),
|
|
148
|
+
ip: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe('IP address for geolocation (e.g., "8.8.8.8"). Omit to look up your own IP.'),
|
|
152
|
+
domain: z
|
|
153
|
+
.string()
|
|
154
|
+
.optional()
|
|
155
|
+
.describe('Domain for DNS lookup (e.g., "example.com")'),
|
|
156
|
+
recordType: z
|
|
157
|
+
.enum(["A", "AAAA", "MX", "TXT", "NS", "CNAME", "SOA"])
|
|
158
|
+
.optional()
|
|
159
|
+
.default("A")
|
|
160
|
+
.describe("DNS record type (default: A)"),
|
|
161
|
+
url: z
|
|
162
|
+
.string()
|
|
163
|
+
.optional()
|
|
164
|
+
.describe('Shortened URL to expand (e.g., "https://bit.ly/xyz")'),
|
|
165
|
+
language: z
|
|
166
|
+
.string()
|
|
167
|
+
.optional()
|
|
168
|
+
.default("en")
|
|
169
|
+
.describe("Language code for Wikipedia (default: en)"),
|
|
170
|
+
}, async (args) => queryPublicData(args));
|
|
171
|
+
// ─── Transport Selection & Startup ─────────────────────────────────────────
|
|
172
|
+
async function main() {
|
|
173
|
+
const args = process.argv.slice(2);
|
|
174
|
+
const useHttp = args.includes("--sse") || args.includes("--http");
|
|
175
|
+
if (useHttp) {
|
|
176
|
+
// HTTP / SSE transport for remote deployments
|
|
177
|
+
const portArg = args.find((a) => a.startsWith("--port="));
|
|
178
|
+
const port = portArg ? parseInt(portArg.split("=")[1], 10) : 3000;
|
|
179
|
+
try {
|
|
180
|
+
const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
181
|
+
const { createServer } = await import("node:http");
|
|
182
|
+
const { randomUUID } = await import("node:crypto");
|
|
183
|
+
const transports = {};
|
|
184
|
+
const httpServer = createServer(async (req, res) => {
|
|
185
|
+
// CORS headers
|
|
186
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
187
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
188
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
|
|
189
|
+
if (req.method === "OPTIONS") {
|
|
190
|
+
res.writeHead(204);
|
|
191
|
+
res.end();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Health check endpoint
|
|
195
|
+
if (req.url === "/health") {
|
|
196
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
197
|
+
res.end(JSON.stringify({ status: "ok", server: "data-aggregator", version: "1.0.0" }));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (req.url !== "/mcp") {
|
|
201
|
+
res.writeHead(404);
|
|
202
|
+
res.end("Not found");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
206
|
+
// Parse body for POST requests
|
|
207
|
+
let body = undefined;
|
|
208
|
+
if (req.method === "POST") {
|
|
209
|
+
const chunks = [];
|
|
210
|
+
for await (const chunk of req) {
|
|
211
|
+
chunks.push(chunk);
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
body = JSON.parse(Buffer.concat(chunks).toString());
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
218
|
+
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (req.method === "GET" && sessionId && transports[sessionId]) {
|
|
223
|
+
await transports[sessionId].handleRequest(req, res);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (req.method === "POST" && sessionId && transports[sessionId]) {
|
|
227
|
+
await transports[sessionId].handleRequest(req, res, body);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (req.method === "POST" && !sessionId) {
|
|
231
|
+
// New session
|
|
232
|
+
const transport = new StreamableHTTPServerTransport({
|
|
233
|
+
sessionIdGenerator: () => randomUUID(),
|
|
234
|
+
onsessioninitialized: (id) => {
|
|
235
|
+
transports[id] = transport;
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
transport.onclose = () => {
|
|
239
|
+
if (transport.sessionId) {
|
|
240
|
+
delete transports[transport.sessionId];
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const sessionServer = new McpServer({ name: "data-aggregator", version: "1.0.0" }, { capabilities: { logging: {} } });
|
|
244
|
+
// Re-register tools for this session's server
|
|
245
|
+
registerTools(sessionServer);
|
|
246
|
+
await sessionServer.connect(transport);
|
|
247
|
+
await transport.handleRequest(req, res, body);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
251
|
+
res.end(JSON.stringify({
|
|
252
|
+
jsonrpc: "2.0",
|
|
253
|
+
error: { code: -32000, message: "Invalid request or session" },
|
|
254
|
+
id: null,
|
|
255
|
+
}));
|
|
256
|
+
});
|
|
257
|
+
httpServer.listen(port, "0.0.0.0", () => {
|
|
258
|
+
console.error(`Data Aggregator MCP server (HTTP) listening on http://0.0.0.0:${port}/mcp`);
|
|
259
|
+
console.error(`Health check: http://0.0.0.0:${port}/health`);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
console.error("Failed to start HTTP transport. Make sure @modelcontextprotocol/node is installed.", err);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// stdio transport (default) for Claude Desktop, CLI
|
|
269
|
+
const transport = new StdioServerTransport();
|
|
270
|
+
await server.connect(transport);
|
|
271
|
+
console.error("Data Aggregator MCP server started (stdio transport)");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Register all tools on a given McpServer instance.
|
|
276
|
+
* Used for per-session servers in HTTP mode.
|
|
277
|
+
*/
|
|
278
|
+
function registerTools(s) {
|
|
279
|
+
s.tool("query_stocks", "Fetch real-time stock and cryptocurrency market data.", {
|
|
280
|
+
symbol: z.string().describe("Ticker symbol (e.g., AAPL, BTC)"),
|
|
281
|
+
type: z.enum(["stock", "crypto", "auto"]).optional().default("auto")
|
|
282
|
+
.describe('Asset type. "auto" detects based on symbol.'),
|
|
283
|
+
}, async (args) => queryStocks(args));
|
|
284
|
+
s.tool("fetch_webpage", "Scrape and extract structured data from any web page.", {
|
|
285
|
+
url: z.string().url().describe("The URL to scrape"),
|
|
286
|
+
selector: z.string().optional()
|
|
287
|
+
.describe("Optional CSS-like selector to extract specific content"),
|
|
288
|
+
}, async (args) => fetchWebpage(args));
|
|
289
|
+
s.tool("search_news", "Search and aggregate news articles from multiple sources.", {
|
|
290
|
+
query: z.string().optional().describe("Search keyword or phrase"),
|
|
291
|
+
category: z.enum(["business", "entertainment", "general", "health", "science", "sports", "technology"]).optional(),
|
|
292
|
+
source: z.string().optional().describe("Specific news source ID"),
|
|
293
|
+
language: z.string().optional().default("en"),
|
|
294
|
+
from: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
295
|
+
to: z.string().optional().describe("End date (YYYY-MM-DD)"),
|
|
296
|
+
maxResults: z.number().int().min(1).max(100).optional().default(10),
|
|
297
|
+
}, async (args) => searchNews(args));
|
|
298
|
+
s.tool("query_weather", "Get current weather conditions and 7-day forecast via Open-Meteo.", {
|
|
299
|
+
city: z.string().optional().describe("City name"),
|
|
300
|
+
latitude: z.number().min(-90).max(90).optional(),
|
|
301
|
+
longitude: z.number().min(-180).max(180).optional(),
|
|
302
|
+
units: z.enum(["celsius", "fahrenheit"]).optional().default("celsius"),
|
|
303
|
+
}, async (args) => queryWeather(args));
|
|
304
|
+
s.tool("query_exchange_rates", "Get currency exchange rates and perform conversions.", {
|
|
305
|
+
base: z.string().describe("Base currency code"),
|
|
306
|
+
target: z.string().describe("Target currency code"),
|
|
307
|
+
amount: z.number().positive().optional().default(1),
|
|
308
|
+
date: z.string().optional().describe("Historical date (YYYY-MM-DD)"),
|
|
309
|
+
}, async (args) => queryExchangeRates(args));
|
|
310
|
+
s.tool("query_public_data", "Access Wikipedia, IP geolocation, DNS lookup, and URL expansion.", {
|
|
311
|
+
command: z.enum(["wikipedia", "ip_geolocation", "dns_lookup", "expand_url"]),
|
|
312
|
+
query: z.string().optional(),
|
|
313
|
+
ip: z.string().optional(),
|
|
314
|
+
domain: z.string().optional(),
|
|
315
|
+
recordType: z.enum(["A", "AAAA", "MX", "TXT", "NS", "CNAME", "SOA"]).optional().default("A"),
|
|
316
|
+
url: z.string().optional(),
|
|
317
|
+
language: z.string().optional().default("en"),
|
|
318
|
+
}, async (args) => queryPublicData(args));
|
|
319
|
+
}
|
|
320
|
+
// ─── Graceful shutdown ─────────────────────────────────────────────────────
|
|
321
|
+
function shutdown() {
|
|
322
|
+
console.error("Shutting down Data Aggregator MCP server...");
|
|
323
|
+
cache.destroy();
|
|
324
|
+
process.exit(0);
|
|
325
|
+
}
|
|
326
|
+
process.on("SIGINT", shutdown);
|
|
327
|
+
process.on("SIGTERM", shutdown);
|
|
328
|
+
// ─── Start ─────────────────────────────────────────────────────────────────
|
|
329
|
+
main().catch((err) => {
|
|
330
|
+
console.error("Fatal error starting server:", err);
|
|
331
|
+
process.exit(1);
|
|
332
|
+
});
|
|
333
|
+
//# sourceMappingURL=index.js.map
|