fmp-ai-tools 0.2.0-beta.2 → 0.2.0-beta.4

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 CHANGED
@@ -302,12 +302,25 @@ Each tool accepts specific parameters. Here are some common ones:
302
302
 
303
303
  ## Error Handling
304
304
 
305
- The tools handle API errors gracefully and will return informative error messages if:
305
+ When a request fails, a tool returns a structured JSON error (instead of `null`) so the model can explain *why* to the user:
306
306
 
307
- - The API key is invalid or missing
308
- - The requested data is not available
309
- - Rate limits are exceeded
310
- - Invalid parameters are provided
307
+ ```json
308
+ { "error": true, "type": "plan-restricted", "message": "This endpoint is not available on your current FMP plan. (403: ...)", "status": 403 }
309
+ ```
310
+
311
+ The `type` field classifies the failure so your agent can react appropriately:
312
+
313
+ | `type` | Meaning |
314
+ | ----------------- | ------------------------------------------------------------ |
315
+ | `plan-restricted` | Endpoint isn't included in your FMP subscription (402/403) |
316
+ | `rate-limit` | FMP quota / rate limit reached (429) |
317
+ | `auth` | Invalid or missing `FMP_API_KEY` (401) |
318
+ | `not-found` | Resource does not exist (404) |
319
+ | `bad-request` | Invalid parameters (400) |
320
+ | `network` | No response from FMP (timeout / offline) |
321
+ | `unknown` | Anything else |
322
+
323
+ This is especially useful on lower FMP tiers: an agent calling an endpoint your plan doesn't cover now gets a clear `plan-restricted` message it can relay, rather than empty data. (Direct `fmp-node-api` callers get the same classification via `response.errorType`.)
311
324
 
312
325
  ## Testing Tools
313
326
 
@@ -177,6 +177,36 @@ async function logApiExecutionWithTiming(toolName, input, executeFn) {
177
177
  }
178
178
  }
179
179
 
180
+ // src/utils/format-response.ts
181
+ function toToolResponse(res) {
182
+ if (res.success === false) {
183
+ return JSON.stringify({
184
+ error: true,
185
+ type: res.errorType ?? "unknown",
186
+ message: res.error ?? "The request failed.",
187
+ status: res.status
188
+ });
189
+ }
190
+ if (isEmpty(res.data)) {
191
+ return JSON.stringify({ data: [], note: "No data was returned for this request." });
192
+ }
193
+ return JSON.stringify(res.data, null, 2);
194
+ }
195
+ function toToolError(error) {
196
+ const message = error instanceof Error ? error.message : String(error);
197
+ let type = "unknown";
198
+ if (/api[\s_-]?key/i.test(message)) {
199
+ type = "auth";
200
+ }
201
+ return JSON.stringify({ error: true, type, message, status: 0 });
202
+ }
203
+ function isEmpty(data) {
204
+ if (data == null) return true;
205
+ if (Array.isArray(data)) return data.length === 0;
206
+ if (typeof data === "object") return Object.keys(data).length === 0;
207
+ return false;
208
+ }
209
+
180
210
  // src/utils/openai-tool-wrapper.ts
181
211
  function createOpenAITool(config) {
182
212
  const { name, description, inputSchema, execute } = config;
@@ -185,8 +215,12 @@ function createOpenAITool(config) {
185
215
  description,
186
216
  parameters: inputSchema,
187
217
  execute: async (input) => {
188
- const args = inputSchema.parse(input);
189
- return await logApiExecutionWithTiming(name, args, () => execute(args));
218
+ try {
219
+ const args = inputSchema.parse(input);
220
+ return await logApiExecutionWithTiming(name, args, () => execute(args));
221
+ } catch (error) {
222
+ return toToolError(error);
223
+ }
190
224
  }
191
225
  });
192
226
  }
@@ -204,7 +238,7 @@ var getCompanyProfile = createOpenAITool({
204
238
  execute: async ({ symbol }) => {
205
239
  const fmp = getFMPClient();
206
240
  const companyProfile = await fmp.company.getCompanyProfile(symbol);
207
- return JSON.stringify(companyProfile.data, null, 2);
241
+ return toToolResponse(companyProfile);
208
242
  }
209
243
  });
210
244
  var getCompanySharesFloat = createOpenAITool({
@@ -216,7 +250,7 @@ var getCompanySharesFloat = createOpenAITool({
216
250
  execute: async ({ symbol }) => {
217
251
  const fmp = getFMPClient();
218
252
  const companySharesFloat = await fmp.company.getSharesFloat(symbol);
219
- return JSON.stringify(companySharesFloat.data, null, 2);
253
+ return toToolResponse(companySharesFloat);
220
254
  }
221
255
  });
222
256
  var getCompanyExecutiveCompensation = createOpenAITool({
@@ -228,7 +262,7 @@ var getCompanyExecutiveCompensation = createOpenAITool({
228
262
  execute: async ({ symbol }) => {
229
263
  const fmp = getFMPClient();
230
264
  const companyExecutiveCompensation = await fmp.company.getExecutiveCompensation(symbol);
231
- return JSON.stringify(companyExecutiveCompensation.data, null, 2);
265
+ return toToolResponse(companyExecutiveCompensation);
232
266
  }
233
267
  });
234
268
  var calendarInputSchema = zod.z.object({
@@ -245,7 +279,7 @@ var getEarningsCalendar = createOpenAITool({
245
279
  from: from ?? void 0,
246
280
  to: to ?? void 0
247
281
  });
248
- return JSON.stringify(earningsCalendar.data, null, 2);
282
+ return toToolResponse(earningsCalendar);
249
283
  }
250
284
  });
251
285
  var getEconomicCalendar = createOpenAITool({
@@ -258,7 +292,7 @@ var getEconomicCalendar = createOpenAITool({
258
292
  from: from ?? void 0,
259
293
  to: to ?? void 0
260
294
  });
261
- return JSON.stringify(economicCalendar.data, null, 2);
295
+ return toToolResponse(economicCalendar);
262
296
  }
263
297
  });
264
298
  var treasuryRatesInputSchema = zod.z.object({
@@ -305,7 +339,7 @@ var getTreasuryRates = createOpenAITool({
305
339
  from: from ?? void 0,
306
340
  to: to ?? void 0
307
341
  });
308
- return JSON.stringify(treasuryRates.data, null, 2);
342
+ return toToolResponse(treasuryRates);
309
343
  }
310
344
  });
311
345
  var getEconomicIndicators = createOpenAITool({
@@ -319,7 +353,7 @@ var getEconomicIndicators = createOpenAITool({
319
353
  from: from ?? void 0,
320
354
  to: to ?? void 0
321
355
  });
322
- return JSON.stringify(economicIndicators.data, null, 2);
356
+ return toToolResponse(economicIndicators);
323
357
  }
324
358
  });
325
359
  var etfHoldingsInputSchema = zod.z.object({
@@ -340,7 +374,7 @@ var getETFHoldings = createOpenAITool({
340
374
  params.date = date;
341
375
  }
342
376
  const etfHoldings = await fmp.etf.getHoldings(params);
343
- return JSON.stringify(etfHoldings.data, null, 2);
377
+ return toToolResponse(etfHoldings);
344
378
  }
345
379
  });
346
380
  var getETFProfile = createOpenAITool({
@@ -350,7 +384,7 @@ var getETFProfile = createOpenAITool({
350
384
  execute: async ({ symbol }) => {
351
385
  const fmp = getFMPClient();
352
386
  const etfProfile = await fmp.etf.getProfile(symbol);
353
- return JSON.stringify(etfProfile.data, null, 2);
387
+ return toToolResponse(etfProfile);
354
388
  }
355
389
  });
356
390
  var getBalanceSheet = createOpenAITool({
@@ -368,7 +402,7 @@ var getBalanceSheet = createOpenAITool({
368
402
  period,
369
403
  limit: Number(limit)
370
404
  });
371
- return JSON.stringify(balanceSheet.data, null, 2);
405
+ return toToolResponse(balanceSheet);
372
406
  }
373
407
  });
374
408
  var getIncomeStatement = createOpenAITool({
@@ -386,7 +420,7 @@ var getIncomeStatement = createOpenAITool({
386
420
  period,
387
421
  limit: Number(limit)
388
422
  });
389
- return JSON.stringify(incomeStatement.data, null, 2);
423
+ return toToolResponse(incomeStatement);
390
424
  }
391
425
  });
