@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 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 1807+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
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 1807+ data sources. The
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.10",
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-c6ffe46614ee14db9190ceb27485537bf8164b74f6f5768addd234dad2987dff",
30
- "sourceCommit": "8dbe088fb6f9d2c88f934173742329b4e20a67d6"
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.10",
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
- const facts = (await factsRes.json()) as { entityName?: string; facts?: { 'us-gaap'?: Record<string, { label?: string; description?: string; units: Record<string, ConceptDoc['units'][string]> }> } };
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
- const gaap = facts.facts?.['us-gaap'] ?? {};
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(gaap)) {
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: 'us-gaap', label: entry.label ?? tag, description: entry.description ?? '', units: entry.units },
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
- const substituted = requested.length > 0
3847
- && data.tag.toLowerCase().replace(/[^a-z0-9]/g, '') !== requested.toLowerCase().replace(/[^a-z0-9]/g, '');
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
package/src/server.ts CHANGED
@@ -10,7 +10,7 @@ import pack from './index.js';
10
10
 
11
11
 
12
12
  const server = new Server(
13
- { name: '@pipeworx/mcp-edgar', version: '0.3.10' },
13
+ { name: '@pipeworx/mcp-edgar', version: '0.3.11' },
14
14
  { capabilities: { tools: {} } },
15
15
  );
16
16