Skip to content

Commit a7ab4c6

Browse files
APIbaseclaude
andcommitted
feat: integrate OpenFIGI — 3 tools (UC-357) — 471 TOOLS, 144 PROVIDERS
New adapter: src/adapters/figi/ — Bloomberg Financial Instrument Global Identifier Tools: figi.map (ISIN/CUSIP/SEDOL/ticker → FIGI), figi.search, figi.filter 300M+ instruments, 45K+ exchanges, ISO 18774. POST-based API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8bcc30d commit a7ab4c6

File tree

19 files changed

+721
-39
lines changed

19 files changed

+721
-39
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# APIbase.pro — The API Hub for AI Agents
22

3-
> One MCP endpoint. 468 tools. 143 providers. Pay per call with x402 (USDC on Base) or MPP (USDC on Tempo).
3+
> One MCP endpoint. 471 tools. 144 providers. Pay per call with x402 (USDC on Base) or MPP (USDC on Tempo).
44
55
**[Live Platform](https://apibase.pro)** | **[Tool Catalog](https://apibase.pro/api/v1/tools)** | **[MCP Endpoint](https://apibase.pro/mcp)** | **[Frameworks](https://apibase.pro/frameworks)** | **[Dashboard](https://apibase.pro/dashboard)**
66

@@ -27,7 +27,7 @@ https://github.com/user-attachments/assets/9e598d61-b2d0-486c-bd34-f0cb0354d09c
2727

2828
## What is APIbase?
2929

30-
Production MCP server that gives AI agents access to 468 real-world API tools through a single endpoint. Agents connect once to `https://apibase.pro/mcp` and can search flights, get stock quotes, translate text, check weather alerts, generate images, send emails, look up holidays, shorten URLs, detect fires by satellite, decode VINs, look up chemical compounds, find EV chargers, batch multiple calls, track usage analytics — and 250+ more tools across 30+ categories.
30+
Production MCP server that gives AI agents access to 471 real-world API tools through a single endpoint. Agents connect once to `https://apibase.pro/mcp` and can search flights, get stock quotes, translate text, check weather alerts, generate images, send emails, look up holidays, shorten URLs, detect fires by satellite, decode VINs, look up chemical compounds, find EV chargers, batch multiple calls, track usage analytics — and 250+ more tools across 30+ categories.
3131

3232
**Built for AI agents, not humans.** Auto-registration, zero setup, pay-per-call via x402 USDC micropayments on Base or MPP (Machine Payments Protocol) on Tempo.
3333

@@ -91,7 +91,7 @@ curl -X POST https://apibase.pro/api/v1/tools/finnhub.quote/call \
9191

9292
---
9393

94-
## Tool Categories (468 tools, 143 providers)
94+
## Tool Categories (471 tools, 144 providers)
9595

9696
| Category | Tools | Providers | Examples |
9797
|----------|-------|-----------|----------|
@@ -413,13 +413,13 @@ GET /.well-known/x402-payment.json → Payment config (network, facilitator
413413
GET /.well-known/openapi.json → OpenAPI 3.1 spec (with x-payment-info)
414414
GET /ai.txt → Plain text AI agent discovery
415415
GET /llms.txt → Concise LLM context
416-
GET /api/v1/tools → Live tool catalog (all 468 tools, JSON schemas)
416+
GET /api/v1/tools → Live tool catalog (all 471 tools, JSON schemas)
417417
GET /health/ready → System health check
418418
POST /mcp prompts/get discover_tools → Browse tools by category or task (progressive disclosure)
419419
GET /frameworks → Integration guides for 9 frameworks
420420
```
421421

422-
**Progressive disclosure:** Instead of loading all 468 tool schemas into context, agents can call the `discover_tools` prompt to find relevant tools first:
422+
**Progressive disclosure:** Instead of loading all 471 tool schemas into context, agents can call the `discover_tools` prompt to find relevant tools first:
423423
- `discover_tools` (no args) → 21 categories with tool counts
424424
- `discover_tools category="travel"` → 17 travel tools
425425
- `discover_tools task="check earthquake near Tokyo"` → matching tools ranked by relevance

config/tool_provider_config.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2832,6 +2832,23 @@ tools:
28322832
price_usd: "0.002"
28332833
cache_ttl: 86400
28342834

2835+
# --- UC-357: OpenFIGI (Bloomberg Financial Identifiers) ---
2836+
- tool_id: figi.map
2837+
name: Map Financial Identifier to FIGI
2838+
provider: figi
2839+
price_usd: "0.001"
2840+
cache_ttl: 86400
2841+
- tool_id: figi.search
2842+
name: Search Financial Instruments
2843+
provider: figi
2844+
price_usd: "0.001"
2845+
cache_ttl: 86400
2846+
- tool_id: figi.filter
2847+
name: Filter Financial Instruments
2848+
provider: figi
2849+
price_usd: "0.001"
2850+
cache_ttl: 86400
2851+
28352852
- tool_id: platform.call_batch
28362853
name: Batch Tool Calls
28372854
provider: platform

src/adapters/figi/index.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { BaseAdapter } from '../base.adapter';
2+
import {
3+
type ProviderRequest,
4+
type ProviderRawResponse,
5+
ProviderErrorCode,
6+
} from '../../types/provider';
7+
import type { FigiInstrument, FigiMapOutput, FigiSearchOutput, FigiFilterOutput } from './types';
8+
9+
const FIGI_BASE = 'https://api.openfigi.com/v3';
10+
11+
/**
12+
* OpenFIGI adapter (UC-357).
13+
*
14+
* Bloomberg Financial Instrument Global Identifier — ISO 18774.
15+
* 300M+ instruments, 45K+ exchanges. POST-based API.
16+
* Optional API key increases rate limits.
17+
*/
18+
export class FigiAdapter extends BaseAdapter {
19+
private readonly apiKey: string;
20+
21+
constructor(apiKey: string) {
22+
super({ provider: 'figi', baseUrl: FIGI_BASE });
23+
this.apiKey = apiKey;
24+
}
25+
26+
protected buildRequest(req: ProviderRequest): {
27+
url: string;
28+
method: string;
29+
headers: Record<string, string>;
30+
body?: string;
31+
} {
32+
const params = req.params as Record<string, unknown>;
33+
const headers: Record<string, string> = {
34+
'Content-Type': 'application/json',
35+
Accept: 'application/json',
36+
};
37+
if (this.apiKey) {
38+
headers['X-OPENFIGI-APIKEY'] = this.apiKey;
39+
}
40+
41+
switch (req.toolId) {
42+
case 'figi.map': {
43+
const mapping: Record<string, unknown> = {
44+
idType: String(params.id_type),
45+
idValue: String(params.id_value),
46+
};
47+
if (params.exchange_code) mapping.exchCode = String(params.exchange_code);
48+
return {
49+
url: `${FIGI_BASE}/mapping`,
50+
method: 'POST',
51+
headers,
52+
body: JSON.stringify([mapping]),
53+
};
54+
}
55+
56+
case 'figi.search': {
57+
const search: Record<string, unknown> = {
58+
query: String(params.query),
59+
};
60+
if (params.exchange_code) search.exchCode = String(params.exchange_code);
61+
if (params.security_type) search.securityType = String(params.security_type);
62+
return {
63+
url: `${FIGI_BASE}/search`,
64+
method: 'POST',
65+
headers,
66+
body: JSON.stringify(search),
67+
};
68+
}
69+
70+
case 'figi.filter': {
71+
const filter: Record<string, unknown> = {};
72+
if (params.exchange_code) filter.exchCode = String(params.exchange_code);
73+
if (params.market_sector) filter.marketSector = String(params.market_sector);
74+
if (params.security_type) filter.securityType = String(params.security_type);
75+
return {
76+
url: `${FIGI_BASE}/filter`,
77+
method: 'POST',
78+
headers,
79+
body: JSON.stringify(filter),
80+
};
81+
}
82+
83+
default:
84+
throw {
85+
code: ProviderErrorCode.INVALID_RESPONSE,
86+
httpStatus: 502,
87+
message: `Unsupported tool: ${req.toolId}`,
88+
provider: this.provider,
89+
toolId: req.toolId,
90+
durationMs: 0,
91+
};
92+
}
93+
}
94+
95+
protected parseResponse(raw: ProviderRawResponse, req: ProviderRequest): unknown {
96+
const body = raw.body;
97+
98+
switch (req.toolId) {
99+
case 'figi.map':
100+
return this.parseMap(body);
101+
case 'figi.search':
102+
return this.parseSearch(body as Record<string, unknown>);
103+
case 'figi.filter':
104+
return this.parseFilter(body as Record<string, unknown>);
105+
default:
106+
return body;
107+
}
108+
}
109+
110+
private parseMap(body: unknown): FigiMapOutput {
111+
// /mapping returns array of { data: [...] } or { error: "..." }
112+
const results = Array.isArray(body) ? body : [];
113+
const instruments: FigiInstrument[] = [];
114+
115+
for (const r of results) {
116+
const rec = r as Record<string, unknown>;
117+
const data = (rec.data ?? []) as Record<string, unknown>[];
118+
for (const item of data.slice(0, 10)) {
119+
instruments.push(this.toInstrument(item));
120+
}
121+
}
122+
123+
return { results: instruments, total: instruments.length };
124+
}
125+
126+
private parseSearch(body: Record<string, unknown>): FigiSearchOutput {
127+
const data = (body.data ?? []) as Record<string, unknown>[];
128+
return {
129+
results: data.slice(0, 20).map((item) => this.toInstrument(item)),
130+
total: data.length,
131+
};
132+
}
133+
134+
private parseFilter(body: Record<string, unknown>): FigiFilterOutput {
135+
const data = (body.data ?? []) as Record<string, unknown>[];
136+
return {
137+
results: data.slice(0, 20).map((item) => this.toInstrument(item)),
138+
total: Number(body.total ?? data.length),
139+
};
140+
}
141+
142+
private toInstrument(item: Record<string, unknown>): FigiInstrument {
143+
return {
144+
figi: String(item.figi ?? ''),
145+
name: String(item.name ?? ''),
146+
ticker: String(item.ticker ?? ''),
147+
exchange_code: String(item.exchCode ?? ''),
148+
market_sector: String(item.marketSector ?? ''),
149+
security_type: String(item.securityType ?? item.securityType2 ?? ''),
150+
composite_figi: String(item.compositeFIGI ?? ''),
151+
share_class_figi: String(item.shareClassFIGI ?? ''),
152+
};
153+
}
154+
}

src/adapters/figi/types.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// ---------------------------------------------------------------------------
2+
// Normalized output types (what agents receive)
3+
// ---------------------------------------------------------------------------
4+
5+
export interface FigiInstrument {
6+
figi: string;
7+
name: string;
8+
ticker: string;
9+
exchange_code: string;
10+
market_sector: string;
11+
security_type: string;
12+
composite_figi: string;
13+
share_class_figi: string;
14+
}
15+
16+
export interface FigiMapOutput {
17+
results: FigiInstrument[];
18+
total: number;
19+
}
20+
21+
export interface FigiSearchOutput {
22+
results: FigiInstrument[];
23+
total: number;
24+
}
25+
26+
export interface FigiFilterOutput {
27+
results: FigiInstrument[];
28+
total: number;
29+
}

src/adapters/registry.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ import { EpaAdapter } from './epa';
143143
import { NceiAdapter } from './ncei';
144144
import { ClimateAdapter } from './climate';
145145
import { QuickchartAdapter } from './quickchart';
146+
import { FigiAdapter } from './figi';
146147
import { config } from '../config';
147148

148149
/**
@@ -916,6 +917,12 @@ export function resolveAdapter(toolId: string): BaseAdapter | undefined {
916917
case 'chart':
917918
// QuickChart — chart image generation, no auth, MIT
918919
return getOrCreate('chart', () => new QuickchartAdapter());
920+
case 'figi': {
921+
const figiKey = (config as Record<string, unknown>).PROVIDER_KEY_OPENFIGI as
922+
| string
923+
| undefined;
924+
return getOrCreate('figi', () => new FigiAdapter(figiKey ?? ''));
925+
}
919926
default:
920927
return undefined;
921928
}

src/config/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ export const appEnvSchema = z.object({
326326
// NOAA NCEI (UC-343) — historical climate data, 1K/day
327327
PROVIDER_KEY_NOAA_NCEI: z.string().optional().default(''),
328328

329+
// OpenFIGI (UC-357) — Bloomberg financial identifier resolution, 25K/day free
330+
PROVIDER_KEY_OPENFIGI: z.string().optional().default(''),
331+
329332
// Predictive Pre-fetching (F8) — fire-and-forget cache warming
330333
PREFETCH_ENABLED: z.string().optional().default('false'),
331334

src/config/provider-limits.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,15 @@
206206
"docs_url": "https://www.ncei.noaa.gov/cdo-web/webservices/v2",
207207
"limit_proof": "Free API key, 1,000 req/day. US Gov public domain. 100K+ global stations, 260+ years."
208208
},
209+
"figi": {
210+
"display_name": "OpenFIGI",
211+
"health_url": "https://api.openfigi.com/v3/search",
212+
"limit_type": "daily",
213+
"free_limit": 25000,
214+
"reset_period": "daily",
215+
"docs_url": "https://www.openfigi.com/api",
216+
"limit_proof": "Bloomberg open standard (ISO 18774). 25K/day free, higher with key. 300M+ instruments."
217+
},
209218
"chart": {
210219
"display_name": "QuickChart",
211220
"health_url": "https://quickchart.io/health",

src/mcp/tool-definitions.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4834,4 +4834,35 @@ export const TOOL_DEFINITIONS: McpToolDefinition[] = [
48344834
openWorldHint: true,
48354835
},
48364836
},
4837+
4838+
// ---------------------------------------------------------------------------
4839+
// OpenFIGI — Bloomberg Financial Identifiers (UC-357, 3 tools)
4840+
// ---------------------------------------------------------------------------
4841+
{
4842+
toolId: 'figi.map',
4843+
mcpName: 'figi.finance.map',
4844+
title: 'Map Financial Identifier to FIGI',
4845+
description:
4846+
'Resolve financial instrument identifiers — ISIN, CUSIP, SEDOL, or ticker symbol to Bloomberg FIGI (ISO 18774). Returns FIGI, composite FIGI, security name, type, and exchange. 300M+ instruments across 45K+ exchanges. Use ID_ISIN, ID_CUSIP, ID_SEDOL, or TICKER as id_type.',
4847+
category: 'finance',
4848+
annotations: READ_ONLY,
4849+
},
4850+
{
4851+
toolId: 'figi.search',
4852+
mcpName: 'figi.finance.search',
4853+
title: 'Search Financial Instruments',
4854+
description:
4855+
'Search 300M+ financial instruments by company name or ticker keyword. Filter by exchange and security type. Returns Bloomberg FIGI, ticker, name, market sector. Covers equities, ETPs, bonds, derivatives globally.',
4856+
category: 'finance',
4857+
annotations: READ_ONLY,
4858+
},
4859+
{
4860+
toolId: 'figi.filter',
4861+
mcpName: 'figi.finance.filter',
4862+
title: 'Filter Financial Instruments',
4863+
description:
4864+
'Filter financial instruments by exchange code, market sector (Equity/Corp/Govt/Index/Curncy/Comdty), or security type (Common Stock/ETP/REIT/ADR). Browse instrument universe by structured criteria. Bloomberg OpenFIGI.',
4865+
category: 'finance',
4866+
annotations: READ_ONLY,
4867+
},
48374868
];

src/schemas/figi.schema.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { z, type ZodSchema } from 'zod';
2+
3+
const figiMap = z
4+
.object({
5+
id_type: z
6+
.enum([
7+
'ID_ISIN',
8+
'ID_CUSIP',
9+
'ID_SEDOL',
10+
'ID_BB_GLOBAL',
11+
'TICKER',
12+
'ID_WERTPAPIER',
13+
'ID_COMMON',
14+
])
15+
.describe('Identifier type: ID_ISIN, ID_CUSIP, ID_SEDOL, TICKER, ID_BB_GLOBAL'),
16+
id_value: z
17+
.string()
18+
.min(1)
19+
.describe(
20+
'Identifier value (e.g. US0378331005 for ISIN, AAPL for ticker, BBG000B9XRY4 for FIGI)',
21+
),
22+
exchange_code: z
23+
.string()
24+
.optional()
25+
.describe('Exchange code to narrow results (e.g. US, LN, JP). Optional.'),
26+
})
27+
.strip();
28+
29+
const figiSearch = z
30+
.object({
31+
query: z
32+
.string()
33+
.min(1)
34+
.describe('Search query — company name or ticker (e.g. "Tesla", "Apple Inc", "MSFT")'),
35+
exchange_code: z.string().optional().describe('Filter by exchange code (e.g. US, LN, JP)'),
36+
security_type: z
37+
.string()
38+
.optional()
39+
.describe('Filter by security type (e.g. "Common Stock", "ETP", "REIT")'),
40+
})
41+
.strip();
42+
43+
const figiFilter = z
44+
.object({
45+
exchange_code: z.string().optional().describe('Exchange code (e.g. US, LN, HK, JP)'),
46+
market_sector: z
47+
.string()
48+
.optional()
49+
.describe('Market sector (e.g. Equity, Corp, Govt, Index, Curncy, Comdty)'),
50+
security_type: z
51+
.string()
52+
.optional()
53+
.describe('Security type (e.g. "Common Stock", "ETP", "REIT", "ADR")'),
54+
})
55+
.strip();
56+
57+
export const figiSchemas: Record<string, ZodSchema> = {
58+
'figi.map': figiMap,
59+
'figi.search': figiSearch,
60+
'figi.filter': figiFilter,
61+
};

0 commit comments

Comments
 (0)