@pipeworx/mcp-edgar 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 +21 -0
- package/README.md +34 -0
- package/package.json +20 -0
- package/server.json +18 -0
- package/src/index.ts +447 -0
- package/tsconfig.json +14 -0
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,34 @@
|
|
|
1
|
+
# mcp-edgar
|
|
2
|
+
|
|
3
|
+
EDGAR MCP — SEC EDGAR public APIs (free, no auth)
|
|
4
|
+
|
|
5
|
+
Part of the [Pipeworx](https://pipeworx.io) open MCP gateway.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
Add to your MCP client config:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"edgar": {
|
|
20
|
+
"url": "https://gateway.pipeworx.io/edgar/mcp"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or use the CLI:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx pipeworx use edgar
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipeworx/mcp-edgar",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "EDGAR MCP — SEC EDGAR public APIs (free, no auth)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "edgar"],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/pipeworx-io/mcp-edgar"
|
|
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/edgar",
|
|
4
|
+
"title": "edgar",
|
|
5
|
+
"description": "EDGAR MCP — SEC EDGAR public APIs (free, no auth)",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://pipeworx.io/packs/edgar",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/pipeworx-io/mcp-edgar",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"remotes": [
|
|
13
|
+
{
|
|
14
|
+
"type": "streamable-http",
|
|
15
|
+
"url": "https://gateway.pipeworx.io/edgar/mcp"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
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
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* EDGAR MCP — SEC EDGAR public APIs (free, no auth)
|
|
18
|
+
*
|
|
19
|
+
* Tools:
|
|
20
|
+
* - edgar_search_filings: full-text search across all SEC filings
|
|
21
|
+
* - edgar_company_filings: get filings for a specific company by CIK or ticker
|
|
22
|
+
* - edgar_company_facts: get structured XBRL financial data for a company
|
|
23
|
+
* - edgar_company_concept: get a specific financial metric over time
|
|
24
|
+
* - edgar_ticker_to_cik: look up CIK from ticker symbol
|
|
25
|
+
*
|
|
26
|
+
* Note: SEC requires a descriptive User-Agent header per their guidelines.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const EFTS_BASE = 'https://efts.sec.gov/LATEST';
|
|
31
|
+
const DATA_BASE = 'https://data.sec.gov';
|
|
32
|
+
const SEC_HEADERS: Record<string, string> = {
|
|
33
|
+
'User-Agent': 'Pipeworx/1.0 (support@pipeworx.io)',
|
|
34
|
+
Accept: 'application/json',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const tools: McpToolExport['tools'] = [
|
|
38
|
+
{
|
|
39
|
+
name: 'edgar_search_filings',
|
|
40
|
+
description:
|
|
41
|
+
'Full-text search across all SEC EDGAR filings. Search by keyword, company name, or topic. Optionally filter by form type and date range.',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object' as const,
|
|
44
|
+
properties: {
|
|
45
|
+
query: { type: 'string', description: 'Search query (e.g., "artificial intelligence", "Tesla revenue")' },
|
|
46
|
+
form_type: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Filter by SEC form type (e.g., "10-K", "10-Q", "8-K", "DEF 14A"). Omit for all types.',
|
|
49
|
+
},
|
|
50
|
+
start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format (e.g., "2024-01-01")' },
|
|
51
|
+
end_date: { type: 'string', description: 'End date in YYYY-MM-DD format (e.g., "2024-12-31")' },
|
|
52
|
+
limit: { type: 'number', description: 'Number of results to return (1-40, default 10)' },
|
|
53
|
+
},
|
|
54
|
+
required: ['query'],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'edgar_company_filings',
|
|
59
|
+
description:
|
|
60
|
+
'Get recent SEC filings for a specific company. Accepts a ticker symbol or CIK number. Optionally filter by form type.',
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: 'object' as const,
|
|
63
|
+
properties: {
|
|
64
|
+
ticker_or_cik: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: 'Ticker symbol (e.g., "AAPL") or CIK number (e.g., "320193")',
|
|
67
|
+
},
|
|
68
|
+
form_type: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'Filter by SEC form type (e.g., "10-K", "10-Q", "8-K"). Omit for all types.',
|
|
71
|
+
},
|
|
72
|
+
limit: { type: 'number', description: 'Max filings to return (1-40, default 20)' },
|
|
73
|
+
},
|
|
74
|
+
required: ['ticker_or_cik'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'edgar_company_facts',
|
|
79
|
+
description:
|
|
80
|
+
'Get structured XBRL financial data for a company by CIK. Returns key financial metrics like revenue, net income, assets, and more with their most recent annual values.',
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object' as const,
|
|
83
|
+
properties: {
|
|
84
|
+
cik: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'Company CIK number (e.g., "320193" for Apple). Use edgar_ticker_to_cik to look up if needed.',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ['cik'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'edgar_company_concept',
|
|
94
|
+
description:
|
|
95
|
+
'Get a specific financial metric over time for a company. Returns all reported values across filings for a given US-GAAP concept.',
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object' as const,
|
|
98
|
+
properties: {
|
|
99
|
+
cik: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
description: 'Company CIK number (e.g., "320193" for Apple)',
|
|
102
|
+
},
|
|
103
|
+
concept: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
description:
|
|
106
|
+
'US-GAAP concept name (e.g., "Revenue", "NetIncomeLoss", "Assets", "Liabilities", "StockholdersEquity", "EarningsPerShareDiluted")',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ['cik', 'concept'],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'edgar_ticker_to_cik',
|
|
114
|
+
description:
|
|
115
|
+
'Look up a company CIK number from its ticker symbol. The CIK is needed for other EDGAR tools.',
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: 'object' as const,
|
|
118
|
+
properties: {
|
|
119
|
+
ticker: {
|
|
120
|
+
type: 'string',
|
|
121
|
+
description: 'Stock ticker symbol (e.g., "AAPL", "MSFT", "TSLA")',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ['ticker'],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
function padCik(cik: string): string {
|
|
132
|
+
return cik.replace(/\D/g, '').padStart(10, '0');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isNumericCik(value: string): boolean {
|
|
136
|
+
return /^\d+$/.test(value.trim());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── Tool implementations ────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
async function searchFilings(
|
|
142
|
+
query: string,
|
|
143
|
+
formType?: string,
|
|
144
|
+
startDate?: string,
|
|
145
|
+
endDate?: string,
|
|
146
|
+
limit?: number,
|
|
147
|
+
) {
|
|
148
|
+
const count = Math.min(40, Math.max(1, limit ?? 10));
|
|
149
|
+
const params = new URLSearchParams({ q: query, from: '0', size: String(count) });
|
|
150
|
+
|
|
151
|
+
if (formType) params.set('forms', formType);
|
|
152
|
+
if (startDate || endDate) {
|
|
153
|
+
params.set('dateRange', 'custom');
|
|
154
|
+
if (startDate) params.set('startdt', startDate);
|
|
155
|
+
if (endDate) params.set('enddt', endDate);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const res = await fetch(`${EFTS_BASE}/search-index?${params}`, { headers: SEC_HEADERS });
|
|
159
|
+
if (!res.ok) throw new Error(`SEC EDGAR search error: ${res.status}`);
|
|
160
|
+
|
|
161
|
+
const data = (await res.json()) as {
|
|
162
|
+
hits: {
|
|
163
|
+
hits: {
|
|
164
|
+
_source: {
|
|
165
|
+
entity_name: string;
|
|
166
|
+
entity_id: string;
|
|
167
|
+
file_num: string;
|
|
168
|
+
form_type: string;
|
|
169
|
+
file_date: string;
|
|
170
|
+
period_of_report: string;
|
|
171
|
+
biz_location: string;
|
|
172
|
+
category: string;
|
|
173
|
+
};
|
|
174
|
+
_id: string;
|
|
175
|
+
}[];
|
|
176
|
+
total: { value: number };
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const results = (data.hits?.hits ?? []).map((hit) => {
|
|
181
|
+
const s = hit._source;
|
|
182
|
+
return {
|
|
183
|
+
entity_name: s.entity_name,
|
|
184
|
+
cik: s.entity_id,
|
|
185
|
+
form_type: s.form_type,
|
|
186
|
+
filing_date: s.file_date,
|
|
187
|
+
period_of_report: s.period_of_report,
|
|
188
|
+
location: s.biz_location,
|
|
189
|
+
filing_id: hit._id,
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
query,
|
|
195
|
+
form_type_filter: formType ?? 'all',
|
|
196
|
+
date_range: { start: startDate ?? null, end: endDate ?? null },
|
|
197
|
+
total_hits: data.hits?.total?.value ?? 0,
|
|
198
|
+
results,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function tickerToCik(ticker: string) {
|
|
203
|
+
const res = await fetch('https://www.sec.gov/files/company_tickers.json', { headers: SEC_HEADERS });
|
|
204
|
+
if (!res.ok) throw new Error(`SEC ticker lookup error: ${res.status}`);
|
|
205
|
+
|
|
206
|
+
const data = (await res.json()) as Record<
|
|
207
|
+
string,
|
|
208
|
+
{ cik_str: number; ticker: string; title: string }
|
|
209
|
+
>;
|
|
210
|
+
|
|
211
|
+
const upperTicker = ticker.toUpperCase().trim();
|
|
212
|
+
for (const entry of Object.values(data)) {
|
|
213
|
+
if (entry.ticker === upperTicker) {
|
|
214
|
+
return {
|
|
215
|
+
ticker: entry.ticker,
|
|
216
|
+
cik: String(entry.cik_str),
|
|
217
|
+
cik_padded: String(entry.cik_str).padStart(10, '0'),
|
|
218
|
+
company_name: entry.title,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
throw new Error(`Ticker "${ticker}" not found in SEC company tickers`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function resolveCik(tickerOrCik: string): Promise<string> {
|
|
227
|
+
if (isNumericCik(tickerOrCik)) return tickerOrCik.trim();
|
|
228
|
+
const result = await tickerToCik(tickerOrCik);
|
|
229
|
+
return result.cik;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function companyFilings(tickerOrCik: string, formType?: string, limit?: number) {
|
|
233
|
+
const cik = await resolveCik(tickerOrCik);
|
|
234
|
+
const paddedCik = padCik(cik);
|
|
235
|
+
const count = Math.min(40, Math.max(1, limit ?? 20));
|
|
236
|
+
|
|
237
|
+
const res = await fetch(`${DATA_BASE}/submissions/CIK${paddedCik}.json`, { headers: SEC_HEADERS });
|
|
238
|
+
if (!res.ok) throw new Error(`SEC EDGAR submissions error: ${res.status}`);
|
|
239
|
+
|
|
240
|
+
const data = (await res.json()) as {
|
|
241
|
+
cik: string;
|
|
242
|
+
name: string;
|
|
243
|
+
sic: string;
|
|
244
|
+
sicDescription: string;
|
|
245
|
+
stateOfIncorporation: string;
|
|
246
|
+
fiscalYearEnd: string;
|
|
247
|
+
tickers: string[];
|
|
248
|
+
filings: {
|
|
249
|
+
recent: {
|
|
250
|
+
accessionNumber: string[];
|
|
251
|
+
filingDate: string[];
|
|
252
|
+
form: string[];
|
|
253
|
+
primaryDocument: string[];
|
|
254
|
+
items: string[];
|
|
255
|
+
size: number[];
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const recent = data.filings.recent;
|
|
261
|
+
const filings: {
|
|
262
|
+
accession_number: string;
|
|
263
|
+
filing_date: string;
|
|
264
|
+
form: string;
|
|
265
|
+
primary_document: string;
|
|
266
|
+
document_url: string;
|
|
267
|
+
}[] = [];
|
|
268
|
+
|
|
269
|
+
for (let i = 0; i < recent.accessionNumber.length && filings.length < count; i++) {
|
|
270
|
+
const form = recent.form[i];
|
|
271
|
+
if (formType && form !== formType) continue;
|
|
272
|
+
|
|
273
|
+
const accession = recent.accessionNumber[i];
|
|
274
|
+
const accessionPath = accession.replace(/-/g, '');
|
|
275
|
+
filings.push({
|
|
276
|
+
accession_number: accession,
|
|
277
|
+
filing_date: recent.filingDate[i],
|
|
278
|
+
form,
|
|
279
|
+
primary_document: recent.primaryDocument[i],
|
|
280
|
+
document_url: `https://www.sec.gov/Archives/edgar/data/${data.cik}/${accessionPath}/${recent.primaryDocument[i]}`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
cik: data.cik,
|
|
286
|
+
company_name: data.name,
|
|
287
|
+
tickers: data.tickers ?? [],
|
|
288
|
+
sic_description: data.sicDescription,
|
|
289
|
+
state_of_incorporation: data.stateOfIncorporation,
|
|
290
|
+
fiscal_year_end: data.fiscalYearEnd,
|
|
291
|
+
filter_form_type: formType ?? 'all',
|
|
292
|
+
filings,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function companyFacts(cik: string) {
|
|
297
|
+
const paddedCik = padCik(cik);
|
|
298
|
+
const res = await fetch(`${DATA_BASE}/api/xbrl/companyfacts/CIK${paddedCik}.json`, {
|
|
299
|
+
headers: SEC_HEADERS,
|
|
300
|
+
});
|
|
301
|
+
if (!res.ok) throw new Error(`SEC EDGAR company facts error: ${res.status}`);
|
|
302
|
+
|
|
303
|
+
const data = (await res.json()) as {
|
|
304
|
+
cik: number;
|
|
305
|
+
entityName: string;
|
|
306
|
+
facts: {
|
|
307
|
+
'us-gaap'?: Record<
|
|
308
|
+
string,
|
|
309
|
+
{
|
|
310
|
+
label: string;
|
|
311
|
+
description: string;
|
|
312
|
+
units: Record<
|
|
313
|
+
string,
|
|
314
|
+
{ end: string; val: number; accn: string; fy: number; fp: string; form: string; filed: string; frame?: string }[]
|
|
315
|
+
>;
|
|
316
|
+
}
|
|
317
|
+
>;
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const usGaap = data.facts?.['us-gaap'] ?? {};
|
|
322
|
+
const KEY_METRICS = [
|
|
323
|
+
'Revenues',
|
|
324
|
+
'RevenueFromContractWithCustomerExcludingAssessedTax',
|
|
325
|
+
'NetIncomeLoss',
|
|
326
|
+
'Assets',
|
|
327
|
+
'Liabilities',
|
|
328
|
+
'StockholdersEquity',
|
|
329
|
+
'CashAndCashEquivalentsAtCarryingValue',
|
|
330
|
+
'EarningsPerShareBasic',
|
|
331
|
+
'EarningsPerShareDiluted',
|
|
332
|
+
'CommonStockSharesOutstanding',
|
|
333
|
+
'OperatingIncomeLoss',
|
|
334
|
+
'GrossProfit',
|
|
335
|
+
'ResearchAndDevelopmentExpense',
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
const metrics: Record<
|
|
339
|
+
string,
|
|
340
|
+
{ label: string; most_recent_annual: { year: number; value: number; filed: string } | null }
|
|
341
|
+
> = {};
|
|
342
|
+
|
|
343
|
+
for (const key of KEY_METRICS) {
|
|
344
|
+
const fact = usGaap[key];
|
|
345
|
+
if (!fact) continue;
|
|
346
|
+
|
|
347
|
+
const usdEntries = fact.units['USD'] ?? fact.units['shares'] ?? [];
|
|
348
|
+
const annual = usdEntries
|
|
349
|
+
.filter((e) => e.form === '10-K' && e.frame !== undefined)
|
|
350
|
+
.sort((a, b) => (b.fy ?? 0) - (a.fy ?? 0));
|
|
351
|
+
|
|
352
|
+
metrics[key] = {
|
|
353
|
+
label: fact.label,
|
|
354
|
+
most_recent_annual: annual[0]
|
|
355
|
+
? { year: annual[0].fy, value: annual[0].val, filed: annual[0].filed }
|
|
356
|
+
: null,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
cik: String(data.cik),
|
|
362
|
+
company_name: data.entityName,
|
|
363
|
+
key_financials: metrics,
|
|
364
|
+
available_concepts: Object.keys(usGaap).length,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function companyConcept(cik: string, concept: string) {
|
|
369
|
+
const paddedCik = padCik(cik);
|
|
370
|
+
const res = await fetch(
|
|
371
|
+
`${DATA_BASE}/api/xbrl/companyconcept/CIK${paddedCik}/us-gaap/${encodeURIComponent(concept)}.json`,
|
|
372
|
+
{ headers: SEC_HEADERS },
|
|
373
|
+
);
|
|
374
|
+
if (!res.ok) throw new Error(`SEC EDGAR company concept error: ${res.status} — concept "${concept}" may not exist for CIK ${cik}`);
|
|
375
|
+
|
|
376
|
+
const data = (await res.json()) as {
|
|
377
|
+
cik: number;
|
|
378
|
+
entityName: string;
|
|
379
|
+
tag: string;
|
|
380
|
+
taxonomy: string;
|
|
381
|
+
label: string;
|
|
382
|
+
description: string;
|
|
383
|
+
units: Record<
|
|
384
|
+
string,
|
|
385
|
+
{ end: string; val: number; accn: string; fy: number; fp: string; form: string; filed: string; frame?: string }[]
|
|
386
|
+
>;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// Return annual (10-K) values sorted by fiscal year descending
|
|
390
|
+
const entries: { fiscal_year: number; period_end: string; value: number; filed: string; unit: string }[] = [];
|
|
391
|
+
|
|
392
|
+
for (const [unit, values] of Object.entries(data.units)) {
|
|
393
|
+
const annuals = values
|
|
394
|
+
.filter((e) => e.form === '10-K' && e.frame !== undefined)
|
|
395
|
+
.sort((a, b) => (b.fy ?? 0) - (a.fy ?? 0));
|
|
396
|
+
|
|
397
|
+
for (const entry of annuals) {
|
|
398
|
+
entries.push({
|
|
399
|
+
fiscal_year: entry.fy,
|
|
400
|
+
period_end: entry.end,
|
|
401
|
+
value: entry.val,
|
|
402
|
+
filed: entry.filed,
|
|
403
|
+
unit,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return {
|
|
409
|
+
cik: String(data.cik),
|
|
410
|
+
company_name: data.entityName,
|
|
411
|
+
concept: data.tag,
|
|
412
|
+
label: data.label,
|
|
413
|
+
description: data.description,
|
|
414
|
+
annual_values: entries,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ── callTool router ─────────────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
421
|
+
switch (name) {
|
|
422
|
+
case 'edgar_search_filings':
|
|
423
|
+
return searchFilings(
|
|
424
|
+
args.query as string,
|
|
425
|
+
args.form_type as string | undefined,
|
|
426
|
+
args.start_date as string | undefined,
|
|
427
|
+
args.end_date as string | undefined,
|
|
428
|
+
args.limit as number | undefined,
|
|
429
|
+
);
|
|
430
|
+
case 'edgar_company_filings':
|
|
431
|
+
return companyFilings(
|
|
432
|
+
args.ticker_or_cik as string,
|
|
433
|
+
args.form_type as string | undefined,
|
|
434
|
+
args.limit as number | undefined,
|
|
435
|
+
);
|
|
436
|
+
case 'edgar_company_facts':
|
|
437
|
+
return companyFacts(args.cik as string);
|
|
438
|
+
case 'edgar_company_concept':
|
|
439
|
+
return companyConcept(args.cik as string, args.concept as string);
|
|
440
|
+
case 'edgar_ticker_to_cik':
|
|
441
|
+
return tickerToCik(args.ticker as string);
|
|
442
|
+
default:
|
|
443
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export default { tools, callTool, meter: { credits: 10 } } 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
|
+
}
|