392
426
  var getCashFlowStatement = createOpenAITool({
@@ -404,7 +438,7 @@ var getCashFlowStatement = createOpenAITool({
404
438
  period,
405
439
  limit: Number(limit)
406
440
  });
407
- return JSON.stringify(cashFlowStatement.data, null, 2);
441
+ return toToolResponse(cashFlowStatement);
408
442
  }
409
443
  });
410
444
  var getKeyMetrics = createOpenAITool({
@@ -418,7 +452,7 @@ var getKeyMetrics = createOpenAITool({
418
452
  execute: async ({ symbol, period, limit }) => {
419
453
  const fmp = getFMPClient();
420
454
  const keyMetrics = await fmp.financial.getKeyMetrics({ symbol, period, limit: Number(limit) });
421
- return JSON.stringify(keyMetrics.data, null, 2);
455
+ return toToolResponse(keyMetrics);
422
456
  }
423
457
  });
424
458
  var getFinancialRatios = createOpenAITool({
@@ -436,7 +470,7 @@ var getFinancialRatios = createOpenAITool({
436
470
  period,
437
471
  limit: Number(limit)
438
472
  });
439
- return JSON.stringify(financialRatios.data, null, 2);
473
+ return toToolResponse(financialRatios);
440
474
  }
441
475
  });
442
476
  var getEnterpriseValue = createOpenAITool({
@@ -454,7 +488,7 @@ var getEnterpriseValue = createOpenAITool({
454
488
  period,
455
489
  limit: Number(limit)
456
490
  });
457
- return JSON.stringify(enterpriseValue.data, null, 2);
491
+ return toToolResponse(enterpriseValue);
458
492
  }
459
493
  });
460
494
  var getCashflowGrowth = createOpenAITool({
@@ -472,7 +506,7 @@ var getCashflowGrowth = createOpenAITool({
472
506
  period,
473
507
  limit: Number(limit)
474
508
  });
475
- return JSON.stringify(cashflowGrowth.data, null, 2);
509
+ return toToolResponse(cashflowGrowth);
476
510
  }
477
511
  });
478
512
  var getIncomeGrowth = createOpenAITool({
@@ -490,7 +524,7 @@ var getIncomeGrowth = createOpenAITool({
490
524
  period,
491
525
  limit: Number(limit)
492
526
  });
493
- return JSON.stringify(incomeGrowth.data, null, 2);
527
+ return toToolResponse(incomeGrowth);
494
528
  }
495
529
  });
496
530
  var getBalanceSheetGrowth = createOpenAITool({
@@ -508,7 +542,7 @@ var getBalanceSheetGrowth = createOpenAITool({
508
542
  period,
509
543
  limit: Number(limit)
510
544
  });
511
- return JSON.stringify(balanceSheetGrowth.data, null, 2);
545
+ return toToolResponse(balanceSheetGrowth);
512
546
  }
513
547
  });
514
548
  var getFinancialGrowth = createOpenAITool({
@@ -526,7 +560,7 @@ var getFinancialGrowth = createOpenAITool({
526
560
  period,
527
561
  limit: Number(limit)
528
562
  });
529
- return JSON.stringify(financialGrowth.data, null, 2);
563
+ return toToolResponse(financialGrowth);
530
564
  }
531
565
  });
532
566
  var getEarningsHistorical = createOpenAITool({
@@ -542,7 +576,7 @@ var getEarningsHistorical = createOpenAITool({
542
576
  symbol,
543
577
  limit: Number(limit)
544
578
  });
545
- return JSON.stringify(earningsHistorical.data, null, 2);
579
+ return toToolResponse(earningsHistorical);
546
580
  }
547
581
  });
