@houtini/fmp-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/src/index.ts ADDED
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Financial Modeling Prep MCP Server
5
+ *
6
+ * Stdio-based Model Context Protocol server for Financial Modeling Prep API.
7
+ * Provides real-time financial data, stock quotes, company fundamentals, and market insights.
8
+ */
9
+
10
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
12
+ import {
13
+ CallToolRequestSchema,
14
+ ListToolsRequestSchema,
15
+ Tool,
16
+ } from '@modelcontextprotocol/sdk/types.js';
17
+
18
+ const FMP_API_KEY = process.env.FMP_API_KEY;
19
+ const FMP_BASE_URL = 'https://financialmodelingprep.com/stable';
20
+
21
+ if (!FMP_API_KEY) {
22
+ console.error('Error: FMP_API_KEY environment variable is required');
23
+ process.exit(1);
24
+ }
25
+
26
+ /**
27
+ * Helper function to make FMP API requests
28
+ */
29
+ async function fetchFMP(endpoint: string): Promise<any> {
30
+ const url = `${FMP_BASE_URL}${endpoint}${endpoint.includes('?') ? '&' : '?'}apikey=${FMP_API_KEY}`;
31
+
32
+ const response = await fetch(url);
33
+
34
+ if (!response.ok) {
35
+ throw new Error(`FMP API error: ${response.status} ${response.statusText}`);
36
+ }
37
+
38
+ return response.json();
39
+ }
40
+
41
+ /**
42
+ * Define available tools
43
+ */
44
+ const TOOLS: Tool[] = [
45
+ {
46
+ name: 'get_quote',
47
+ description: 'Get real-time stock quote for a symbol (e.g., AAPL, TSLA, MSFT)',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: {
51
+ symbol: {
52
+ type: 'string',
53
+ description: 'Stock ticker symbol (e.g., AAPL)',
54
+ },
55
+ },
56
+ required: ['symbol'],
57
+ },
58
+ },
59
+ {
60
+ name: 'search_symbol',
61
+ description: 'Search for stock symbols by company name or ticker',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ query: {
66
+ type: 'string',
67
+ description: 'Search query (company name or ticker)',
68
+ },
69
+ },
70
+ required: ['query'],
71
+ },
72
+ },
73
+ {
74
+ name: 'get_company_profile',
75
+ description: 'Get detailed company profile information including description, industry, sector, CEO, and more',
76
+ inputSchema: {
77
+ type: 'object',
78
+ properties: {
79
+ symbol: {
80
+ type: 'string',
81
+ description: 'Stock ticker symbol',
82
+ },
83
+ },
84
+ required: ['symbol'],
85
+ },
86
+ },
87
+ {
88
+ name: 'get_income_statement',
89
+ description: 'Get company income statement (annual or quarterly)',
90
+ inputSchema: {
91
+ type: 'object',
92
+ properties: {
93
+ symbol: {
94
+ type: 'string',
95
+ description: 'Stock ticker symbol',
96
+ },
97
+ period: {
98
+ type: 'string',
99
+ description: 'Period type (annual or quarter)',
100
+ enum: ['annual', 'quarter'],
101
+ },
102
+ limit: {
103
+ type: 'number',
104
+ description: 'Number of periods to return (default: 5)',
105
+ },
106
+ },
107
+ required: ['symbol'],
108
+ },
109
+ },
110
+ {
111
+ name: 'get_balance_sheet',
112
+ description: 'Get company balance sheet statement (annual or quarterly)',
113
+ inputSchema: {
114
+ type: 'object',
115
+ properties: {
116
+ symbol: {
117
+ type: 'string',
118
+ description: 'Stock ticker symbol',
119
+ },
120
+ period: {
121
+ type: 'string',
122
+ description: 'Period type (annual or quarter)',
123
+ enum: ['annual', 'quarter'],
124
+ },
125
+ limit: {
126
+ type: 'number',
127
+ description: 'Number of periods to return (default: 5)',
128
+ },
129
+ },
130
+ required: ['symbol'],
131
+ },
132
+ },
133
+ {
134
+ name: 'get_cash_flow',
135
+ description: 'Get company cash flow statement (annual or quarterly)',
136
+ inputSchema: {
137
+ type: 'object',
138
+ properties: {
139
+ symbol: {
140
+ type: 'string',
141
+ description: 'Stock ticker symbol',
142
+ },
143
+ period: {
144
+ type: 'string',
145
+ description: 'Period type (annual or quarter)',
146
+ enum: ['annual', 'quarter'],
147
+ },
148
+ limit: {
149
+ type: 'number',
150
+ description: 'Number of periods to return (default: 5)',
151
+ },
152
+ },
153
+ required: ['symbol'],
154
+ },
155
+ },
156
+ {
157
+ name: 'get_stock_news',
158
+ description: 'Get latest news articles for a stock symbol',
159
+ inputSchema: {
160
+ type: 'object',
161
+ properties: {
162
+ symbol: {
163
+ type: 'string',
164
+ description: 'Stock ticker symbol',
165
+ },
166
+ limit: {
167
+ type: 'number',
168
+ description: 'Number of articles to return (default: 10)',
169
+ },
170
+ },
171
+ required: ['symbol'],
172
+ },
173
+ },
174
+ ];
175
+
176
+ /**
177
+ * Create and configure the MCP server
178
+ */
179
+ const server = new Server(
180
+ {
181
+ name: 'fmp-mcp-server',
182
+ version: '1.0.0',
183
+ },
184
+ {
185
+ capabilities: {
186
+ tools: {},
187
+ },
188
+ }
189
+ );
190
+
191
+ /**
192
+ * Handler for listing available tools
193
+ */
194
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
195
+ return {
196
+ tools: TOOLS,
197
+ };
198
+ });
199
+
200
+ /**
201
+ * Handler for tool execution
202
+ */
203
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
204
+ const { name, arguments: args } = request.params;
205
+
206
+ try {
207
+ switch (name) {
208
+ case 'get_quote': {
209
+ const { symbol } = args as { symbol: string };
210
+ const data = await fetchFMP(`/quote?symbol=${symbol.toUpperCase()}`);
211
+ return {
212
+ content: [
213
+ {
214
+ type: 'text',
215
+ text: JSON.stringify(data, null, 2),
216
+ },
217
+ ],
218
+ };
219
+ }
220
+
221
+ case 'search_symbol': {
222
+ const { query } = args as { query: string };
223
+ const data = await fetchFMP(`/search-symbol?query=${encodeURIComponent(query)}&limit=10`);
224
+ return {
225
+ content: [
226
+ {
227
+ type: 'text',
228
+ text: JSON.stringify(data, null, 2),
229
+ },
230
+ ],
231
+ };
232
+ }
233
+
234
+ case 'get_company_profile': {
235
+ const { symbol } = args as { symbol: string };
236
+ const data = await fetchFMP(`/profile?symbol=${symbol.toUpperCase()}`);
237
+ return {
238
+ content: [
239
+ {
240
+ type: 'text',
241
+ text: JSON.stringify(data, null, 2),
242
+ },
243
+ ],
244
+ };
245
+ }
246
+
247
+ case 'get_income_statement': {
248
+ const { symbol, period = 'annual', limit = 5 } = args as {
249
+ symbol: string;
250
+ period?: string;
251
+ limit?: number;
252
+ };
253
+ const data = await fetchFMP(`/income-statement?symbol=${symbol.toUpperCase()}&period=${period}&limit=${limit}`);
254
+ return {
255
+ content: [
256
+ {
257
+ type: 'text',
258
+ text: JSON.stringify(data, null, 2),
259
+ },
260
+ ],
261
+ };
262
+ }
263
+
264
+ case 'get_balance_sheet': {
265
+ const { symbol, period = 'annual', limit = 5 } = args as {
266
+ symbol: string;
267
+ period?: string;
268
+ limit?: number;
269
+ };
270
+ const data = await fetchFMP(`/balance-sheet-statement?symbol=${symbol.toUpperCase()}&period=${period}&limit=${limit}`);
271
+ return {
272
+ content: [
273
+ {
274
+ type: 'text',
275
+ text: JSON.stringify(data, null, 2),
276
+ },
277
+ ],
278
+ };
279
+ }
280
+
281
+ case 'get_cash_flow': {
282
+ const { symbol, period = 'annual', limit = 5 } = args as {
283
+ symbol: string;
284
+ period?: string;
285
+ limit?: number;
286
+ };
287
+ const data = await fetchFMP(`/cash-flow-statement?symbol=${symbol.toUpperCase()}&period=${period}&limit=${limit}`);
288
+ return {
289
+ content: [
290
+ {
291
+ type: 'text',
292
+ text: JSON.stringify(data, null, 2),
293
+ },
294
+ ],
295
+ };
296
+ }
297
+
298
+ case 'get_stock_news': {
299
+ const { symbol, limit = 10 } = args as { symbol: string; limit?: number };
300
+ const data = await fetchFMP(`/stock_news?tickers=${symbol.toUpperCase()}&limit=${limit}`);
301
+ return {
302
+ content: [
303
+ {
304
+ type: 'text',
305
+ text: JSON.stringify(data, null, 2),
306
+ },
307
+ ],
308
+ };
309
+ }
310
+
311
+ default:
312
+ throw new Error(`Unknown tool: ${name}`);
313
+ }
314
+ } catch (error) {
315
+ const errorMessage = error instanceof Error ? error.message : String(error);
316
+ return {
317
+ content: [
318
+ {
319
+ type: 'text',
320
+ text: `Error: ${errorMessage}`,
321
+ },
322
+ ],
323
+ isError: true,
324
+ };
325
+ }
326
+ });
327
+
328
+ /**
329
+ * Start the server
330
+ */
331
+ async function main() {
332
+ const transport = new StdioServerTransport();
333
+ await server.connect(transport);
334
+
335
+ // Log to stderr so it doesn't interfere with stdio protocol
336
+ console.error('FMP MCP Server running on stdio');
337
+ }
338
+
339
+ main().catch((error) => {
340
+ console.error('Fatal error:', error);
341
+ process.exit(1);
342
+ });
package/test-server.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Simple test script to verify the FMP MCP server works
5
+ * Run with: node test-server.js
6
+ */
7
+
8
+ import { spawn } from 'child_process';
9
+
10
+ const server = spawn('node', ['./build/index.js'], {
11
+ env: {
12
+ ...process.env,
13
+ FMP_API_KEY: '4bqSxSXvO3TBUszm6VqRMKszhq5M0SmF'
14
+ },
15
+ stdio: ['pipe', 'pipe', 'inherit']
16
+ });
17
+
18
+ server.stdout.on('data', (data) => {
19
+ console.log('STDOUT:', data.toString());
20
+ });
21
+
22
+ server.on('error', (error) => {
23
+ console.error('Failed to start server:', error);
24
+ });
25
+
26
+ server.on('exit', (code) => {
27
+ console.log('Server exited with code:', code);
28
+ });
29
+
30
+ // Give it 2 seconds to start
31
+ setTimeout(() => {
32
+ console.log('Server should be running now. Check stderr for "FMP MCP Server running on stdio"');
33
+ process.exit(0);
34
+ }, 2000);
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "./build",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "build"]
19
+ }