@pipeworx/mcp-edgar 0.3.10 → 0.3.11
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 +2 -2
- package/package.json +3 -3
- package/server.json +1 -1
- package/src/index.ts +75 -9
- package/src/server.ts +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Securities and Exchange Commission's filings database. Every public company in the US files here, every filing is timestamped and immutable, and every disclosure (revenue, debt, risk factors, executive compensation, M&A activity, insider trading) is structured for machine retrieval. Free and authoritative.
|
|
4
4
|
|
|
5
|
-
Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to
|
|
5
|
+
Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1819+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
|
|
6
6
|
|
|
7
7
|
## Why this matters for AI agents
|
|
8
8
|
|
|
@@ -106,7 +106,7 @@ directly, instead of just this one's:
|
|
|
106
106
|
}
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
Both URLs reach the same gateway and the same
|
|
109
|
+
Both URLs reach the same gateway and the same 1819+ data sources. The
|
|
110
110
|
only difference is which pack's tools are listed **directly**; `ask_pipeworx`
|
|
111
111
|
reaches all of them from either one.
|
|
112
112
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipeworx/mcp-edgar",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "EDGAR MCP — SEC EDGAR public APIs (free, no auth)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@cloudflare/workers-types": "^4.20260405.1"
|
|
27
27
|
},
|
|
28
28
|
"pipeworx": {
|
|
29
|
-
"sourceHash": "v1-
|
|
30
|
-
"sourceCommit": "
|
|
29
|
+
"sourceHash": "v1-7ce3b2ab39a6713c682f44a33fe013a7e07b55298b5e01883e16f33b0d1ab1d4",
|
|
30
|
+
"sourceCommit": "a03f96ed672b8c149593c64ecbf30280cabf084c"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.pipeworx-io/edgar",
|
|
4
4
|
"title": "Edgar",
|
|
5
5
|
"description": "EDGAR MCP — SEC EDGAR public APIs (free, no auth)",
|
|
6
|
-
"version": "0.3.
|
|
6
|
+
"version": "0.3.11",
|
|
7
7
|
"websiteUrl": "https://pipeworx.io/packs/edgar",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/pipeworx-io/mcp-edgar",
|
package/src/index.ts
CHANGED
|
@@ -1258,7 +1258,7 @@ const tools: McpToolExport['tools'] = [
|
|
|
1258
1258
|
concept: {
|
|
1259
1259
|
type: 'string',
|
|
1260
1260
|
description:
|
|
1261
|
-
'REQUIRED (alias `metric`). Metric name. Common: "Revenue" / "Revenues", "NetIncomeLoss", "Cash", "Assets", "Liabilities", "StockholdersEquity", "EarningsPerShareDiluted", "LongTermDebt".',
|
|
1261
|
+
'REQUIRED (alias `metric`). Metric name. Common: "Revenue" / "Revenues", "NetIncomeLoss", "Cash", "Assets", "Liabilities", "StockholdersEquity", "EarningsPerShareDiluted", "LongTermDebt", "SharesOutstanding" (newest cover-page count, dei), "Capex", "OperatingCashFlow", "ResearchAndDevelopment", "SGA", "CostOfRevenue", "DilutedShares", "PublicFloat". Exact us-gaap or dei tag names also work.',
|
|
1262
1262
|
},
|
|
1263
1263
|
period: {
|
|
1264
1264
|
type: 'string',
|
|
@@ -3148,6 +3148,17 @@ async function companyFacts(cikOrTicker: string) {
|
|
|
3148
3148
|
// Anything not in the table still falls through to [concept] (the bare
|
|
3149
3149
|
// literal), so we don't regress for niche concepts not yet observed in
|
|
3150
3150
|
// failures.
|
|
3151
|
+
// Cover-page tags live in the `dei` taxonomy, not us-gaap. Until 2026-10-16
|
|
3152
|
+
// every candidate was fetched under /us-gaap/, so the dei tag listed below for
|
|
3153
|
+
// "shares outstanding" 404'd on every filer and
|
|
3154
|
+
// edgar_company_concept({concept:"EntityCommonStockSharesOutstanding"}) told
|
|
3155
|
+
// callers "this filer reports no XBRL tag matching ..." for Tesla, which has 67
|
|
3156
|
+
// such facts (latest 3,949,547,394 at 2026-07-16). A report built on that
|
|
3157
|
+
// answer took the share count from a stale 10-Q cover instead and was off 5%
|
|
3158
|
+
// on every per-share number. Resolve the namespace per TAG, not per lookup.
|
|
3159
|
+
const DEI_TAGS = new Set(['EntityCommonStockSharesOutstanding', 'EntityPublicFloat']);
|
|
3160
|
+
const namespaceForTag = (defaultNs: string, tag: string): string => (DEI_TAGS.has(tag) ? 'dei' : defaultNs);
|
|
3161
|
+
|
|
3151
3162
|
const CONCEPT_CANDIDATES: Record<string, string[]> = {
|
|
3152
3163
|
// ── friendly names ────────────────────────────────────────────────
|
|
3153
3164
|
revenue: ['RevenueFromContractWithCustomerExcludingAssessedTax', 'Revenues', 'SalesRevenueNet'],
|
|
@@ -3165,6 +3176,31 @@ const CONCEPT_CANDIDATES: Record<string, string[]> = {
|
|
|
3165
3176
|
sharesoutstanding: ['CommonStockSharesOutstanding', 'EntityCommonStockSharesOutstanding', 'CommonStockSharesIssued'],
|
|
3166
3177
|
commonsharesoutstanding: ['CommonStockSharesOutstanding', 'EntityCommonStockSharesOutstanding', 'CommonStockSharesIssued'],
|
|
3167
3178
|
commonstocksharesoutstanding: ['CommonStockSharesOutstanding', 'EntityCommonStockSharesOutstanding'],
|
|
3179
|
+
// The cash-flow and expense lines an equity model needs. Measured 2026-10-16:
|
|
3180
|
+
// a routed "Tesla capex Q2 2026" question reached this tool as
|
|
3181
|
+
// concept:"CapitalExpenditures", which no filer tags, and the answer was
|
|
3182
|
+
// "reports no matching XBRL tag" — a miss on a figure every 10-Q carries as
|
|
3183
|
+
// PaymentsToAcquirePropertyPlantAndEquipment. The report that asked went to a
|
|
3184
|
+
// third-party site for it instead.
|
|
3185
|
+
capex: ['PaymentsToAcquirePropertyPlantAndEquipment', 'PaymentsToAcquireProductiveAssets', 'PaymentsForCapitalImprovements'],
|
|
3186
|
+
capitalexpenditure: ['PaymentsToAcquirePropertyPlantAndEquipment', 'PaymentsToAcquireProductiveAssets', 'PaymentsForCapitalImprovements'],
|
|
3187
|
+
capitalexpenditures: ['PaymentsToAcquirePropertyPlantAndEquipment', 'PaymentsToAcquireProductiveAssets', 'PaymentsForCapitalImprovements'],
|
|
3188
|
+
operatingcashflow: ['NetCashProvidedByUsedInOperatingActivities', 'NetCashProvidedByUsedInOperatingActivitiesContinuingOperations'],
|
|
3189
|
+
cashfromoperations: ['NetCashProvidedByUsedInOperatingActivities', 'NetCashProvidedByUsedInOperatingActivitiesContinuingOperations'],
|
|
3190
|
+
netcashfromoperatingactivities: ['NetCashProvidedByUsedInOperatingActivities', 'NetCashProvidedByUsedInOperatingActivitiesContinuingOperations'],
|
|
3191
|
+
netcashprovidedbyoperatingactivities: ['NetCashProvidedByUsedInOperatingActivities', 'NetCashProvidedByUsedInOperatingActivitiesContinuingOperations'],
|
|
3192
|
+
researchanddevelopment: ['ResearchAndDevelopmentExpense', 'ResearchAndDevelopmentExpenseExcludingAcquiredInProcessCost'],
|
|
3193
|
+
researchanddevelopmentexpense: ['ResearchAndDevelopmentExpense', 'ResearchAndDevelopmentExpenseExcludingAcquiredInProcessCost'],
|
|
3194
|
+
sga: ['SellingGeneralAndAdministrativeExpense'],
|
|
3195
|
+
sellinggeneralandadministrative: ['SellingGeneralAndAdministrativeExpense'],
|
|
3196
|
+
costofrevenue: ['CostOfRevenue', 'CostOfGoodsAndServicesSold', 'CostOfGoodsSold'],
|
|
3197
|
+
costofgoodssold: ['CostOfGoodsAndServicesSold', 'CostOfGoodsSold', 'CostOfRevenue'],
|
|
3198
|
+
cogs: ['CostOfGoodsAndServicesSold', 'CostOfGoodsSold', 'CostOfRevenue'],
|
|
3199
|
+
dilutedshares: ['WeightedAverageNumberOfDilutedSharesOutstanding'],
|
|
3200
|
+
weightedaveragedilutedshares: ['WeightedAverageNumberOfDilutedSharesOutstanding'],
|
|
3201
|
+
basicshares: ['WeightedAverageNumberOfSharesOutstandingBasic'],
|
|
3202
|
+
weightedaveragebasicshares: ['WeightedAverageNumberOfSharesOutstandingBasic'],
|
|
3203
|
+
publicfloat: ['EntityPublicFloat'],
|
|
3168
3204
|
cash: [
|
|
3169
3205
|
'CashAndCashEquivalentsAtCarryingValue',
|
|
3170
3206
|
'Cash',
|
|
@@ -3419,7 +3455,7 @@ async function companyConcept(
|
|
|
3419
3455
|
const tryNamespace = async (ns: string, tags: string[]) => {
|
|
3420
3456
|
for (const candidate of tags) {
|
|
3421
3457
|
const r = await pwFetch(
|
|
3422
|
-
`${DATA_BASE}/api/xbrl/companyconcept/CIK${paddedCik}/${ns}/${encodeURIComponent(candidate)}.json`,
|
|
3458
|
+
`${DATA_BASE}/api/xbrl/companyconcept/CIK${paddedCik}/${namespaceForTag(ns, candidate)}/${encodeURIComponent(candidate)}.json`,
|
|
3423
3459
|
{ headers: SEC_HEADERS },
|
|
3424
3460
|
);
|
|
3425
3461
|
if (!r.ok) { lastStatus = r.status; continue; }
|
|
@@ -3458,12 +3494,18 @@ async function companyConcept(
|
|
|
3458
3494
|
if (!factsRes.ok) {
|
|
3459
3495
|
lastStatus = factsRes.status;
|
|
3460
3496
|
} else if (tokens.length) {
|
|
3461
|
-
|
|
3497
|
+
type FactsNs = Record<string, { label?: string; description?: string; units: Record<string, ConceptDoc['units'][string]> }>;
|
|
3498
|
+
const facts = (await factsRes.json()) as { entityName?: string; facts?: { 'us-gaap'?: FactsNs; dei?: FactsNs } };
|
|
3462
3499
|
resolvedEntityName = facts.entityName ?? '';
|
|
3463
|
-
|
|
3500
|
+
// Scan dei too: cover-page facts (shares outstanding, public float) are
|
|
3501
|
+
// reported there and nowhere in us-gaap for many filers.
|
|
3502
|
+
const scanNamespaces: [string, FactsNs][] = [
|
|
3503
|
+
['us-gaap', facts.facts?.['us-gaap'] ?? {}],
|
|
3504
|
+
['dei', facts.facts?.dei ?? {}],
|
|
3505
|
+
];
|
|
3464
3506
|
const FLOW = /^(ProceedsFrom|RepaymentsOf|PaymentsOf|IncreaseDecrease|AmortizationOf|Gain|Loss)/;
|
|
3465
3507
|
const matches: { isFlow: boolean; latestEnd: string; doc: ConceptDoc }[] = [];
|
|
3466
|
-
for (const [tag, entry] of Object.entries(
|
|
3508
|
+
for (const [scanNs, nsFacts] of scanNamespaces) for (const [tag, entry] of Object.entries(nsFacts)) {
|
|
3467
3509
|
if (!tokens.some((t) => norm(tag).includes(t))) continue;
|
|
3468
3510
|
let latestEnd = '';
|
|
3469
3511
|
for (const [, values] of unitEntries(entry.units)) {
|
|
@@ -3475,7 +3517,7 @@ async function companyConcept(
|
|
|
3475
3517
|
matches.push({
|
|
3476
3518
|
isFlow: FLOW.test(tag),
|
|
3477
3519
|
latestEnd,
|
|
3478
|
-
doc: { cik: Number(cik), entityName: facts.entityName ?? '', tag, taxonomy:
|
|
3520
|
+
doc: { cik: Number(cik), entityName: facts.entityName ?? '', tag, taxonomy: scanNs, label: entry.label ?? tag, description: entry.description ?? '', units: entry.units },
|
|
3479
3521
|
});
|
|
3480
3522
|
}
|
|
3481
3523
|
matches.sort((a, b) => Number(a.isFlow) - Number(b.isFlow) || (b.latestEnd ?? '').localeCompare(a.latestEnd ?? ''));
|
|
@@ -3556,7 +3598,7 @@ async function companyConcept(
|
|
|
3556
3598
|
reported: false,
|
|
3557
3599
|
latest: null,
|
|
3558
3600
|
values: [],
|
|
3559
|
-
note: `This filer reports no XBRL tag matching "${concept}" under any recognized name — checked us-gaap candidates ${JSON.stringify(candidates)}, IFRS, and a full company-facts name scan. This means EITHER the company genuinely has none (common for e.g. long-term debt at equity-funded filers) OR it reports the item under a tag we didn't match (e.g. convertible/other debt lines). value is null, not zero — call edgar_company_facts({cik:"${cikOrTicker}"}) to see the exact tags this filer uses before treating it as absent.`,
|
|
3601
|
+
note: `This filer reports no XBRL tag matching "${concept}" under any recognized name — checked us-gaap/dei candidates ${JSON.stringify(candidates)}, IFRS, and a full company-facts name scan (us-gaap and dei). This means EITHER the company genuinely has none (common for e.g. long-term debt at equity-funded filers) OR it reports the item under a tag we didn't match (e.g. convertible/other debt lines). value is null, not zero — call edgar_company_facts({cik:"${cikOrTicker}"}) to see the exact tags this filer uses before treating it as absent.`,
|
|
3560
3602
|
};
|
|
3561
3603
|
}
|
|
3562
3604
|
found.sort((a, b) => (b.latestEnd ?? '').localeCompare(a.latestEnd ?? ''));
|
|
@@ -3611,6 +3653,17 @@ async function companyConcept(
|
|
|
3611
3653
|
value: entry.val,
|
|
3612
3654
|
form: entry.form, // 10-K / 10-Q / 20-F / 40-F (+ /A amendments)
|
|
3613
3655
|
filed: entry.filed,
|
|
3656
|
+
// Which filing this exact number came out of (fleet #3178). SEC's
|
|
3657
|
+
// companyconcept payload carries `accn` on every fact and we were
|
|
3658
|
+
// dropping it, so a caller holding a value and a period_end had no way
|
|
3659
|
+
// back to the document without a second edgar_company_filings call and
|
|
3660
|
+
// a date match — and a consumer that FANS OUT over this tool
|
|
3661
|
+
// (entity_profile's fundamentals) could not cite its own figures at all.
|
|
3662
|
+
// Reported rows only: a derived Q4/standalone-quarter row below is
|
|
3663
|
+
// arithmetic over several filings, not a fact any one of them states, so
|
|
3664
|
+
// it carries `accession: undefined` rather than the accession of
|
|
3665
|
+
// whichever leg happened to be the minuend.
|
|
3666
|
+
accession: entry.accn as string | undefined,
|
|
3614
3667
|
unit,
|
|
3615
3668
|
derived: false as boolean | undefined,
|
|
3616
3669
|
note: undefined as string | undefined,
|
|
@@ -3660,6 +3713,7 @@ async function companyConcept(
|
|
|
3660
3713
|
value: fyRow.value - qSum,
|
|
3661
3714
|
form: 'derived',
|
|
3662
3715
|
filed: fyRow.filed,
|
|
3716
|
+
accession: undefined, // arithmetic over FY + Q1-Q3, not a fact one filing states
|
|
3663
3717
|
unit: fyRow.unit,
|
|
3664
3718
|
derived: true,
|
|
3665
3719
|
note: 'Computed as FY minus Q1-Q3 — SEC filers do not report a standalone Q4 fact.',
|
|
@@ -3724,6 +3778,7 @@ async function companyConcept(
|
|
|
3724
3778
|
value: q2ytd.value - q1.value,
|
|
3725
3779
|
form: 'derived',
|
|
3726
3780
|
filed: q2ytd.filed,
|
|
3781
|
+
accession: undefined, // YTD(Q2) minus Q1 — spans two filings
|
|
3727
3782
|
unit: q2ytd.unit,
|
|
3728
3783
|
derived: true,
|
|
3729
3784
|
note: `Computed as the YTD figure filed for Q2 (${q2ytd.value}, covering ${q2ytd.period_start}..${q2ytd.period_end}) minus Q1 (${q1.value}) — this filer reports this concept cumulatively in its 10-Q (a cash-flow-statement concept, typically), not quarter-only.`,
|
|
@@ -3743,6 +3798,7 @@ async function companyConcept(
|
|
|
3743
3798
|
value: q3ytd.value - q2ytd.value,
|
|
3744
3799
|
form: 'derived',
|
|
3745
3800
|
filed: q3ytd.filed,
|
|
3801
|
+
accession: undefined, // YTD(Q3) minus YTD(Q2) — spans two filings
|
|
3746
3802
|
unit: q3ytd.unit,
|
|
3747
3803
|
derived: true,
|
|
3748
3804
|
note: `Computed as the YTD figure filed for Q3 (${q3ytd.value}) minus the YTD figure filed for Q2 (${q2ytd.value}) — this filer reports this concept cumulatively in its 10-Q, not quarter-only.`,
|
|
@@ -3843,8 +3899,18 @@ async function companyConcept(
|
|
|
3843
3899
|
// neither the caller nor any automatic check can tell it apart". A
|
|
3844
3900
|
// substituted tag is the same shape, and gets the same treatment.
|
|
3845
3901
|
const requested = concept.trim();
|
|
3846
|
-
|
|
3847
|
-
|
|
3902
|
+
// A friendly name the tool itself documents ("shares outstanding", "revenue")
|
|
3903
|
+
// resolving to one of ITS OWN listed tags is the advertised behaviour, not a
|
|
3904
|
+
// substitution — the warning below is for the fuzzy name-scan path, where a
|
|
3905
|
+
// narrower tag really can stand in for a broader concept. Until 2026-10-16
|
|
3906
|
+
// "shares outstanding" → CommonStockSharesOutstanding carried the full "NOT
|
|
3907
|
+
// interchangeable" warning on a correct answer.
|
|
3908
|
+
const requestedKey = requested.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
3909
|
+
const aliasServed = (CONCEPT_CANDIDATES[requestedKey] ?? []).some(
|
|
3910
|
+
(t) => t.toLowerCase() === data.tag.toLowerCase(),
|
|
3911
|
+
);
|
|
3912
|
+
const substituted = requested.length > 0 && !aliasServed
|
|
3913
|
+
&& data.tag.toLowerCase().replace(/[^a-z0-9]/g, '') !== requestedKey;
|
|
3848
3914
|
|
|
3849
3915
|
// Coverage-bound disclosure (fleet #693). A returned value can be genuinely
|
|
3850
3916
|
// CURRENT for this exact XBRL tag while a newer periodic filing exists that
|