548
582
  var insiderTradingInputSchema = zod.z.object({
@@ -556,7 +590,7 @@ var getInsiderTrading = createOpenAITool({
556
590
  execute: async ({ symbol, page }) => {
557
591
  const fmp = getFMPClient();
558
592
  const insiderTrading = await fmp.insider.getInsiderTradesBySymbol(symbol, page);
559
- return JSON.stringify(insiderTrading.data, null, 2);
593
+ return toToolResponse(insiderTrading);
560
594
  }
561
595
  });
562
596
  var institutionalHoldersInputSchema = zod.z.object({
@@ -569,7 +603,7 @@ var getInstitutionalHolders = createOpenAITool({
569
603
  execute: async ({ symbol }) => {
570
604
  const fmp = getFMPClient();
571
605
  const institutionalHolders = await fmp.institutional.getInstitutionalHolders({ symbol });
572
- return JSON.stringify(institutionalHolders.data, null, 2);
606
+ return toToolResponse(institutionalHolders);
573
607
  }
574
608
  });
575
609
  var emptyInputSchema = zod.z.object({});
@@ -580,7 +614,7 @@ var getMarketPerformance = createOpenAITool({
580
614
  execute: async () => {
581
615
  const fmp = getFMPClient();
582
616
  const marketPerformance = await fmp.market.getMarketPerformance();
583
- return JSON.stringify(marketPerformance.data, null, 2);
617
+ return toToolResponse(marketPerformance);
584
618
  }
585
619
  });
586
620
  var getSectorPerformance = createOpenAITool({
@@ -590,7 +624,7 @@ var getSectorPerformance = createOpenAITool({
590
624
  execute: async () => {
591
625
  const fmp = getFMPClient();
592
626
  const sectorPerformance = await fmp.market.getSectorPerformance();
593
- return JSON.stringify(sectorPerformance.data, null, 2);
627
+ return toToolResponse(sectorPerformance);
594
628
  }
595
629
  });
596
630
  var getGainers = createOpenAITool({
@@ -600,7 +634,7 @@ var getGainers = createOpenAITool({
600
634
  execute: async () => {
601
635
  const fmp = getFMPClient();
602
636
  const gainers = await fmp.market.getGainers();
603
- return JSON.stringify(gainers.data, null, 2);
637
+ return toToolResponse(gainers);
604
638
  }
605
639
  });
606
640
  var getLosers = createOpenAITool({
@@ -610,7 +644,7 @@ var getLosers = createOpenAITool({
610
644
  execute: async () => {
611
645
  const fmp = getFMPClient();
612
646
  const losers = await fmp.market.getLosers();
613
- return JSON.stringify(losers.data, null, 2);
647
+ return toToolResponse(losers);
614
648
  }
615
649
  });
616
650
  var getMostActive = createOpenAITool({
@@ -620,7 +654,7 @@ var getMostActive = createOpenAITool({
620
654
  execute: async () => {
621
655
  const fmp = getFMPClient();
622
656
  const mostActive = await fmp.market.getMostActive();
623
- return JSON.stringify(mostActive.data, null, 2);
657
+ return toToolResponse(mostActive);
624
658
  }
625
659
  });
626
660
  var stockQuoteInputSchema = zod.z.object({
@@ -633,7 +667,7 @@ var getStockQuote = createOpenAITool({
633
667
  execute: async ({ symbol }) => {
634
668
  const fmp = getFMPClient();
635
669
  const stockQuote = await fmp.quote.getQuote(symbol);
636
- return JSON.stringify(stockQuote.data, null, 2);
670
+ return toToolResponse(stockQuote);
637
671
  }
638
672
  });
639
673
  var symbolInputSchema = zod.z.object({
@@ -652,7 +686,7 @@ var getSenateTrading = createOpenAITool({
652
686
  execute: async ({ symbol }) => {
653
687
  const fmp = getFMPClient();
654
688
  const senateTrading = await fmp.senateHouse.getSenateTrading({ symbol });
655
- return JSON.stringify(senateTrading.data, null, 2);
689
+ return toToolResponse(senateTrading);
656
690
  }
657
691
  });
658
692
  var getHouseTrading = createOpenAITool({
@@ -662,7 +696,7 @@ var getHouseTrading = createOpenAITool({
662
696
  execute: async ({ symbol }) => {
663
697
  const fmp = getFMPClient();
664
698
  const houseTrading = await fmp.senateHouse.getHouseTrading({ symbol });
665
- return JSON.stringify(houseTrading.data, null, 2);
699
+ return toToolResponse(houseTrading);
666
700
  }
667
701
  });
668
702
  var getSenateTradingByName = createOpenAITool({
@@ -672,7 +706,7 @@ var getSenateTradingByName = createOpenAITool({
672
706
  execute: async ({ name }) => {
673
707
  const fmp = getFMPClient();
674
708
  const senateTradingByName = await fmp.senateHouse.getSenateTradingByName({ name });
675
- return JSON.stringify(senateTradingByName.data, null, 2);
709
+ return toToolResponse(senateTradingByName);
676
710
  }
677
711
  });
678
712
  var getHouseTradingByName = createOpenAITool({
@@ -682,7 +716,7 @@ var getHouseTradingByName = createOpenAITool({
682
716
  execute: async ({ name }) => {
683
717
  const fmp = getFMPClient();
684
718
  const houseTradingByName = await fmp.senateHouse.getHouseTradingByName({ name });
685
- return JSON.stringify(houseTradingByName.data, null, 2);
719
+ return toToolResponse(houseTradingByName);
686
720
  }
687
721
  });
688
722
  var getSenateTradingRSSFeed = createOpenAITool({
@@ -692,7 +726,7 @@ var getSenateTradingRSSFeed = createOpenAITool({
692
726
  execute: async ({ page }) => {
693
727
  const fmp = getFMPClient();
694
728
  const senateTradingRSSFeed = await fmp.senateHouse.getSenateTradingRSSFeed({ page });
695
- return JSON.stringify(senateTradingRSSFeed.data, null, 2);
729
+ return toToolResponse(senateTradingRSSFeed);
696
730
  }
697
731
  });
698
732
  var getHouseTradingRSSFeed = createOpenAITool({
@@ -702,7 +736,7 @@ var getHouseTradingRSSFeed = createOpenAITool({
702
736
  execute: async ({ page }) => {
703
737
  const fmp = getFMPClient();
704
738
  const houseTradingRSSFeed = await fmp.senateHouse.getHouseTradingRSSFeed({ page });
705
- return JSON.stringify(houseTradingRSSFeed.data, null, 2);
739
+ return toToolResponse(houseTradingRSSFeed);
706
740
  }
707
741
  });
708
742
  var symbolInputSchema2 = zod.z.object({
@@ -715,7 +749,7 @@ var getMarketCap = createOpenAITool({
715
749
  execute: async ({ symbol }) => {
716
750
  const fmp = getFMPClient();
717
751
  const marketCap = await fmp.stock.getMarketCap(symbol);
718
- return JSON.stringify(marketCap.data, null, 2);
752
+ return toToolResponse(marketCap);
719
753
  }
720
754
  });
721
755
  var getStockSplits = createOpenAITool({
@@ -725,7 +759,7 @@ var getStockSplits = createOpenAITool({
725
759
  execute: async ({ symbol }) => {
726
760
  const fmp = getFMPClient();
727
761
  const stockSplits = await fmp.stock.getStockSplits(symbol);
728
- return JSON.stringify(stockSplits.data, null, 2);
762
+ return toToolResponse(stockSplits);
729
763
  }
730
764
  });
731
765
  var getDividendHistory = createOpenAITool({
@@ -735,7 +769,7 @@ var getDividendHistory = createOpenAITool({
735
769
  execute: async ({ symbol }) => {
736
770
  const fmp = getFMPClient();
737
771
  const dividendHistory = await fmp.stock.getDividendHistory(symbol);
738
- return JSON.stringify(dividendHistory.data, null, 2);
772
+ return toToolResponse(dividendHistory);
739
773
  }
740
774
  });
741
775