finmap-mcp 2.0.7 → 3.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 +56 -38
- package/dist/core.js +53 -485
- package/dist/stdio-server.js +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The finmap.org MCP server provides comprehensive historical data from the US, UK
|
|
|
11
11
|
| American Stock Exchange | `amex` | United States | 2024-12-09 | Daily |
|
|
12
12
|
| US Combined (AMEX + NASDAQ + NYSE) | `us-all` | United States | 2024-12-09 | Daily |
|
|
13
13
|
| London Stock Exchange | `lse` | United Kingdom | 2025-02-07 | Hourly (weekdays) |
|
|
14
|
-
| Hong Kong Stock Exchange | `hkex` | Hong Kong | 2025-09-
|
|
14
|
+
| Hong Kong Stock Exchange | `hkex` | Hong Kong | 2025-09-29 | Every 30 minutes (weekdays) |
|
|
15
15
|
| Borsa Istanbul | `bist` | Turkey | 2015-11-30 | Every two months |
|
|
16
16
|
| Moscow Exchange | `moex` | Russia | 2011-12-19 | Every 15 minutes (weekdays) |
|
|
17
17
|
|
|
@@ -59,13 +59,31 @@ npx finmap-mcp
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
|
|
63
|
+
## HTTP API Wrapper
|
|
64
|
+
|
|
65
|
+
The server also exposes a compact HTTP API for GPT Actions and direct integration.
|
|
66
|
+
|
|
67
|
+
- `GET /api/openapi.json`
|
|
68
|
+
- `GET /api/list-exchanges`
|
|
69
|
+
- `POST /api/list-sectors`
|
|
70
|
+
- `POST /api/list-sector-companies`
|
|
71
|
+
- `POST /api/search-companies`
|
|
72
|
+
- `POST /api/market-overview`
|
|
73
|
+
- `POST /api/sector-performance`
|
|
74
|
+
- `POST /api/rank-stocks`
|
|
75
|
+
- `POST /api/stock-snapshot`
|
|
76
|
+
- `POST /api/company-profile`
|
|
77
|
+
|
|
78
|
+
A ready-to-import GPT Actions schema is available in `gpt-actions.yaml`.
|
|
79
|
+
|
|
62
80
|
## Available Tools
|
|
63
81
|
|
|
64
|
-
### 1. `
|
|
65
|
-
- Title: List exchanges
|
|
66
|
-
- Description: Return supported exchanges
|
|
82
|
+
### 1. `list_supported_exchanges`
|
|
83
|
+
- Title: List supported stock exchanges
|
|
84
|
+
- Description: Return metadata for all supported stock exchanges in the Finmap dataset. Includes exchange ID, exchange name, country, currency, earliest available historical data date, and update frequency.
|
|
67
85
|
|
|
68
|
-
**Example
|
|
86
|
+
**Example Prompt:** `#finmap-mcp list available stock exchanges`
|
|
69
87
|
|
|
70
88
|
**Example Response:**
|
|
71
89
|
```json
|
|
@@ -132,18 +150,18 @@ npx finmap-mcp
|
|
|
132
150
|
"name": "Hong Kong Stock Exchange",
|
|
133
151
|
"country": "Hong Kong",
|
|
134
152
|
"currency": "HKD",
|
|
135
|
-
"availableSince": "2025-09-
|
|
153
|
+
"availableSince": "2025-09-29",
|
|
136
154
|
"updateFrequency": "Every 30 minutes (weekdays)"
|
|
137
155
|
}
|
|
138
156
|
]
|
|
139
157
|
}
|
|
140
158
|
```
|
|
141
159
|
|
|
142
|
-
### 2. `
|
|
143
|
-
- Title: List sectors
|
|
144
|
-
- Description:
|
|
160
|
+
### 2. `list_exchange_sectors`
|
|
161
|
+
- Title: List sectors for a stock exchange
|
|
162
|
+
- Description: Return all business sectors available on a specific exchange and trading date. Each sector includes the number of companies in that sector.
|
|
145
163
|
|
|
146
|
-
**Example
|
|
164
|
+
**Example Prompt:** `#finmap-mcp List sectors for the Turkish stock exchange`
|
|
147
165
|
|
|
148
166
|
**Example Response:**
|
|
149
167
|
```json
|
|
@@ -236,11 +254,11 @@ npx finmap-mcp
|
|
|
236
254
|
}
|
|
237
255
|
```
|
|
238
256
|
|
|
239
|
-
### 3. `
|
|
240
|
-
- Title: List
|
|
241
|
-
- Description: Return company tickers and names for an exchange on a specific date
|
|
257
|
+
### 3. `list_sector_companies`
|
|
258
|
+
- Title: List companies by sector
|
|
259
|
+
- Description: Return company tickers and names grouped by sector for an exchange on a specific trading date. Optionally filter results by a single sector.
|
|
242
260
|
|
|
243
|
-
**Example
|
|
261
|
+
**Example Prompt:** `#finmap-mcp List companies in the Real Estate sector`
|
|
244
262
|
|
|
245
263
|
**Example Response:**
|
|
246
264
|
```json
|
|
@@ -271,11 +289,11 @@ npx finmap-mcp
|
|
|
271
289
|
}
|
|
272
290
|
```
|
|
273
291
|
|
|
274
|
-
### 4. `
|
|
275
|
-
- Title: Search companies
|
|
276
|
-
- Description:
|
|
292
|
+
### 4. `search_exchange_companies`
|
|
293
|
+
- Title: Search companies by name or ticker
|
|
294
|
+
- Description: Search for companies on a specific exchange by partial ticker symbol or company name. Results are ranked by relevance using ticker and name similarity.
|
|
277
295
|
|
|
278
|
-
**Example
|
|
296
|
+
**Example Prompt:** `#finmap-mcp Search for companies named 'Sprouts'`
|
|
279
297
|
|
|
280
298
|
**Example Response:**
|
|
281
299
|
```json
|
|
@@ -295,11 +313,11 @@ npx finmap-mcp
|
|
|
295
313
|
}
|
|
296
314
|
```
|
|
297
315
|
|
|
298
|
-
### 5. `
|
|
299
|
-
- Title:
|
|
300
|
-
- Description:
|
|
316
|
+
### 5. `analyze_market_overview`
|
|
317
|
+
- Title: Analyze market overview
|
|
318
|
+
- Description: Return aggregated statistics for a stock exchange on a specific date. Includes total market capitalization, trading volume, total traded value, number of trades, and sector-level market breakdown.
|
|
301
319
|
|
|
302
|
-
**Example
|
|
320
|
+
**Example Prompt:** `#finmap-mcp market overview for Nasdaq`
|
|
303
321
|
|
|
304
322
|
**Example Response:**
|
|
305
323
|
```json
|
|
@@ -358,11 +376,11 @@ npx finmap-mcp
|
|
|
358
376
|
}
|
|
359
377
|
```
|
|
360
378
|
|
|
361
|
-
### 6. `
|
|
362
|
-
- Title:
|
|
363
|
-
- Description:
|
|
379
|
+
### 6. `analyze_sector_performance`
|
|
380
|
+
- Title: Analyze sector performance
|
|
381
|
+
- Description: Return aggregated metrics for each sector in a stock exchange. Includes sector market capitalization, price change percentage, trading volume, traded value, number of trades, and number of companies in the sector.
|
|
364
382
|
|
|
365
|
-
**Example
|
|
383
|
+
**Example Prompt:** `#finmap-mcp Get overview for the Utilities sector`
|
|
366
384
|
|
|
367
385
|
**Example Response:**
|
|
368
386
|
```json
|
|
@@ -384,11 +402,11 @@ npx finmap-mcp
|
|
|
384
402
|
}
|
|
385
403
|
```
|
|
386
404
|
|
|
387
|
-
### 7. `
|
|
388
|
-
- Title:
|
|
389
|
-
- Description:
|
|
405
|
+
### 7. `get_stock_snapshot`
|
|
406
|
+
- Title: Get stock market snapshot
|
|
407
|
+
- Description: Return detailed trading metrics for a single stock ticker on a specific exchange and trading date. Includes price open, last sale price, price change percentage, trading volume, traded value, number of trades, and market capitalization.
|
|
390
408
|
|
|
391
|
-
**Example
|
|
409
|
+
**Example Prompt:** `#finmap-mcp Dominion Energy, stock data`
|
|
392
410
|
|
|
393
411
|
**Example Response:**
|
|
394
412
|
```json
|
|
@@ -412,11 +430,11 @@ npx finmap-mcp
|
|
|
412
430
|
}
|
|
413
431
|
```
|
|
414
432
|
|
|
415
|
-
### 8. `
|
|
416
|
-
- Title: Rank
|
|
417
|
-
- Description:
|
|
433
|
+
### 8. `rank_exchange_companies`
|
|
434
|
+
- Title: Rank companies by market metric
|
|
435
|
+
- Description: Return companies ranked by a selected market metric on a specific exchange. Supported ranking metrics: market capitalization, price change percentage, trading volume, traded value, and number of trades. Results can be limited and optionally filtered by sector.
|
|
418
436
|
|
|
419
|
-
**Example
|
|
437
|
+
**Example Prompt:** `#finmap-mcp UK, rank stocks by market cap`
|
|
420
438
|
|
|
421
439
|
**Example Response:**
|
|
422
440
|
```json
|
|
@@ -488,11 +506,11 @@ npx finmap-mcp
|
|
|
488
506
|
}
|
|
489
507
|
```
|
|
490
508
|
|
|
491
|
-
### 9. `
|
|
492
|
-
- Title:
|
|
493
|
-
- Description:
|
|
509
|
+
### 9. `get_company_profile_us`
|
|
510
|
+
- Title: Get US company profile
|
|
511
|
+
- Description: Return business description and background information for a US-listed company. Supported exchanges: NASDAQ, NYSE, and AMEX.
|
|
494
512
|
|
|
495
|
-
**Example
|
|
513
|
+
**Example Prompt:** `#finmap-mcp Sprouts Farmers Market, get company profile`
|
|
496
514
|
|
|
497
515
|
**Example Response:**
|
|
498
516
|
```json
|
package/dist/core.js
CHANGED
|
@@ -1,71 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const BASE_URL = "https://finmap.org";
|
|
3
|
-
const DATA_BASE_URL = "https://raw.githubusercontent.com/finmap-org";
|
|
4
|
-
const INFO = {
|
|
5
|
-
provider: "finmap.org",
|
|
6
|
-
description: "Discover interactive stock charts and curated news at finmap.org",
|
|
7
|
-
github: "https://github.com/finmap-org",
|
|
8
|
-
donate: {
|
|
9
|
-
patreon: "https://patreon.com/finmap",
|
|
10
|
-
boosty: "https://boosty.to/finmap",
|
|
11
|
-
},
|
|
12
|
-
issues: "https://github.com/finmap-org/mcp-server/issues",
|
|
13
|
-
feedback: "contact@finmap.org",
|
|
14
|
-
};
|
|
15
|
-
const STOCK_EXCHANGES = [
|
|
16
|
-
"amex",
|
|
17
|
-
"nasdaq",
|
|
18
|
-
"nyse",
|
|
19
|
-
"us-all",
|
|
20
|
-
"lse",
|
|
21
|
-
"moex",
|
|
22
|
-
"bist",
|
|
23
|
-
"hkex",
|
|
24
|
-
];
|
|
25
|
-
const US_EXCHANGES = ["amex", "nasdaq", "nyse"];
|
|
26
|
-
const SORT_FIELDS = [
|
|
27
|
-
"priceChangePct",
|
|
28
|
-
"marketCap",
|
|
29
|
-
"value",
|
|
30
|
-
"volume",
|
|
31
|
-
"numTrades",
|
|
32
|
-
];
|
|
33
|
-
const SORT_ORDERS = ["asc", "desc"];
|
|
34
|
-
const INDICES = {
|
|
35
|
-
EXCHANGE: 0,
|
|
36
|
-
COUNTRY: 1,
|
|
37
|
-
TYPE: 2,
|
|
38
|
-
SECTOR: 3,
|
|
39
|
-
INDUSTRY: 4,
|
|
40
|
-
CURRENCY_ID: 5,
|
|
41
|
-
TICKER: 6,
|
|
42
|
-
NAME_ENG: 7,
|
|
43
|
-
NAME_ENG_SHORT: 8,
|
|
44
|
-
NAME_ORIGINAL: 9,
|
|
45
|
-
NAME_ORIGINAL_SHORT: 10,
|
|
46
|
-
PRICE_OPEN: 11,
|
|
47
|
-
PRICE_LAST_SALE: 12,
|
|
48
|
-
PRICE_CHANGE_PCT: 13,
|
|
49
|
-
VOLUME: 14,
|
|
50
|
-
VALUE: 15,
|
|
51
|
-
NUM_TRADES: 16,
|
|
52
|
-
MARKET_CAP: 17,
|
|
53
|
-
LISTED_FROM: 18,
|
|
54
|
-
LISTED_TILL: 19,
|
|
55
|
-
WIKI_PAGE_ID_ENG: 20,
|
|
56
|
-
WIKI_PAGE_ID_ORIGINAL: 21,
|
|
57
|
-
ITEMS_PER_SECTOR: 22,
|
|
58
|
-
};
|
|
59
|
-
const EXCHANGE_TO_COUNTRY_MAP = {
|
|
60
|
-
amex: "us",
|
|
61
|
-
nasdaq: "us",
|
|
62
|
-
nyse: "us",
|
|
63
|
-
"us-all": "us",
|
|
64
|
-
lse: "uk",
|
|
65
|
-
moex: "russia",
|
|
66
|
-
bist: "turkey",
|
|
67
|
-
hkex: "hongkong",
|
|
68
|
-
};
|
|
1
|
+
import { companyProfileSchema, getCompanyProfile, getMarketOverview, getSectorsOverview, getStockData, listExchanges, listSectors, listSectorsSchema, listTickers, listTickersSchema, marketOverviewSchema, rankStocks, rankStocksSchema, searchCompanies, searchCompaniesSchema, sectorsOverviewSchema, stockDataSchema, } from "./api.js";
|
|
69
2
|
function createResponse(data) {
|
|
70
3
|
return {
|
|
71
4
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
@@ -74,475 +7,110 @@ function createResponse(data) {
|
|
|
74
7
|
function createErrorResponse(error) {
|
|
75
8
|
return createResponse(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
|
|
76
9
|
}
|
|
77
|
-
function createCharts(exchange, date) {
|
|
78
|
-
return {
|
|
79
|
-
histogram: `${BASE_URL}/?chart=histogram&data=marketcap¤cy=USD&exchange=${exchange}`,
|
|
80
|
-
treemap: `${BASE_URL}/?chart=treemap&data=marketcap¤cy=USD&exchange=${exchange}${date ? `&date=${date.replaceAll("-", "/")}` : ""}`,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function calculateMatchScore(ticker, name, searchTerm) {
|
|
84
|
-
const tickerLower = ticker.toLowerCase();
|
|
85
|
-
const nameLower = name.toLowerCase();
|
|
86
|
-
if (tickerLower === searchTerm)
|
|
87
|
-
return 100;
|
|
88
|
-
if (tickerLower.startsWith(searchTerm))
|
|
89
|
-
return 90;
|
|
90
|
-
if (tickerLower.includes(searchTerm))
|
|
91
|
-
return 80;
|
|
92
|
-
if (nameLower.includes(searchTerm))
|
|
93
|
-
return 70;
|
|
94
|
-
return 0;
|
|
95
|
-
}
|
|
96
|
-
const EXCHANGE_INFO = {
|
|
97
|
-
amex: {
|
|
98
|
-
name: "American Stock Exchange",
|
|
99
|
-
country: "United States",
|
|
100
|
-
currency: "USD",
|
|
101
|
-
availableSince: "2024-12-09",
|
|
102
|
-
updateFrequency: "Daily",
|
|
103
|
-
},
|
|
104
|
-
nasdaq: {
|
|
105
|
-
name: "NASDAQ Stock Market",
|
|
106
|
-
country: "United States",
|
|
107
|
-
currency: "USD",
|
|
108
|
-
availableSince: "2024-12-09",
|
|
109
|
-
updateFrequency: "Daily",
|
|
110
|
-
},
|
|
111
|
-
nyse: {
|
|
112
|
-
name: "New York Stock Exchange",
|
|
113
|
-
country: "United States",
|
|
114
|
-
currency: "USD",
|
|
115
|
-
availableSince: "2024-12-09",
|
|
116
|
-
updateFrequency: "Daily",
|
|
117
|
-
},
|
|
118
|
-
"us-all": {
|
|
119
|
-
name: "US Combined (AMEX + NASDAQ + NYSE)",
|
|
120
|
-
country: "United States",
|
|
121
|
-
currency: "USD",
|
|
122
|
-
availableSince: "2024-12-09",
|
|
123
|
-
updateFrequency: "Daily",
|
|
124
|
-
},
|
|
125
|
-
lse: {
|
|
126
|
-
name: "London Stock Exchange",
|
|
127
|
-
country: "United Kingdom",
|
|
128
|
-
currency: "GBP",
|
|
129
|
-
availableSince: "2025-02-07",
|
|
130
|
-
updateFrequency: "Hourly (weekdays)",
|
|
131
|
-
},
|
|
132
|
-
moex: {
|
|
133
|
-
name: "Moscow Exchange",
|
|
134
|
-
country: "Russia",
|
|
135
|
-
currency: "RUB",
|
|
136
|
-
availableSince: "2011-12-19",
|
|
137
|
-
updateFrequency: "Every 15 minutes (weekdays)",
|
|
138
|
-
},
|
|
139
|
-
bist: {
|
|
140
|
-
name: "Borsa Istanbul",
|
|
141
|
-
country: "Turkey",
|
|
142
|
-
currency: "TRY",
|
|
143
|
-
availableSince: "2015-11-30",
|
|
144
|
-
updateFrequency: "Every two months",
|
|
145
|
-
},
|
|
146
|
-
hkex: {
|
|
147
|
-
name: "Hong Kong Stock Exchange",
|
|
148
|
-
country: "Hong Kong",
|
|
149
|
-
currency: "HKD",
|
|
150
|
-
availableSince: "2025-09-29",
|
|
151
|
-
updateFrequency: "Every 30 minutes (weekdays)",
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
const exchangeSchema = z
|
|
155
|
-
.enum(STOCK_EXCHANGES)
|
|
156
|
-
.describe("Stock exchange: amex, nasdaq, nyse, us-all, lse, moex, bist, hkex");
|
|
157
|
-
const dateSchema = {
|
|
158
|
-
year: z.number().int().min(2012).optional(),
|
|
159
|
-
month: z.number().int().min(1).max(12).optional(),
|
|
160
|
-
day: z.number().int().min(1).max(31).optional(),
|
|
161
|
-
};
|
|
162
|
-
function buildDateString(year, month, day) {
|
|
163
|
-
const currentDate = new Date();
|
|
164
|
-
const y = year ?? currentDate.getFullYear();
|
|
165
|
-
const m = month ?? currentDate.getMonth() + 1;
|
|
166
|
-
const d = day ?? currentDate.getDate();
|
|
167
|
-
return `${y.toString()}/${m.toString().padStart(2, "0")}/${d.toString().padStart(2, "0")}`;
|
|
168
|
-
}
|
|
169
|
-
function validateAndFormatDate(dateString) {
|
|
170
|
-
const date = dateString.replaceAll("/", "-");
|
|
171
|
-
z.string().date().parse(date);
|
|
172
|
-
const dayOfWeek = new Date(date).getDay();
|
|
173
|
-
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
|
174
|
-
throw new Error("Data is only available for work days (Monday to Friday)");
|
|
175
|
-
}
|
|
176
|
-
return date;
|
|
177
|
-
}
|
|
178
|
-
function getDate(year, month, day) {
|
|
179
|
-
const dateString = buildDateString(year, month, day);
|
|
180
|
-
return validateAndFormatDate(dateString);
|
|
181
|
-
}
|
|
182
|
-
async function fetchMarketData(stockExchange, formattedDate) {
|
|
183
|
-
const country = EXCHANGE_TO_COUNTRY_MAP[stockExchange];
|
|
184
|
-
const date = formattedDate.replaceAll("-", "/");
|
|
185
|
-
const url = `${DATA_BASE_URL}/data-${country}/refs/heads/main/marketdata/${date}/${stockExchange}.json`;
|
|
186
|
-
const response = await fetch(url);
|
|
187
|
-
if (response.status === 404) {
|
|
188
|
-
throw new Error(`Not found, try another date. The date must be on or after ${EXCHANGE_INFO[stockExchange].availableSince} for ${stockExchange}`);
|
|
189
|
-
}
|
|
190
|
-
return response.json();
|
|
191
|
-
}
|
|
192
|
-
async function fetchSecurityInfo(exchange, ticker) {
|
|
193
|
-
const firstLetter = ticker.charAt(0).toUpperCase();
|
|
194
|
-
const url = `${DATA_BASE_URL}/data-us/refs/heads/main/securities/${exchange}/${firstLetter}/${ticker}.json`;
|
|
195
|
-
const response = await fetch(url);
|
|
196
|
-
if (response.status === 404) {
|
|
197
|
-
throw new Error(`Security ${ticker} not found on ${exchange}`);
|
|
198
|
-
}
|
|
199
|
-
const data = (await response.json());
|
|
200
|
-
return data;
|
|
201
|
-
}
|
|
202
10
|
export function registerFinmapTools(server) {
|
|
203
|
-
server.registerTool("
|
|
204
|
-
title: "List exchanges",
|
|
205
|
-
description: "Return supported exchanges
|
|
11
|
+
server.registerTool("list_supported_exchanges", {
|
|
12
|
+
title: "List supported stock exchanges",
|
|
13
|
+
description: "Return metadata for all supported stock exchanges in the Finmap dataset, including exchange ID, exchange name, country, currency, earliest available historical data date, and update frequency.",
|
|
206
14
|
inputSchema: {},
|
|
207
15
|
}, async () => {
|
|
208
16
|
try {
|
|
209
|
-
|
|
210
|
-
id,
|
|
211
|
-
...info,
|
|
212
|
-
}));
|
|
213
|
-
return createResponse({ info: INFO, exchanges });
|
|
17
|
+
return createResponse(listExchanges());
|
|
214
18
|
}
|
|
215
19
|
catch (error) {
|
|
216
20
|
return createErrorResponse(error);
|
|
217
21
|
}
|
|
218
22
|
});
|
|
219
|
-
server.registerTool("
|
|
220
|
-
title: "List sectors",
|
|
221
|
-
description: "
|
|
222
|
-
inputSchema:
|
|
223
|
-
}, async (
|
|
23
|
+
server.registerTool("list_exchange_sectors", {
|
|
24
|
+
title: "List sectors for a stock exchange",
|
|
25
|
+
description: "Return all business sectors available on a specific exchange and trading date. Each sector includes the number of companies in that sector.",
|
|
26
|
+
inputSchema: listSectorsSchema.shape,
|
|
27
|
+
}, async (input) => {
|
|
224
28
|
try {
|
|
225
|
-
|
|
226
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
227
|
-
const sectorCounts = {};
|
|
228
|
-
marketDataResponse.securities.data.forEach((item) => {
|
|
229
|
-
if (item[INDICES.TYPE] !== "sector" && item[INDICES.SECTOR]) {
|
|
230
|
-
sectorCounts[item[INDICES.SECTOR]] =
|
|
231
|
-
(sectorCounts[item[INDICES.SECTOR]] || 0) + 1;
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
const sectors = Object.entries(sectorCounts).map(([name, count]) => ({
|
|
235
|
-
name,
|
|
236
|
-
itemsPerSector: count,
|
|
237
|
-
}));
|
|
238
|
-
return createResponse({
|
|
239
|
-
info: INFO,
|
|
240
|
-
date: formattedDate,
|
|
241
|
-
exchange: stockExchange.toUpperCase(),
|
|
242
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
243
|
-
sectors,
|
|
244
|
-
});
|
|
29
|
+
return createResponse(await listSectors(listSectorsSchema.parse(input)));
|
|
245
30
|
}
|
|
246
31
|
catch (error) {
|
|
247
32
|
return createErrorResponse(error);
|
|
248
33
|
}
|
|
249
34
|
});
|
|
250
|
-
server.registerTool("
|
|
251
|
-
title: "List
|
|
252
|
-
description: "Return company tickers and names for an exchange on a specific date
|
|
253
|
-
inputSchema:
|
|
254
|
-
|
|
255
|
-
...dateSchema,
|
|
256
|
-
sector: z.string().optional().describe("Filter by specific sector"),
|
|
257
|
-
englishNames: z
|
|
258
|
-
.boolean()
|
|
259
|
-
.default(true)
|
|
260
|
-
.describe("Use English names if available"),
|
|
261
|
-
},
|
|
262
|
-
}, async ({ stockExchange, year, month, day, sector, englishNames, }) => {
|
|
35
|
+
server.registerTool("list_sector_companies", {
|
|
36
|
+
title: "List companies by sector",
|
|
37
|
+
description: "Return company tickers and names grouped by sector for an exchange on a specific trading date. Optionally filter results by a single sector.",
|
|
38
|
+
inputSchema: listTickersSchema.shape,
|
|
39
|
+
}, async (input) => {
|
|
263
40
|
try {
|
|
264
|
-
|
|
265
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
266
|
-
const sectorGroups = {};
|
|
267
|
-
marketDataResponse.securities.data.forEach((item) => {
|
|
268
|
-
if (item[INDICES.TYPE] !== "sector" &&
|
|
269
|
-
item[INDICES.SECTOR] &&
|
|
270
|
-
(!sector || item[INDICES.SECTOR] === sector)) {
|
|
271
|
-
const sectorName = item[INDICES.SECTOR];
|
|
272
|
-
const ticker = item[INDICES.TICKER];
|
|
273
|
-
const name = englishNames
|
|
274
|
-
? item[INDICES.NAME_ENG]
|
|
275
|
-
: item[INDICES.NAME_ORIGINAL_SHORT] || item[INDICES.NAME_ENG];
|
|
276
|
-
if (ticker && name) {
|
|
277
|
-
if (!sectorGroups[sectorName])
|
|
278
|
-
sectorGroups[sectorName] = [];
|
|
279
|
-
sectorGroups[sectorName].push({ ticker, name });
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
Object.values(sectorGroups).forEach((companies) => {
|
|
284
|
-
companies.sort((a, b) => a.ticker.localeCompare(b.ticker));
|
|
285
|
-
});
|
|
286
|
-
return createResponse({
|
|
287
|
-
info: INFO,
|
|
288
|
-
date: formattedDate,
|
|
289
|
-
exchange: stockExchange.toUpperCase(),
|
|
290
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
291
|
-
sectors: sectorGroups,
|
|
292
|
-
});
|
|
41
|
+
return createResponse(await listTickers(listTickersSchema.parse(input)));
|
|
293
42
|
}
|
|
294
43
|
catch (error) {
|
|
295
44
|
return createErrorResponse(error);
|
|
296
45
|
}
|
|
297
46
|
});
|
|
298
|
-
server.registerTool("
|
|
299
|
-
title: "Search companies",
|
|
300
|
-
description: "
|
|
301
|
-
inputSchema:
|
|
302
|
-
|
|
303
|
-
...dateSchema,
|
|
304
|
-
query: z
|
|
305
|
-
.string()
|
|
306
|
-
.describe("Search term (partial ticker or company name)"),
|
|
307
|
-
limit: z
|
|
308
|
-
.number()
|
|
309
|
-
.int()
|
|
310
|
-
.min(1)
|
|
311
|
-
.max(50)
|
|
312
|
-
.default(10)
|
|
313
|
-
.describe("Maximum results"),
|
|
314
|
-
},
|
|
315
|
-
}, async ({ stockExchange, year, month, day, query, limit, }) => {
|
|
47
|
+
server.registerTool("search_exchange_companies", {
|
|
48
|
+
title: "Search companies by name or ticker",
|
|
49
|
+
description: "Search for companies on a specific exchange by partial ticker symbol or company name. Results are ranked by relevance using ticker and name similarity.",
|
|
50
|
+
inputSchema: searchCompaniesSchema.shape,
|
|
51
|
+
}, async (input) => {
|
|
316
52
|
try {
|
|
317
|
-
|
|
318
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
319
|
-
const searchTerm = query.toLowerCase();
|
|
320
|
-
const matches = marketDataResponse.securities.data
|
|
321
|
-
.filter((item) => item[INDICES.TYPE] !== "sector" && item[INDICES.SECTOR])
|
|
322
|
-
.map((item) => ({
|
|
323
|
-
ticker: item[INDICES.TICKER],
|
|
324
|
-
name: item[INDICES.NAME_ENG],
|
|
325
|
-
sector: item[INDICES.SECTOR],
|
|
326
|
-
score: calculateMatchScore(item[INDICES.TICKER], item[INDICES.NAME_ENG], searchTerm),
|
|
327
|
-
}))
|
|
328
|
-
.filter((match) => match.score > 0)
|
|
329
|
-
.sort((a, b) => b.score - a.score)
|
|
330
|
-
.slice(0, limit);
|
|
331
|
-
return createResponse({
|
|
332
|
-
info: INFO,
|
|
333
|
-
date: formattedDate,
|
|
334
|
-
exchange: stockExchange.toUpperCase(),
|
|
335
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
336
|
-
query,
|
|
337
|
-
matches,
|
|
338
|
-
});
|
|
53
|
+
return createResponse(await searchCompanies(searchCompaniesSchema.parse(input)));
|
|
339
54
|
}
|
|
340
55
|
catch (error) {
|
|
341
56
|
return createErrorResponse(error);
|
|
342
57
|
}
|
|
343
58
|
});
|
|
344
|
-
server.registerTool("
|
|
345
|
-
title: "
|
|
346
|
-
description: "
|
|
347
|
-
inputSchema:
|
|
348
|
-
}, async (
|
|
59
|
+
server.registerTool("analyze_market_overview", {
|
|
60
|
+
title: "Analyze market overview",
|
|
61
|
+
description: "Return aggregated statistics for a stock exchange on a specific date, including total market capitalization, trading volume, total traded value, number of trades, and sector-level market breakdown.",
|
|
62
|
+
inputSchema: marketOverviewSchema.shape,
|
|
63
|
+
}, async (input) => {
|
|
349
64
|
try {
|
|
350
|
-
|
|
351
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
352
|
-
const sectorItems = marketDataResponse.securities.data.filter((item) => item[INDICES.TYPE] === "sector");
|
|
353
|
-
let marketTotal = {};
|
|
354
|
-
const sectors = [];
|
|
355
|
-
sectorItems.forEach((item) => {
|
|
356
|
-
const sectorData = {
|
|
357
|
-
name: item[INDICES.TICKER],
|
|
358
|
-
marketCap: item[INDICES.MARKET_CAP],
|
|
359
|
-
marketCapChangePct: item[INDICES.PRICE_CHANGE_PCT],
|
|
360
|
-
volume: item[INDICES.VOLUME],
|
|
361
|
-
value: item[INDICES.VALUE],
|
|
362
|
-
numTrades: item[INDICES.NUM_TRADES],
|
|
363
|
-
itemsPerSector: item[INDICES.ITEMS_PER_SECTOR],
|
|
364
|
-
};
|
|
365
|
-
if (item[INDICES.SECTOR] === "") {
|
|
366
|
-
marketTotal = sectorData;
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
sectors.push(sectorData);
|
|
370
|
-
}
|
|
371
|
-
});
|
|
372
|
-
return createResponse({
|
|
373
|
-
info: INFO,
|
|
374
|
-
charts: createCharts(stockExchange, formattedDate),
|
|
375
|
-
date: formattedDate,
|
|
376
|
-
exchange: stockExchange.toUpperCase(),
|
|
377
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
378
|
-
marketTotal,
|
|
379
|
-
sectors,
|
|
380
|
-
});
|
|
65
|
+
return createResponse(await getMarketOverview(marketOverviewSchema.parse(input)));
|
|
381
66
|
}
|
|
382
67
|
catch (error) {
|
|
383
68
|
return createErrorResponse(error);
|
|
384
69
|
}
|
|
385
70
|
});
|
|
386
|
-
server.registerTool("
|
|
387
|
-
title: "
|
|
388
|
-
description: "
|
|
389
|
-
inputSchema:
|
|
390
|
-
|
|
391
|
-
...dateSchema,
|
|
392
|
-
sector: z
|
|
393
|
-
.string()
|
|
394
|
-
.optional()
|
|
395
|
-
.describe("Get data for specific sector only"),
|
|
396
|
-
},
|
|
397
|
-
}, async ({ stockExchange, year, month, day, sector, }) => {
|
|
71
|
+
server.registerTool("analyze_sector_performance", {
|
|
72
|
+
title: "Analyze sector performance",
|
|
73
|
+
description: "Return aggregated metrics for each sector in a stock exchange, including sector market capitalization, price change percentage, trading volume, traded value, number of trades, and number of companies in the sector. Optionally filter for a single sector.",
|
|
74
|
+
inputSchema: sectorsOverviewSchema.shape,
|
|
75
|
+
}, async (input) => {
|
|
398
76
|
try {
|
|
399
|
-
|
|
400
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
401
|
-
const sectors = marketDataResponse.securities.data
|
|
402
|
-
.filter((item) => item[INDICES.TYPE] === "sector" && item[INDICES.SECTOR] !== "")
|
|
403
|
-
.filter((item) => !sector || item[INDICES.TICKER] === sector)
|
|
404
|
-
.map((item) => ({
|
|
405
|
-
name: item[INDICES.TICKER],
|
|
406
|
-
marketCap: item[INDICES.MARKET_CAP],
|
|
407
|
-
marketCapChangePct: item[INDICES.PRICE_CHANGE_PCT],
|
|
408
|
-
volume: item[INDICES.VOLUME],
|
|
409
|
-
value: item[INDICES.VALUE],
|
|
410
|
-
numTrades: item[INDICES.NUM_TRADES],
|
|
411
|
-
itemsPerSector: item[INDICES.ITEMS_PER_SECTOR],
|
|
412
|
-
}));
|
|
413
|
-
return createResponse({
|
|
414
|
-
info: INFO,
|
|
415
|
-
charts: createCharts(stockExchange, formattedDate),
|
|
416
|
-
date: formattedDate,
|
|
417
|
-
exchange: stockExchange.toUpperCase(),
|
|
418
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
419
|
-
sectors,
|
|
420
|
-
});
|
|
77
|
+
return createResponse(await getSectorsOverview(sectorsOverviewSchema.parse(input)));
|
|
421
78
|
}
|
|
422
79
|
catch (error) {
|
|
423
80
|
return createErrorResponse(error);
|
|
424
81
|
}
|
|
425
82
|
});
|
|
426
|
-
server.registerTool("
|
|
427
|
-
title: "
|
|
428
|
-
description: "
|
|
429
|
-
inputSchema:
|
|
430
|
-
|
|
431
|
-
...dateSchema,
|
|
432
|
-
ticker: z.string().describe("Stock ticker symbol (case-sensitive)"),
|
|
433
|
-
},
|
|
434
|
-
}, async ({ stockExchange, year, month, day, ticker, }) => {
|
|
83
|
+
server.registerTool("get_stock_snapshot", {
|
|
84
|
+
title: "Get stock market snapshot",
|
|
85
|
+
description: "Return detailed trading metrics for a single stock ticker on a specific exchange and trading date, including price open, last sale price, price change percentage, trading volume, traded value, number of trades, and market capitalization.",
|
|
86
|
+
inputSchema: stockDataSchema.shape,
|
|
87
|
+
}, async (input) => {
|
|
435
88
|
try {
|
|
436
|
-
|
|
437
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
438
|
-
const stockData = marketDataResponse.securities.data.find((item) => item[INDICES.TYPE] !== "sector" && item[INDICES.TICKER] === ticker);
|
|
439
|
-
if (!stockData) {
|
|
440
|
-
throw new Error(`Ticker ${ticker} not found on ${stockExchange} for date ${formattedDate}`);
|
|
441
|
-
}
|
|
442
|
-
return createResponse({
|
|
443
|
-
info: INFO,
|
|
444
|
-
charts: createCharts(stockExchange, formattedDate),
|
|
445
|
-
exchange: stockData[INDICES.EXCHANGE],
|
|
446
|
-
country: stockData[INDICES.COUNTRY],
|
|
447
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
448
|
-
sector: stockData[INDICES.SECTOR],
|
|
449
|
-
ticker: stockData[INDICES.TICKER],
|
|
450
|
-
nameEng: stockData[INDICES.NAME_ENG],
|
|
451
|
-
nameOriginal: stockData[INDICES.NAME_ORIGINAL],
|
|
452
|
-
priceOpen: stockData[INDICES.PRICE_OPEN],
|
|
453
|
-
priceLastSale: stockData[INDICES.PRICE_LAST_SALE],
|
|
454
|
-
priceChangePct: stockData[INDICES.PRICE_CHANGE_PCT],
|
|
455
|
-
volume: stockData[INDICES.VOLUME],
|
|
456
|
-
value: stockData[INDICES.VALUE],
|
|
457
|
-
numTrades: stockData[INDICES.NUM_TRADES],
|
|
458
|
-
marketCap: stockData[INDICES.MARKET_CAP],
|
|
459
|
-
listedFrom: stockData[INDICES.LISTED_FROM],
|
|
460
|
-
listedTill: stockData[INDICES.LISTED_TILL],
|
|
461
|
-
});
|
|
89
|
+
return createResponse(await getStockData(stockDataSchema.parse(input)));
|
|
462
90
|
}
|
|
463
91
|
catch (error) {
|
|
464
92
|
return createErrorResponse(error);
|
|
465
93
|
}
|
|
466
94
|
});
|
|
467
|
-
server.registerTool("
|
|
468
|
-
title: "Rank
|
|
469
|
-
description: "
|
|
470
|
-
inputSchema:
|
|
471
|
-
|
|
472
|
-
...dateSchema,
|
|
473
|
-
sortBy: z
|
|
474
|
-
.enum(SORT_FIELDS)
|
|
475
|
-
.describe("Sort by: marketCap, priceChangePct, volume, value, numTrades"),
|
|
476
|
-
order: z
|
|
477
|
-
.enum(SORT_ORDERS)
|
|
478
|
-
.default("desc")
|
|
479
|
-
.describe("Sort order: asc or desc"),
|
|
480
|
-
limit: z
|
|
481
|
-
.number()
|
|
482
|
-
.int()
|
|
483
|
-
.min(1)
|
|
484
|
-
.max(500)
|
|
485
|
-
.default(10)
|
|
486
|
-
.describe("Number of results"),
|
|
487
|
-
sector: z.string().optional().describe("Filter by specific sector"),
|
|
488
|
-
},
|
|
489
|
-
}, async ({ stockExchange, year, month, day, sortBy, order, limit, sector, }) => {
|
|
95
|
+
server.registerTool("rank_exchange_companies", {
|
|
96
|
+
title: "Rank companies by market metric",
|
|
97
|
+
description: "Return companies ranked by a selected market metric on a specific exchange and date. Supported metrics: market capitalization, price change percentage, trading volume, traded value, and number of trades. Results can be limited and optionally filtered by sector.",
|
|
98
|
+
inputSchema: rankStocksSchema.shape,
|
|
99
|
+
}, async (input) => {
|
|
490
100
|
try {
|
|
491
|
-
|
|
492
|
-
const marketDataResponse = await fetchMarketData(stockExchange, formattedDate);
|
|
493
|
-
const stocks = marketDataResponse.securities.data
|
|
494
|
-
.filter((item) => item[INDICES.TYPE] !== "sector" && item[INDICES.SECTOR] !== "")
|
|
495
|
-
.filter((item) => !sector || item[INDICES.SECTOR] === sector)
|
|
496
|
-
.map((item) => ({
|
|
497
|
-
ticker: item[INDICES.TICKER],
|
|
498
|
-
name: item[INDICES.NAME_ENG],
|
|
499
|
-
sector: item[INDICES.SECTOR],
|
|
500
|
-
priceLastSale: item[INDICES.PRICE_LAST_SALE],
|
|
501
|
-
priceChangePct: item[INDICES.PRICE_CHANGE_PCT],
|
|
502
|
-
marketCap: item[INDICES.MARKET_CAP],
|
|
503
|
-
volume: item[INDICES.VOLUME],
|
|
504
|
-
value: item[INDICES.VALUE],
|
|
505
|
-
numTrades: item[INDICES.NUM_TRADES],
|
|
506
|
-
}))
|
|
507
|
-
.sort((a, b) => {
|
|
508
|
-
const aVal = a[sortBy], bVal = b[sortBy];
|
|
509
|
-
return order === "desc" ? bVal - aVal : aVal - bVal;
|
|
510
|
-
})
|
|
511
|
-
.slice(0, limit);
|
|
512
|
-
return createResponse({
|
|
513
|
-
info: INFO,
|
|
514
|
-
charts: createCharts(stockExchange, formattedDate),
|
|
515
|
-
date: formattedDate,
|
|
516
|
-
exchange: stockExchange.toUpperCase(),
|
|
517
|
-
currency: EXCHANGE_INFO[stockExchange].currency,
|
|
518
|
-
sortBy,
|
|
519
|
-
order,
|
|
520
|
-
limit,
|
|
521
|
-
count: stocks.length,
|
|
522
|
-
stocks,
|
|
523
|
-
});
|
|
101
|
+
return createResponse(await rankStocks(rankStocksSchema.parse(input)));
|
|
524
102
|
}
|
|
525
103
|
catch (error) {
|
|
526
104
|
return createErrorResponse(error);
|
|
527
105
|
}
|
|
528
106
|
});
|
|
529
|
-
server.registerTool("
|
|
530
|
-
title: "
|
|
531
|
-
description: "
|
|
532
|
-
inputSchema:
|
|
533
|
-
|
|
534
|
-
.enum(US_EXCHANGES)
|
|
535
|
-
.describe("US exchange: amex, nasdaq, nyse"),
|
|
536
|
-
ticker: z.string().describe("Stock ticker symbol (case-sensitive)"),
|
|
537
|
-
},
|
|
538
|
-
}, async ({ exchange, ticker }) => {
|
|
107
|
+
server.registerTool("get_company_profile_us", {
|
|
108
|
+
title: "Get US company profile",
|
|
109
|
+
description: "Return business description and background information for a US-listed company by ticker. Supported exchanges: NASDAQ, NYSE, and AMEX.",
|
|
110
|
+
inputSchema: companyProfileSchema.shape,
|
|
111
|
+
}, async (input) => {
|
|
539
112
|
try {
|
|
540
|
-
|
|
541
|
-
return createResponse({
|
|
542
|
-
info: INFO,
|
|
543
|
-
charts: createCharts(exchange),
|
|
544
|
-
...securityInfo,
|
|
545
|
-
});
|
|
113
|
+
return createResponse(await getCompanyProfile(companyProfileSchema.parse(input)));
|
|
546
114
|
}
|
|
547
115
|
catch (error) {
|
|
548
116
|
return createErrorResponse(error);
|
package/dist/stdio-server.js
CHANGED
|
@@ -4,7 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
const server = new McpServer({
|
|
6
6
|
name: "finmap-mcp",
|
|
7
|
-
version: "
|
|
7
|
+
version: "3.0.0",
|
|
8
8
|
});
|
|
9
9
|
registerFinmapTools(server);
|
|
10
10
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "finmap-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "MCP server providing financial market data from finmap.org",
|
|
5
5
|
"main": "./dist/stdio-server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -73,15 +73,15 @@
|
|
|
73
73
|
"package": "npm run build:stdio && npm pack --pack-destination dist"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"agents": "^0.7.
|
|
76
|
+
"agents": "^0.7.6",
|
|
77
77
|
"zod": "^4.3.6"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@biomejs/biome": "^2.4.
|
|
81
|
-
"@types/node": "^25.
|
|
80
|
+
"@biomejs/biome": "^2.4.7",
|
|
81
|
+
"@types/node": "^25.5.0",
|
|
82
82
|
"rimraf": "^6.1.3",
|
|
83
83
|
"tsx": "^4.21.0",
|
|
84
84
|
"typescript": "5.9.3",
|
|
85
|
-
"wrangler": "^4.
|
|
85
|
+
"wrangler": "^4.74.0"
|
|
86
86
|
}
|
|
87
87
|
}
|