@pipeworx/mcp-sec-xbrl 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pipeworx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # mcp-sec-xbrl
2
+
3
+ SEC XBRL MCP — wraps SEC EDGAR XBRL API (data.sec.gov)
4
+
5
+ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 965+ live data sources.
6
+
7
+ ## Tools
8
+
9
+ | Tool | Description |
10
+ |------|-------------|
11
+
12
+ ## Quick Start
13
+
14
+ Add to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "sec-xbrl": {
20
+ "url": "https://gateway.pipeworx.io/sec-xbrl/mcp"
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ Or connect to the full Pipeworx gateway for access to all 965+ data sources:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "pipeworx": {
32
+ "url": "https://gateway.pipeworx.io/mcp"
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Using with ask_pipeworx
39
+
40
+ Instead of calling tools directly, you can ask questions in plain English:
41
+
42
+ ```
43
+ ask_pipeworx({ question: "your question about Sec Xbrl data" })
44
+ ```
45
+
46
+ The gateway picks the right tool and fills the arguments automatically.
47
+
48
+ ## More
49
+
50
+ - [All tools and guides](https://github.com/pipeworx-io/examples)
51
+ - [pipeworx.io](https://pipeworx.io)
52
+
53
+ ## License
54
+
55
+ MIT
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@pipeworx/mcp-sec-xbrl",
3
+ "version": "0.1.0",
4
+ "description": "SEC XBRL MCP — wraps SEC EDGAR XBRL API (data.sec.gov)",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "sec-xbrl"],
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/pipeworx-io/mcp-sec-xbrl"
13
+ },
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.0"
19
+ }
20
+ }
package/server.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.pipeworx-io/sec-xbrl",
4
+ "title": "Sec Xbrl",
5
+ "description": "SEC XBRL MCP — wraps SEC EDGAR XBRL API (data.sec.gov)",
6
+ "version": "0.1.0",
7
+ "websiteUrl": "https://pipeworx.io/packs/sec-xbrl",
8
+ "repository": {
9
+ "url": "https://github.com/pipeworx-io/mcp-sec-xbrl",
10
+ "source": "github"
11
+ },
12
+ "remotes": [
13
+ {
14
+ "type": "streamable-http",
15
+ "url": "https://gateway.pipeworx.io/sec-xbrl/mcp"
16
+ }
17
+ ]
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,458 @@
1
+ interface McpToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: 'object';
6
+ properties: Record<string, unknown>;
7
+ required?: string[];
8
+ };
9
+ }
10
+
11
+ interface McpToolExport {
12
+ tools: McpToolDefinition[];
13
+ callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
14
+ meter?: { credits: number };
15
+ cost?: Record<string, unknown>;
16
+ provider?: string;
17
+ }
18
+
19
+ /**
20
+ * SEC XBRL MCP — wraps SEC EDGAR XBRL API (data.sec.gov)
21
+ *
22
+ * Free, no authentication required. User-Agent header is required by SEC.
23
+ * Provides structured financial data from SEC filings.
24
+ *
25
+ * Tools:
26
+ * - get_company_facts: get all XBRL facts for a company (financial statements)
27
+ * - get_company_concept: get a specific financial metric across all filings
28
+ * - search_filings: search recent SEC filings for a company
29
+ * - get_company_financials: high-level convenience — revenue, net income, assets, etc.
30
+ * from the most recent 10-K, picking the right XBRL tag automatically.
31
+ */
32
+
33
+
34
+ const BASE_URL = 'https://data.sec.gov';
35
+ const EFTS_URL = 'https://efts.sec.gov/LATEST';
36
+ const USER_AGENT = 'Pipeworx/1.0 (pipeworx.io)';
37
+
38
+ async function secGet(url: string): Promise<unknown> {
39
+ const res = await fetch(url, {
40
+ headers: {
41
+ 'User-Agent': USER_AGENT,
42
+ Accept: 'application/json',
43
+ },
44
+ });
45
+ if (!res.ok) {
46
+ const text = await res.text();
47
+ // SEC returns S3-style XML error bodies on 404 — extract <Code> / <Message>
48
+ // so callers see "NoSuchKey: The specified key does not exist" instead of
49
+ // 200 chars of escaped XML. Production analytics 2026-06-08: 6x
50
+ // get_company_concept 404s, all with the same indecipherable XML envelope.
51
+ // Same pattern we used for FMI on 2026-06-06.
52
+ const code = /<Code>([^<]+)<\/Code>/i.exec(text)?.[1];
53
+ const message = /<Message>([^<]+)<\/Message>/i.exec(text)?.[1];
54
+ if (code || message) {
55
+ const human = [code, message].filter(Boolean).join(': ');
56
+ const hint = res.status === 404
57
+ ? ' (CIK or concept tag not found — for company concepts try common ones like Revenues, NetIncomeLoss, Assets; verify the CIK via edgar_ticker_to_cik)'
58
+ : '';
59
+ throw new Error(`SEC API error (${res.status}): ${human}${hint}`);
60
+ }
61
+ throw new Error(`SEC API error (${res.status}): ${text.slice(0, 200)}`);
62
+ }
63
+ return res.json();
64
+ }
65
+
66
+ function padCik(cik: string): string {
67
+ if (typeof cik !== 'string' || !cik.trim()) {
68
+ throw new Error('Required argument "cik" is missing or empty. Pass a CIK like "320193" or "0000320193".');
69
+ }
70
+ // SEC requires 10-digit zero-padded CIK
71
+ const cleaned = cik.replace(/^0+/, '').replace(/\D/g, '');
72
+ return cleaned.padStart(10, '0');
73
+ }
74
+
75
+ const tools: McpToolExport['tools'] = [
76
+ {
77
+ name: 'get_company_facts',
78
+ description:
79
+ 'List all XBRL taxonomies and concept tags filed by a company (CIK required). Returns entity name, taxonomy names (us-gaap, dei, etc.), concept count per taxonomy, and a sample of up to 20 tag names each. Use get_company_concept or get_company_financials to retrieve actual numeric values for a specific tag.',
80
+ inputSchema: {
81
+ type: 'object',
82
+ properties: {
83
+ cik: {
84
+ type: 'string',
85
+ description: 'SEC Central Index Key (e.g., "320193" for Apple, "789019" for Microsoft)',
86
+ },
87
+ },
88
+ required: ['cik'],
89
+ },
90
+ },
91
+ {
92
+ name: 'get_company_concept',
93
+ description:
94
+ 'Get a specific financial metric for a company across all filings. Use this to track revenue, net income, or any XBRL tag over time. Example: get_company_concept(cik: "320193", taxonomy: "us-gaap", tag: "Revenue").',
95
+ inputSchema: {
96
+ type: 'object',
97
+ properties: {
98
+ cik: {
99
+ type: 'string',
100
+ description: 'SEC Central Index Key (e.g., "320193" for Apple)',
101
+ },
102
+ taxonomy: {
103
+ type: 'string',
104
+ description: 'XBRL taxonomy: "us-gaap", "ifrs-full", "dei", or "srt"',
105
+ },
106
+ tag: {
107
+ type: 'string',
108
+ description: 'XBRL concept tag (e.g., "Revenue", "NetIncomeLoss", "Assets", "EarningsPerShareBasic")',
109
+ },
110
+ },
111
+ required: ['cik', 'taxonomy', 'tag'],
112
+ },
113
+ },
114
+ {
115
+ name: 'search_filings',
116
+ description:
117
+ 'Search recent SEC filings for a company by CIK. Optionally filter by filing type (10-K, 10-Q, 8-K, etc.). Returns filing dates, types, and accession numbers.',
118
+ inputSchema: {
119
+ type: 'object',
120
+ properties: {
121
+ cik: {
122
+ type: 'string',
123
+ description: 'SEC Central Index Key (e.g., "320193" for Apple)',
124
+ },
125
+ type: {
126
+ type: 'string',
127
+ description: 'Filing type filter (e.g., "10-K", "10-Q", "8-K", "DEF 14A")',
128
+ },
129
+ },
130
+ required: ['cik'],
131
+ },
132
+ },
133
+ {
134
+ name: 'get_company_financials',
135
+ description:
136
+ 'High-level summary of a public US company\'s annual (10-K) financials: revenue, net income, total assets, cash, EPS, etc. Returns clean numerical values with the XBRL tag used and the period-end date. By default returns the most recent fiscal year; pass `fiscal_year_end` to get a specific year (e.g. "2024-12-31" for Tesla FY2024 or just "2024" to auto-match the year). Prefer this over get_company_facts/get_company_concept for any single-company financial snapshot question. Pass a CIK (e.g. "320193") or a ticker (e.g. "AAPL"; auto-resolves to CIK).',
137
+ inputSchema: {
138
+ type: 'object',
139
+ properties: {
140
+ company: {
141
+ type: 'string',
142
+ description: 'CIK (e.g., "320193", "0000320193") OR ticker symbol (e.g., "AAPL", "TSLA", "MSFT"). Ticker is auto-resolved to CIK via SEC EDGAR.',
143
+ },
144
+ fiscal_year_end: {
145
+ type: 'string',
146
+ description: 'Optional. Pass a 4-digit fiscal year ("2024") to get values from any 10-K period ending in that calendar year, OR a specific period-end date ("2024-12-31"). Omit to get the most recent fiscal year.',
147
+ },
148
+ },
149
+ required: ['company'],
150
+ },
151
+ },
152
+ ];
153
+
154
+ // Pulls the company identifier from any of the common alias names an LLM
155
+ // might supply. The schema names "company" or "cik" depending on the tool,
156
+ // but agents reach for "ticker", "symbol", or "company" interchangeably in
157
+ // production — the prior 21% error rate on this pack was almost entirely
158
+ // agents passing `{ticker: "AAPL"}` and getting "Required argument 'company'
159
+ // is missing" back. Resolving all aliases in dispatch makes the tools
160
+ // LLM-forgiving without breaking the documented schema.
161
+ function identifierArg(args: Record<string, unknown>): string {
162
+ return (
163
+ (args.cik as string | undefined) ??
164
+ (args.company as string | undefined) ??
165
+ (args.ticker as string | undefined) ??
166
+ (args.symbol as string | undefined) ??
167
+ ''
168
+ );
169
+ }
170
+
171
+ async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
172
+ switch (name) {
173
+ case 'get_company_facts':
174
+ return getCompanyFacts(identifierArg(args));
175
+ case 'get_company_concept':
176
+ return getCompanyConcept(
177
+ identifierArg(args),
178
+ args.taxonomy as string,
179
+ args.tag as string,
180
+ );
181
+ case 'search_filings':
182
+ return searchFilings(identifierArg(args), args.type as string | undefined);
183
+ case 'get_company_financials':
184
+ return getCompanyFinancials(identifierArg(args), args.fiscal_year_end as string | undefined);
185
+ default:
186
+ throw new Error(`Unknown tool: ${name}`);
187
+ }
188
+ }
189
+
190
+ // ── ticker → CIK resolution ────────────────────────────────────────────
191
+ let TICKER_CACHE: Record<string, string> | null = null;
192
+
193
+ async function resolveTickerToCik(input: string): Promise<string> {
194
+ const trimmed = input.trim();
195
+ // Already a CIK (digits)?
196
+ if (/^\d+$/.test(trimmed)) return padCik(trimmed);
197
+
198
+ if (!TICKER_CACHE) {
199
+ const data = (await secGet('https://www.sec.gov/files/company_tickers.json')) as Record<
200
+ string,
201
+ { cik_str: number; ticker: string; title: string }
202
+ >;
203
+ TICKER_CACHE = {};
204
+ for (const entry of Object.values(data)) {
205
+ TICKER_CACHE[entry.ticker.toUpperCase()] = String(entry.cik_str);
206
+ }
207
+ }
208
+ const cik = TICKER_CACHE[trimmed.toUpperCase()];
209
+ if (!cik) throw new Error(`Could not resolve ticker "${input}" to a SEC CIK. Try passing the CIK directly.`);
210
+ return padCik(cik);
211
+ }
212
+
213
+ // ── high-level financials ──────────────────────────────────────────────
214
+ const REVENUE_TAGS = [
215
+ 'Revenues',
216
+ 'RevenueFromContractWithCustomerExcludingAssessedTax',
217
+ 'RevenueFromContractWithCustomerIncludingAssessedTax',
218
+ 'SalesRevenueNet',
219
+ 'SalesRevenueGoodsNet',
220
+ ];
221
+
222
+ interface XbrlEntry {
223
+ val: number;
224
+ fy: number;
225
+ fp: string;
226
+ end: string;
227
+ filed: string;
228
+ form: string;
229
+ }
230
+
231
+ interface XbrlFact {
232
+ label: string;
233
+ units: Record<string, XbrlEntry[]>;
234
+ }
235
+
236
+ // Sort by `end` (period end date) descending, not `fy` (filing year). A 10-K
237
+ // filed in 2025 contains comparative data for FY2023, FY2024, AND FY2025 — all
238
+ // stamped fy=2025 since fy is the filing context, not the reported period.
239
+ // `end` is the actual period-end date, which is what we want.
240
+ //
241
+ // If `targetEnd` is provided, pick the entry whose `end` matches (exact date
242
+ // or 4-digit year prefix). Otherwise return the most recent annual entry.
243
+ function pickAnnual(
244
+ facts: Record<string, XbrlFact>,
245
+ tag: string,
246
+ targetEnd?: string,
247
+ ): (XbrlEntry & { tag: string; label: string }) | null {
248
+ const fact = facts[tag];
249
+ if (!fact) return null;
250
+ const entries = fact.units?.['USD'] ?? fact.units?.['USD/shares'] ?? fact.units?.['shares'] ?? [];
251
+ let annual = entries.filter((e) => e.form === '10-K' && e.fp === 'FY');
252
+ if (targetEnd) {
253
+ // Accept either full ISO date or 4-digit year prefix
254
+ annual = annual.filter((e) =>
255
+ /^\d{4}$/.test(targetEnd) ? e.end?.startsWith(targetEnd) : e.end === targetEnd,
256
+ );
257
+ }
258
+ annual.sort((a, b) => (b.end ?? '').localeCompare(a.end ?? ''));
259
+ if (!annual[0]) return null;
260
+ return { tag, label: fact.label, ...annual[0] };
261
+ }
262
+
263
+ // Pick whichever candidate tag has the most recent annual entry — companies
264
+ // change which us-gaap tag they use over time (e.g. MSFT switched from
265
+ // "Revenues" to "RevenueFromContractWithCustomerExcludingAssessedTax" after
266
+ // ASC 606), so first-match-wins returns stale data.
267
+ function bestMatch(facts: Record<string, XbrlFact>, tagCandidates: string[], targetEnd?: string) {
268
+ let best: (XbrlEntry & { tag: string; label: string }) | null = null;
269
+ for (const tag of tagCandidates) {
270
+ const hit = pickAnnual(facts, tag, targetEnd);
271
+ if (!hit) continue;
272
+ if (!best || (hit.end ?? '') > (best.end ?? '')) best = hit;
273
+ }
274
+ return best;
275
+ }
276
+
277
+ async function getCompanyFinancials(company: string, fiscalYearEnd?: string) {
278
+ if (typeof company !== 'string' || !company.trim()) {
279
+ throw new Error('Required argument "company" is missing or empty. Pass a CIK like "320193" or a ticker like "AAPL".');
280
+ }
281
+ const cik = await resolveTickerToCik(company);
282
+ const data = (await secGet(
283
+ `${BASE_URL}/api/xbrl/companyfacts/CIK${cik}.json`,
284
+ )) as {
285
+ cik: number;
286
+ entityName: string;
287
+ facts: { 'us-gaap'?: Record<string, XbrlFact> };
288
+ };
289
+ const usGaap = data.facts?.['us-gaap'] ?? {};
290
+
291
+ const revenue = bestMatch(usGaap, REVENUE_TAGS, fiscalYearEnd);
292
+
293
+ return {
294
+ cik: String(data.cik),
295
+ entity_name: data.entityName,
296
+ period_end: revenue?.end ?? null,
297
+ fiscal_year: revenue?.fy ?? null,
298
+ revenue,
299
+ net_income: bestMatch(usGaap, ['NetIncomeLoss'], fiscalYearEnd),
300
+ total_assets: bestMatch(usGaap, ['Assets'], fiscalYearEnd),
301
+ total_liabilities: bestMatch(usGaap, ['Liabilities'], fiscalYearEnd),
302
+ stockholders_equity: bestMatch(usGaap, ['StockholdersEquity'], fiscalYearEnd),
303
+ cash_and_equivalents: bestMatch(usGaap, [
304
+ 'CashAndCashEquivalentsAtCarryingValue',
305
+ 'Cash',
306
+ ], fiscalYearEnd),
307
+ operating_income: bestMatch(usGaap, ['OperatingIncomeLoss'], fiscalYearEnd),
308
+ gross_profit: bestMatch(usGaap, ['GrossProfit'], fiscalYearEnd),
309
+ rnd_expense: bestMatch(usGaap, ['ResearchAndDevelopmentExpense'], fiscalYearEnd),
310
+ eps_basic: bestMatch(usGaap, ['EarningsPerShareBasic'], fiscalYearEnd),
311
+ eps_diluted: bestMatch(usGaap, ['EarningsPerShareDiluted'], fiscalYearEnd),
312
+ common_shares_outstanding: bestMatch(usGaap, ['CommonStockSharesOutstanding'], fiscalYearEnd),
313
+ };
314
+ }
315
+
316
+ async function getCompanyFacts(cik: string) {
317
+ const paddedCik = padCik(cik);
318
+ const data = (await secGet(
319
+ `${BASE_URL}/api/xbrl/companyfacts/CIK${paddedCik}.json`,
320
+ )) as {
321
+ cik: number;
322
+ entityName: string;
323
+ facts: Record<
324
+ string,
325
+ Record<
326
+ string,
327
+ {
328
+ label: string;
329
+ description: string;
330
+ units: Record<string, Array<{ val: number; fy: number; fp: string; end: string; filed: string }>>;
331
+ }
332
+ >
333
+ >;
334
+ };
335
+
336
+ // Summarize the available facts rather than returning everything
337
+ const taxonomies: Record<string, string[]> = {};
338
+ for (const [taxonomy, concepts] of Object.entries(data.facts)) {
339
+ taxonomies[taxonomy] = Object.keys(concepts);
340
+ }
341
+
342
+ return {
343
+ cik: data.cik,
344
+ entity_name: data.entityName,
345
+ taxonomies: Object.entries(taxonomies).map(([name, tags]) => ({
346
+ taxonomy: name,
347
+ concept_count: tags.length,
348
+ sample_tags: tags.slice(0, 20),
349
+ })),
350
+ };
351
+ }
352
+
353
+ async function getCompanyConcept(cik: string, taxonomy: string, tag: string) {
354
+ const paddedCik = padCik(cik);
355
+ const data = (await secGet(
356
+ `${BASE_URL}/api/xbrl/companyconcept/CIK${paddedCik}/${encodeURIComponent(taxonomy)}/${encodeURIComponent(tag)}.json`,
357
+ )) as {
358
+ cik: number;
359
+ entityName: string;
360
+ tag: string;
361
+ taxonomy: string;
362
+ label: string;
363
+ description: string;
364
+ units: Record<
365
+ string,
366
+ Array<{
367
+ val: number;
368
+ fy: number;
369
+ fp: string;
370
+ end: string;
371
+ start?: string;
372
+ filed: string;
373
+ accn: string;
374
+ form: string;
375
+ }>
376
+ >;
377
+ };
378
+
379
+ const units: Record<string, unknown[]> = {};
380
+ for (const [unit, entries] of Object.entries(data.units)) {
381
+ // Return the most recent entries (last 20)
382
+ units[unit] = entries.slice(-20).map((e) => ({
383
+ value: e.val,
384
+ fiscal_year: e.fy,
385
+ fiscal_period: e.fp,
386
+ period_end: e.end,
387
+ filed: e.filed,
388
+ form: e.form,
389
+ }));
390
+ }
391
+
392
+ return {
393
+ cik: data.cik,
394
+ entity_name: data.entityName,
395
+ tag: data.tag,
396
+ taxonomy: data.taxonomy,
397
+ label: data.label,
398
+ description: data.description,
399
+ units,
400
+ };
401
+ }
402
+
403
+ async function searchFilings(cik: string, type?: string) {
404
+ const paddedCik = padCik(cik);
405
+ const data = (await secGet(
406
+ `${BASE_URL}/submissions/CIK${paddedCik}.json`,
407
+ )) as {
408
+ cik: string;
409
+ entityType: string;
410
+ name: string;
411
+ tickers: string[];
412
+ exchanges: string[];
413
+ ein: string;
414
+ sic: string;
415
+ sicDescription: string;
416
+ stateOfIncorporation: string;
417
+ filings: {
418
+ recent: {
419
+ accessionNumber: string[];
420
+ filingDate: string[];
421
+ reportDate: string[];
422
+ form: string[];
423
+ primaryDocument: string[];
424
+ primaryDocDescription: string[];
425
+ };
426
+ };
427
+ };
428
+
429
+ const recent = data.filings.recent;
430
+ let filings = recent.accessionNumber.map((accn, i) => ({
431
+ accession_number: accn,
432
+ filing_date: recent.filingDate[i],
433
+ report_date: recent.reportDate[i],
434
+ form: recent.form[i],
435
+ document: recent.primaryDocument[i],
436
+ description: recent.primaryDocDescription[i],
437
+ }));
438
+
439
+ if (type) {
440
+ filings = filings.filter((f) =>
441
+ f.form?.toLowerCase() === type.toLowerCase(),
442
+ );
443
+ }
444
+
445
+ return {
446
+ cik: data.cik,
447
+ name: data.name,
448
+ tickers: data.tickers,
449
+ exchanges: data.exchanges,
450
+ sic: data.sic,
451
+ sic_description: data.sicDescription,
452
+ state_of_incorporation: data.stateOfIncorporation,
453
+ filing_count: filings.length,
454
+ filings: filings.slice(0, 25),
455
+ };
456
+ }
457
+
458
+ export default { tools, callTool, meter: { credits: 1 } } satisfies McpToolExport;
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }