@cyanheads/secedgar-mcp-server 0.15.7 → 0.15.8
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/AGENTS.md +18 -10
- package/CLAUDE.md +18 -10
- package/README.md +112 -162
- package/changelog/0.15.x/0.15.8.md +29 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +1 -1
- package/dist/config/server-config.js.map +1 -1
- package/dist/mcp-server/tools/definitions/company-search.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/company-search.tool.js +15 -26
- package/dist/mcp-server/tools/definitions/company-search.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/dataframe-query.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/dataframe-query.tool.js +7 -1
- package/dist/mcp-server/tools/definitions/dataframe-query.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-beneficial-owners.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/get-beneficial-owners.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-filing.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-filing.tool.js +16 -11
- package/dist/mcp-server/tools/definitions/get-filing.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-insider-transactions.tool.d.ts +5 -2
- package/dist/mcp-server/tools/definitions/get-insider-transactions.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-insider-transactions.tool.js +151 -28
- package/dist/mcp-server/tools/definitions/get-insider-transactions.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-institutional-holdings.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/get-institutional-holdings.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-institutional-holdings.tool.js +89 -41
- package/dist/mcp-server/tools/definitions/get-institutional-holdings.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-material-events.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-material-events.tool.js +29 -33
- package/dist/mcp-server/tools/definitions/get-material-events.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +6 -3
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-filings.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-filings.tool.js +12 -20
- package/dist/mcp-server/tools/definitions/search-filings.tool.js.map +1 -1
- package/dist/services/edgar/edgar-api-service.d.ts +53 -41
- package/dist/services/edgar/edgar-api-service.d.ts.map +1 -1
- package/dist/services/edgar/edgar-api-service.js +213 -144
- package/dist/services/edgar/edgar-api-service.js.map +1 -1
- package/dist/services/edgar/filing-headers.d.ts +37 -9
- package/dist/services/edgar/filing-headers.d.ts.map +1 -1
- package/dist/services/edgar/filing-headers.js +40 -11
- package/dist/services/edgar/filing-headers.js.map +1 -1
- package/dist/services/edgar/filing-to-text.d.ts.map +1 -1
- package/dist/services/edgar/filing-to-text.js +24 -2
- package/dist/services/edgar/filing-to-text.js.map +1 -1
- package/dist/services/edgar/mirror/tickers-sync.d.ts +4 -2
- package/dist/services/edgar/mirror/tickers-sync.d.ts.map +1 -1
- package/dist/services/edgar/mirror/tickers-sync.js +4 -2
- package/dist/services/edgar/mirror/tickers-sync.js.map +1 -1
- package/dist/services/edgar/mirror/types.d.ts +6 -2
- package/dist/services/edgar/mirror/types.d.ts.map +1 -1
- package/dist/services/edgar/ownership-parser.d.ts +16 -0
- package/dist/services/edgar/ownership-parser.d.ts.map +1 -1
- package/dist/services/edgar/ownership-parser.js +19 -0
- package/dist/services/edgar/ownership-parser.js.map +1 -1
- package/dist/services/edgar/submissions-archive.d.ts +78 -0
- package/dist/services/edgar/submissions-archive.d.ts.map +1 -0
- package/dist/services/edgar/submissions-archive.js +87 -0
- package/dist/services/edgar/submissions-archive.js.map +1 -0
- package/package.json +1 -1
- package/server.json +3 -3
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<div align="center">
|
|
15
15
|
|
|
16
|
-
[](https://github.com/cyanheads/secedgar-mcp-server/releases/latest/download/secedgar-mcp-server.mcpb) [](https://cursor.com/en/install-mcp?name=secedgar-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvc2VjZWRnYXItbWNwLXNlcnZlciJdLCJlbnYiOnsiRURHQVJfVVNFUl9BR0VOVCI6IllvdXJOYW1lIHlvdXItZW1haWxAZXhhbXBsZS5jb20ifX0=) [](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22secedgar-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads
|
|
16
|
+
[](https://github.com/cyanheads/secedgar-mcp-server/releases/latest/download/secedgar-mcp-server.mcpb) [](https://cursor.com/en/install-mcp?name=secedgar-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvc2VjZWRnYXItbWNwLXNlcnZlciJdLCJlbnYiOnsiRURHQVJfVVNFUl9BR0VOVCI6IllvdXJOYW1lIHlvdXItZW1haWxAZXhhbXBsZS5jb20ifX0=) [](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22secedgar-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads%2Fsecedgar-mcp-server%22%5D%2C%22env%22%3A%7B%22EDGAR_USER_AGENT%22%3A%22YourName%20your-email%40example.com%22%7D%7D)
|
|
17
17
|
|
|
18
18
|
[](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
19
19
|
|
|
@@ -29,245 +29,195 @@
|
|
|
29
29
|
|
|
30
30
|
## Overview
|
|
31
31
|
|
|
32
|
-
SEC EDGAR filings, XBRL financials, and
|
|
32
|
+
SEC EDGAR filings, XBRL financials, and ownership data. No API key needed, only the User-Agent header SEC requires. Resolve companies by ticker, name, or CIK, search filings back to 1993, pull XBRL financials and cross-company comparisons by concept, and trace ownership through insider transactions, 13F holdings, 13D/13G blockholders, and fund portfolios. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
|
|
33
33
|
|
|
34
34
|
### Tools
|
|
35
35
|
|
|
36
36
|
| Tool | Description |
|
|
37
37
|
|:---|:---|
|
|
38
38
|
| `secedgar_company_search` | Find companies and retrieve entity info with optional recent filings |
|
|
39
|
-
| `secedgar_search_filings` | Search EDGAR filings since 1993
|
|
40
|
-
| `secedgar_get_filing` | Fetch a
|
|
41
|
-
| `secedgar_get_financials` | Get historical XBRL financial data for
|
|
42
|
-
| `secedgar_get_snapshot` | One-call financial profile
|
|
43
|
-
| `secedgar_get_material_events` | 8-K filings with item codes decoded and filterable
|
|
44
|
-
| `secedgar_get_insider_transactions` | Form 4 / 4-A insider transactions
|
|
45
|
-
| `secedgar_get_institutional_holdings` | 13F-HR quarterly
|
|
46
|
-
| `secedgar_find_holders` | Reverse 13F lookup
|
|
47
|
-
| `secedgar_get_beneficial_owners` | 5%+ blockholders of an issuer
|
|
39
|
+
| `secedgar_search_filings` | Search EDGAR filings since 1993: full text from 2001, archive browse before that |
|
|
40
|
+
| `secedgar_get_filing` | Fetch a filing's metadata and document text, paged or by section |
|
|
41
|
+
| `secedgar_get_financials` | Get historical XBRL financial data for one company and concept |
|
|
42
|
+
| `secedgar_get_snapshot` | One-call financial profile: the latest value of every supported concept |
|
|
43
|
+
| `secedgar_get_material_events` | 8-K filings with item codes decoded and filterable |
|
|
44
|
+
| `secedgar_get_insider_transactions` | Form 4 / 4-A insider transactions parsed from ownership XML |
|
|
45
|
+
| `secedgar_get_institutional_holdings` | 13F-HR quarterly holdings of one institutional manager |
|
|
46
|
+
| `secedgar_find_holders` | Reverse 13F lookup: which managers reported holding an issuer |
|
|
47
|
+
| `secedgar_get_beneficial_owners` | 5%+ blockholders of an issuer from structured SCHEDULE 13D / 13G filings |
|
|
48
48
|
| `secedgar_get_fund_holdings` | ETF and mutual fund portfolio holdings from the quarterly NPORT-P report |
|
|
49
|
-
| `secedgar_fetch_frames` |
|
|
49
|
+
| `secedgar_fetch_frames` | One XBRL concept × one period across every reporting company |
|
|
50
50
|
| `secedgar_compare_companies` | Compare named companies across several concepts, aligned on calendar periods |
|
|
51
51
|
| `secedgar_search_concepts` | Discover supported XBRL concept names or reverse-lookup a raw tag |
|
|
52
52
|
| `secedgar_dataframe_describe` | List canvas dataframes with provenance, TTL, and schema |
|
|
53
53
|
| `secedgar_dataframe_query` | Run a single-statement SELECT across dataframes |
|
|
54
|
-
| `secedgar_dataframe_drop` | Drop a canvas dataframe by name
|
|
54
|
+
| `secedgar_dataframe_drop` | Drop a canvas dataframe by name; opt-in via `EDGAR_DATAFRAME_DROP_ENABLED=true` |
|
|
55
55
|
|
|
56
56
|
### Resources
|
|
57
57
|
|
|
58
58
|
| Resource | Description |
|
|
59
59
|
|:---|:---|
|
|
60
|
-
| `secedgar://concepts` |
|
|
61
|
-
| `secedgar://filing-types` | Common SEC filing types
|
|
60
|
+
| `secedgar://concepts` | XBRL financial concepts grouped by statement, mapping friendly names to XBRL tags |
|
|
61
|
+
| `secedgar://filing-types` | Common SEC filing types, plus the 8-K item-code tables for both numbering regimes |
|
|
62
|
+
|
|
63
|
+
`secedgar_search_concepts` serves the same concept catalog to tool-only clients.
|
|
62
64
|
|
|
63
65
|
### Prompts
|
|
64
66
|
|
|
65
67
|
| Prompt | Description |
|
|
66
68
|
|:---|:---|
|
|
67
|
-
| `secedgar_company_analysis` |
|
|
69
|
+
| `secedgar_company_analysis` | Structured analysis of a company's SEC filings: financial trends, risk factors, material events |
|
|
68
70
|
|
|
69
71
|
## Capability reference
|
|
70
72
|
|
|
71
73
|
### `secedgar_company_search` <sub>tool</sub>
|
|
72
74
|
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
- Near-match suggestions on a zero-result name or ticker search (e.g. `Microsfot` → `MICROSOFT CORP / MSFT`)
|
|
77
|
-
- Optional recent filings inline, filtered by `forms` (exact form match — list `10-K/A` to include amendments); `filed_after`/`filed_before` and under-filled form filters page into the older submissions archive, reaching filings past the ~1000-entry recent window — `history_scanned_through` reports the scan depth, and the full filtered history stages as `df_<id>` when it exceeds `filing_limit`
|
|
78
|
-
- Returns entity metadata — SIC code, exchanges, fiscal year end, state of incorporation
|
|
75
|
+
- `query` takes a ticker (equities, ETFs, and mutual funds; `BRK-B` or `BRK.B`), a current or former company name, or a CIK; `include_filings` (default on) adds up to `filing_limit` filings (1–50, default 10), filtered by exact `forms` match and `filed_after` / `filed_before`
|
|
76
|
+
- Returns SIC code, exchanges, fiscal year end, and state of incorporation, plus `series_id` / `class_id` for a fund ticker; fails as `no_match` (near matches in `data.suggestions`) or `multiple_matches`
|
|
77
|
+
- A date filter or an under-filled form filter scans past the recent window (the last year or 1,000 filings, whichever holds more) into the archive, up to 10 archive pages, and `history_scanned_through` reports how far it reached
|
|
79
78
|
|
|
80
79
|
---
|
|
81
80
|
|
|
82
81
|
### `secedgar_search_filings` <sub>tool</sub>
|
|
83
82
|
|
|
84
|
-
- Full-text
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
- A range crossing 2001-01-01 is split at the boundary and merged, each row tagged with its `source` (`efts`/`submissions`/`full-index`); `period_ending`, `ticker`, `file_description`, `sic`, and `location` exist only on `efts` rows
|
|
88
|
-
- Filing-date range (`filed_after` + `filed_before`, both inclusive, both required together) and form filtering (`forms`, amendments included), pagination up to 10,000 results; response includes form distribution for narrowing follow-up searches
|
|
89
|
-
- The full result set stages as `df_<id>` when it exceeds the inline limit
|
|
83
|
+
- Full-text `query` (phrases, `OR`, `-exclusion`, `wildcard*`, `ticker:` / `cik:` scoping), or browse by `forms` and/or entity with no query; `filed_after` and `filed_before` must be given together; `limit` up to 100, and `offset` (up to 9,999) pages server-side only under `sort: "relevance"` on a 2001-onward search
|
|
84
|
+
- Full text covers 2001 onward. Earlier ranges, back to 1993, come from the archives, and pre-2001 free text needs `ticker:` / `cik:` scope and reads up to 50 documents (`scan` reports candidates, scanned, and matched)
|
|
85
|
+
- A range crossing 2001-01-01 is split and merged, each row tagged with `source` (`efts`, `submissions`, `full-index`); the response carries `total`, `total_is_exact`, and `form_distribution`
|
|
90
86
|
|
|
91
87
|
---
|
|
92
88
|
|
|
93
89
|
### `secedgar_get_filing` <sub>tool</sub>
|
|
94
90
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
- Offset paging for large documents (10-K, S-1/A can exceed 1M chars) — pass a truncated response's `next_offset` as `offset` to continue; first-page truncated responses include a detected `outline` (headings + offsets)
|
|
100
|
-
- `section` jumps directly to a named heading by substring match ignoring case, whitespace style, and quote style (`"risk factors"`, `"item 7"`) — a miss returns the detected outline; extracted text is cached per `accession + document` (bounded LRU, 8 entries) so paged calls are cheap
|
|
91
|
+
- `accession_number` in dash or 18-digit form, optional `cik` to speed the lookup, `document` for an exhibit, `include_xbrl` for XBRL artifacts; `content_limit` 1,000–200,000 characters per page (default 50,000)
|
|
92
|
+
- Page with `offset` / `next_offset` until `content_truncated` is false, or jump with `section` (substring match on detected headings); the first page of a truncated document carries an `outline` of up to 50 headings with offsets
|
|
93
|
+
- `documents` splits the filing into primary, exhibits, and auxiliary; entries marked `binary` (scans, PDFs, archives) fail as `binary_document`, and a `section` miss returns `section_not_found` with the outline
|
|
94
|
+
- `form`, `filing_date`, and `period_ending` come from the company's submissions feed for a recent filing and from the filing's own SEC header for an older one
|
|
101
95
|
|
|
102
96
|
---
|
|
103
97
|
|
|
104
98
|
### `secedgar_get_financials` <sub>tool</sub>
|
|
105
99
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
- Optional `limit` caps the inline series to the most-recent N periods; the full series stays queryable via `df_<id>`
|
|
109
|
-
- `caveats` names every calendar quarter absent from the frame-tagged series — SEC reports fiscal Q4 as the 10-K residual, so the calendar quarter it spans carries no discrete quarterly value (calendar-year filers included), and a filer whose other fiscal quarters span non-calendar durations can lose a second quarter the same way
|
|
110
|
-
- A separate `caveats` entry appears when the concept resolved to an XBRL tag SEC has retired from the taxonomy — that only happens when no current tag reports for the filer, and the series can then stop years short
|
|
100
|
+
- `company` (ticker or CIK) plus `concept` as a friendly name or raw XBRL tag; `taxonomy` `us-gaap` (default), `ifrs-full`, or `dei`; `period_type` `annual`, `quarterly`, or `all`, defaulting to annual with a fallback to the full series for instant concepts; `limit` 1–100 trims the inline series
|
|
101
|
+
- A deduplicated series, newest first, one value per calendar period with its source `form`, `filed` date, and `accession_number`; `tags_tried` names the tags walked, and an empty result fails as `no_concept_data`, `no_frame_data`, or `no_period_data`
|
|
111
102
|
|
|
112
103
|
---
|
|
113
104
|
|
|
114
105
|
### `secedgar_get_snapshot` <sub>tool</sub>
|
|
115
106
|
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
- Duration concepts (income statement, cash flow, per-share) report their latest full year and latest single quarter; balance-sheet and entity-info concepts report their latest point-in-time value
|
|
119
|
-
- Concepts the filer does not report are listed under `gaps` with the XBRL tags that were tried — never zero-filled or interpolated
|
|
120
|
-
- IFRS filers resolve through the mapped IFRS tag variants via `taxonomy: "ifrs-full"`, covering the income statement, balance sheet, cash flow, and per-share concepts; each line reports the taxonomy its value came from
|
|
121
|
-
- Compact single-record profile, no dataframe — reach for `secedgar_get_financials` when a time series is needed
|
|
107
|
+
- `company`, `taxonomy` `us-gaap` (default) or `ifrs-full`, and `period_type` `annual`, `quarterly`, or `both` (default); one companyfacts read covers every supported concept, and nothing is staged as a dataframe
|
|
108
|
+
- Each `lines` entry reports the latest `annual` and `quarterly` value for a duration concept, or the latest `instant` value for balance-sheet and entity-info concepts, with the `tag` and `taxonomy` it came from; concepts the filer doesn't report land in `gaps` with `tags_tried`
|
|
122
109
|
|
|
123
110
|
---
|
|
124
111
|
|
|
125
112
|
### `secedgar_get_material_events` <sub>tool</sub>
|
|
126
113
|
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
- A date window pages into the older submissions archive, reaching 8-K filings older than the ~1000-filing recent window; `history_scanned_through` discloses the scan depth
|
|
131
|
-
- The full decode table is in the `secedgar://filing-types` resource
|
|
132
|
-
- The full filtered set materializes as `df_<id>` with item codes on every row — item frequency over time is one `secedgar_dataframe_query` away
|
|
114
|
+
- `company` plus up to 20 `items` codes, dotted (`2.02`) since 2004-08-23 and single integers (`12`) before; the two regimes don't overlap, so pair them across the changeover; `filed_after` / `filed_before` work alone and reach into the archive; `limit` 1–100 (default 20)
|
|
115
|
+
- Each filing decodes its `items` to `code`, `label`, and `regime` (`current` / `legacy`); `item_distribution` counts every code in the window before the filter, and `total_8k_scanned` against `total_matched` shows what the filter removed
|
|
116
|
+
- A date window reads every archive page overlapping it, up to 10; without one, the archive is read only to fill `limit`, stopping on the page that fills it; `history_scanned_through` and `dataset.truncated` report what went unread
|
|
133
117
|
|
|
134
118
|
---
|
|
135
119
|
|
|
136
120
|
### `secedgar_get_insider_transactions` <sub>tool</sub>
|
|
137
121
|
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
- Transaction code mapped to a readable type (purchase, sale, gift, award, exercise, …); shares signed by acquired/disposed, price per share, and shares owned after each transaction; covers non-derivative (open-market) and derivative (option/RSU) lines
|
|
142
|
-
- Filter by `transaction_type` (`purchase`, `sale`, `all`); scans newest filings first
|
|
143
|
-
- The full set parsed from the scanned recent filings materializes as `df_<id>` (the inline list is a preview capped at `limit`) — query it to aggregate net buy/sell by insider
|
|
122
|
+
- `company` is the issuer; `transaction_type` `purchase` (code P), `sale` (code S), or `all` (default); `limit` 1–100 (default 20); does not cover Forms 3 or 5
|
|
123
|
+
- Without a date window it scans up to 100 of the newest Form 4 / 4-A filings; `filed_after` / `filed_before` (inclusive, either alone) read any period since mid-2003, paging into the archive (up to 10 pages) when the window predates the recent submissions window, and with a canvas every in-window filing is parsed, up to 100; `history_scanned_through` names the oldest filing parsed
|
|
124
|
+
- Each transaction carries the reporting person, relationship, `transaction_code` and `transaction_type`, `is_derivative`, unsigned `shares_traded` with `direction` (`acquire` / `dispose`), price per share, and shares owned after; `dataset.truncated` flags Form 4 filings beyond those parsed
|
|
144
125
|
|
|
145
126
|
---
|
|
146
127
|
|
|
147
128
|
### `secedgar_get_institutional_holdings` <sub>tool</sub>
|
|
148
129
|
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
- Resolves the filing-manager name and reporting quarter from the cover page; target a specific quarter with `quarter` (e.g. `"2025-Q4"`)
|
|
153
|
-
- `total_holdings_in_filing` counts raw info-table rows, `total_positions` counts distinct positions after consolidation (both before `limit`); page with `offset`, which returns `next_offset` while rows remain
|
|
154
|
-
- The full parsed holdings set materializes as `df_<id>` for full-filing aggregation or cross-quarter joins on `cusip` + `reporting_period`
|
|
130
|
+
- `company` is the 13F filer (a CIK is most reliable), not a portfolio company, which is `secedgar_find_holders`' job; `quarter` as `YYYY-QN`, defaulting to the newest filing; `limit` 1–500 (default 20) with `offset` / `next_offset`; `consolidate` (default true) merges sub-lines into positions sorted by value
|
|
131
|
+
- A `quarter` older than the recent submissions window is found in the archive, read forward from the quarter end (up to 10 pages); a quarter the manager covered with a 13F-NT notice — or, with no `quarter`, a manager whose recent filings are notices only — fails as `no_filings_found` naming the notice's accession number and period
|
|
132
|
+
- Holdings carry issuer, CUSIP, `market_value_usd` in whole USD, shares or principal, and `put_call`; `total_holdings_in_filing` counts raw rows and `total_positions` distinct positions; a shared legal name fails as `ambiguous_entity`
|
|
155
133
|
|
|
156
134
|
---
|
|
157
135
|
|
|
158
136
|
### `secedgar_find_holders` <sub>tool</sub>
|
|
159
137
|
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
- `quarter` targets a reporting period (`"2026-Q1"`); omit it for the newest quarter whose 45-day filing deadline has passed — the applied quarter and its filing window are echoed back
|
|
163
|
-
- Filings are kept by the period they report, not the date they were filed, so amendments restating an older quarter (roughly 6% of any window) don't land in the wrong quarter's holder list
|
|
164
|
-
- Up to 500 filer rows are fetched per call; `total_filings` reports the full count and `dataset.truncated` flags when more exist
|
|
165
|
-
- **The list is unranked** — EDGAR search relevance carries no signal about position size; read a manager's actual position by passing its `filer_cik` to `secedgar_get_institutional_holdings`
|
|
138
|
+
- `issuer` as a ticker, CIK, or name, plus an optional 9-character `cusip`, the precise match key (a name phrase-match both over- and under-matches); `quarter` as `YYYY-QN`, defaulting to the newest quarter past its 45-day filing deadline; `limit` 1–100 (default 20) from up to 500 fetched filings
|
|
139
|
+
- Rows carry `filer_cik`, `accession_number`, and `form`, while `search_mode` (`cusip` / `name`), `total_filings`, `fetched`, and `holders_in_quarter` size the result; the list is unranked, so pass a `filer_cik` to `secedgar_get_institutional_holdings` to read the position
|
|
166
140
|
|
|
167
141
|
---
|
|
168
142
|
|
|
169
143
|
### `secedgar_get_beneficial_owners` <sub>tool</sub>
|
|
170
144
|
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
- Coverage starts **2024-12-18**, when SEC replaced the legacy `SC 13D` / `SC 13G` text filings with structured XML; earlier stakes are readable but not parseable, and `legacy_filings_before_coverage` reports how many the issuer has
|
|
175
|
-
- Amendments carry the current position and are included by default; `include_amendments=false` leaves only the filings that opened a position
|
|
176
|
-
- The full parsed set registers as `df_<id>`, one row per reporting person, so it joins the insider and 13F dataframes on issuer CIK
|
|
145
|
+
- `issuer` is the company being held; `form_kind` `all` (default), `13D`, or `13G`; `include_amendments` (default true); `limit` 1–20 filings (default 10), each a separate document fetch
|
|
146
|
+
- Each filing lists `reporting_persons` with voting power, dispositive power, and `percent_of_class` per person, which joint filers report for the same shares, so they don't sum; a 13D carries `purpose_of_transaction`, a 13G has none
|
|
147
|
+
- Coverage starts 2024-12-18 with the structured XML schedules; `legacy_filings_before_coverage` counts the issuer's older `SC 13D` / `SC 13G` text filings
|
|
177
148
|
|
|
178
149
|
---
|
|
179
150
|
|
|
180
151
|
### `secedgar_get_fund_holdings` <sub>tool</sub>
|
|
181
152
|
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
- Positions carry the security name, CUSIP/ISIN/LEI where the filer reports them, share balance, USD value, and percent of net assets, alongside fund-level net assets, total assets, and total liabilities
|
|
186
|
-
- Positions come back largest-first by percent of net assets, one page of `limit` rows from `offset`; the full report registers as `df_<id>` for aggregation and for joining the 13F and insider dataframes on CUSIP
|
|
153
|
+
- `fund` as a ticker (`VOO`), series ID (`S000002839`), or CIK; `series_id` picks one fund of a multi-series trust, which otherwise fails as `series_required` with the series listed; `report_date` targets a period from `available_report_periods`; `limit` 1–100 (default 20) with `offset` / `next_offset`
|
|
154
|
+
- Positions come largest first by `percent_of_net_assets`, with name, CUSIP / ISIN / LEI, `balance` and `units`, `value_usd`, and asset and issuer category, alongside fund net assets, total assets, and total liabilities
|
|
155
|
+
- Holdings are as of `report_period_date`, roughly two months before `filing_date`; `publication_lag_days` states the gap
|
|
187
156
|
|
|
188
157
|
---
|
|
189
158
|
|
|
190
159
|
### `secedgar_fetch_frames` <sub>tool</sub>
|
|
191
160
|
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
- The full frames response (all reporters, typically 2k–10k rows) materializes as `df_<id>`
|
|
196
|
-
- `related_tags` flags alternate-definition tags some filers use as their primary line (e.g. `cash` → restricted-cash-inclusive total, `equity` → NCI-inclusive total), so a whole-universe screen on the base tag isn't silently under-inclusive — query those separately
|
|
197
|
-
- One call hits one XBRL tag; when a friendly name maps to multiple same-meaning tags, `unqueried_tags` lists the others to query and combine with an analysis-specific priority
|
|
161
|
+
- `concept` as a friendly name or raw tag, `period` as `CY2023`, `CY2024Q2`, or `CY2023Q4I`, `unit` (default `USD`), `sort` `desc` / `asc`; `limit` 1–100 (default 25) with `offset` / `next_offset` down the ranking
|
|
162
|
+
- One call queries one tag: `unqueried_tags` lists same-meaning variants to fetch separately, and `related_tags` lists alternate-definition tags some filers report instead
|
|
163
|
+
- `value_distribution.max_to_p95_ratio` flags scale-factor outliers, `period_end_range` shows fiscal-year mixing, and `caveats` names the fiscal-Q4 gap in quarterly frames
|
|
198
164
|
|
|
199
165
|
---
|
|
200
166
|
|
|
201
167
|
### `secedgar_compare_companies` <sub>tool</sub>
|
|
202
168
|
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
- Balance-sheet and entity-info concepts align on the calendar year or quarter their point-in-time snapshot falls in, so they sit in the same matrix as income-statement lines; each cell keeps its underlying XBRL frame
|
|
206
|
-
- `periods` bounds the inline matrix (1-12, default 4), shrinking further when companies × concepts × periods is too large for one response; the full aligned series always materializes as `df_<id>`
|
|
207
|
-
- A company that fails to resolve is reported in `failed_companies` and the comparison proceeds with the rest; a company that does not report a concept is reported in `gaps` with the tags that were tried — never interpolated
|
|
208
|
-
- `caveats` surface a filer missing calendar quarters, a concept that resolved to a retired tag for one company, differing period ends inside one aligned period, and unit mismatches across companies
|
|
169
|
+
- 2–10 `companies` × 1–8 `concepts`; `taxonomy` `us-gaap` (default) or `ifrs-full`; `period_type` `annual` (default) or `quarterly`; `periods` 1–12 (default 4), trimmed further when the inline matrix gets too large
|
|
170
|
+
- `cells` align each value on a calendar `period` and keep its `frame` and `period_end`; `failed_companies` (reason `not_found`, `ambiguous`, or `no_company_facts`) and `gaps` report what's missing, and `caveats` flag differing period ends and unit mismatches
|
|
209
171
|
|
|
210
172
|
---
|
|
211
173
|
|
|
212
174
|
### `secedgar_search_concepts` <sub>tool</sub>
|
|
213
175
|
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
- Reverse-lookup raw tags like `NetIncomeLoss` to the supported friendly names
|
|
217
|
-
- Surfaces `related_tags` for concepts with a high-coverage alternate-definition tag (e.g. restricted-cash-inclusive cash) so callers can discover them before screening
|
|
218
|
-
- Filtering by `taxonomy: "ifrs-full"` narrows the catalog to concepts with an IFRS tag confirmed against live 20-F filers — a concept with no IFRS equivalent is left out rather than mapped to a guess
|
|
219
|
-
- Returns the same catalog used by `secedgar_get_financials`, `secedgar_fetch_frames`, and `secedgar://concepts`
|
|
176
|
+
- `search` is a substring over friendly name, label, and tags, so a raw tag like `NetIncomeLoss` reverse-maps to its friendly name; `group` and `taxonomy` filter; no arguments returns the full catalog
|
|
177
|
+
- Each concept lists `tags`, `ifrs_tags` (only where an IFRS element was confirmed in live 20-F filings), `related_tags`, `unit`, and `group`
|
|
220
178
|
|
|
221
179
|
---
|
|
222
180
|
|
|
223
181
|
### `secedgar_dataframe_describe` <sub>tool</sub>
|
|
224
182
|
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
- Each entry surfaces source tool, query parameters, creation/expiry timestamps, row count, column schema, and whether the dataframe is truncated relative to the upstream source
|
|
228
|
-
- Read the column schema here before writing SQL for `secedgar_dataframe_query`
|
|
183
|
+
- Optional `name` for one dataframe; omit it to list the tenant's active dataframes, newest first
|
|
184
|
+
- Each entry carries `source_tool`, `query_params`, `created_at` / `expires_at`, `row_count`, `truncated`, and the `column_schema` that SQL for `secedgar_dataframe_query` has to match
|
|
229
185
|
|
|
230
186
|
---
|
|
231
187
|
|
|
232
188
|
### `secedgar_dataframe_query` <sub>tool</sub>
|
|
233
189
|
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
-
|
|
237
|
-
- `register_as` persists the result as a new dataframe (`df_XXXXX_XXXXX`) with a fresh TTL, to chain analyses without re-running the source query
|
|
238
|
-
- BIGINT columns (XBRL `value`, COUNT/SUM results) serialize as JSON strings to preserve precision past 2^53 — cast to `DOUBLE` in projections for inline arithmetic
|
|
190
|
+
- One DuckDB SELECT in `sql` (joins, aggregates, window functions, CTEs); `row_limit` 1–10,000 (default 1,000), `preview` for fewer inline rows, and `register_as` (`df_XXXXX_XXXXX`) to save the result as a new dataframe
|
|
191
|
+
- Returns `columns`, `rows`, `row_count`, and `row_count_capped`, which when true means `row_count` is the cap, not a total; BIGINT columns serialize as strings
|
|
192
|
+
- Writes, DDL, file-reading functions, multiple statements, and system catalogs are rejected with typed reasons (`non_select_statement`, `denied_function`, `multi_statement`, `system_catalog_access`, and others)
|
|
239
193
|
|
|
240
194
|
---
|
|
241
195
|
|
|
242
196
|
### `secedgar_dataframe_drop` <sub>tool</sub>
|
|
243
197
|
|
|
244
|
-
-
|
|
245
|
-
-
|
|
246
|
-
- Off, the tool is registered through `disabledTool()`: absent from `tools/list` and uncallable, but shown on the HTTP landing page in a `disabled` group naming the reason and the flag that enables it
|
|
198
|
+
- `name` of the dataframe to drop; idempotent, returning `dropped: false` when nothing matched
|
|
199
|
+
- Off unless `EDGAR_DATAFRAME_DROP_ENABLED=true`; disabled, it is absent from `tools/list` and uncallable, but still listed on the HTTP landing page with the flag that enables it
|
|
247
200
|
|
|
248
201
|
---
|
|
249
202
|
|
|
250
203
|
### `secedgar://concepts` <sub>resource</sub>
|
|
251
204
|
|
|
252
|
-
-
|
|
253
|
-
-
|
|
205
|
+
- The friendly-name catalog grouped by statement, as `text/markdown`, with the us-gaap, IFRS, and alternate-definition tags for each concept
|
|
206
|
+
- The names are what `secedgar_get_financials`, `secedgar_compare_companies`, and `secedgar_fetch_frames` accept as concepts
|
|
254
207
|
|
|
255
208
|
---
|
|
256
209
|
|
|
257
210
|
### `secedgar://filing-types` <sub>resource</sub>
|
|
258
211
|
|
|
259
|
-
- Common SEC
|
|
260
|
-
- Includes the
|
|
261
|
-
- Helps choose the `forms` filter for `secedgar_search_filings` and `secedgar_company_search`, or the `items` filter for `secedgar_get_material_events`
|
|
212
|
+
- Common SEC forms with cadence and use cases, as `text/markdown`
|
|
213
|
+
- Includes the 8-K item-code tables for both numbering regimes, the vocabulary of `secedgar_get_material_events`' `items` filter
|
|
262
214
|
|
|
263
215
|
---
|
|
264
216
|
|
|
265
217
|
### `secedgar_company_analysis` <sub>prompt</sub>
|
|
266
218
|
|
|
267
|
-
- Arguments: `company`
|
|
268
|
-
-
|
|
269
|
-
- `focus_areas` mentioning insider, institutional, or blockholder/activist terms adds the matching ownership step — insider transactions (Form 4/4-A), institutional holdings (`secedgar_find_holders` then `secedgar_get_institutional_holdings`), or 5%+ blockholders (13D/13G) — the generic word "ownership" adds all three
|
|
270
|
-
- Returns a single user-role message carrying the numbered workflow and a findings template to fill in
|
|
219
|
+
- Arguments: `company` required; `focus_areas` optional free text
|
|
220
|
+
- Returns one user message with a numbered workflow (company search, financial trends, filing review, material events, peer comparison via `secedgar_fetch_frames`) and a findings template; insider, institutional, or blockholder terms in `focus_areas` add those ownership steps, and "ownership" adds all three
|
|
271
221
|
|
|
272
222
|
## Features
|
|
273
223
|
|
|
@@ -275,19 +225,18 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
|
|
|
275
225
|
|
|
276
226
|
EDGAR-specific:
|
|
277
227
|
|
|
278
|
-
- One
|
|
279
|
-
- CIK resolution from tickers (
|
|
280
|
-
- Friendly XBRL concept
|
|
281
|
-
-
|
|
282
|
-
- Opt-in local SQLite mirror of
|
|
228
|
+
- One process-wide queue paces SEC requests under the 10 req/s limit. A 429 is never retried: every SEC call is refused locally as `rate_limited` with a `retryAfter` countdown for `EDGAR_RATE_LIMIT_COOLDOWN_SECONDS`, then a single probe goes out. Reads served from the local mirror keep answering
|
|
229
|
+
- CIK resolution from tickers (fund tickers included), current and former company names, or raw CIKs, with corporate-suffix normalization and near-match suggestions on a miss
|
|
230
|
+
- Friendly XBRL concept names that handle historical tag changes. `secedgar_get_financials`, `secedgar_get_snapshot`, and `secedgar_compare_companies` share one frame dedup and tag priority, so their numbers agree, and each reports `caveats` for calendar quarters missing from the frame-tagged series (SEC files fiscal Q4 only as the 10-K residual) and for series that stop years short
|
|
231
|
+
- Filing documents converted from HTML to text, with heading detection and offset paging for oversized filings
|
|
232
|
+
- Opt-in local SQLite mirror of company tickers and XBRL company-facts (`EDGAR_MIRROR_ENABLED`) that serves CIK resolution and financials from disk
|
|
283
233
|
|
|
284
234
|
Agent-friendly output:
|
|
285
235
|
|
|
286
|
-
- In-conversation SQL
|
|
287
|
-
- Discriminated outputs
|
|
288
|
-
-
|
|
289
|
-
- One parameter name per concept
|
|
290
|
-
- Provenance on scan and staleness — `history_scanned_through`, `publication_lag_days`, and `dataset.truncated` let agents reason about scan depth, report lag, and completeness
|
|
236
|
+
- In-conversation SQL: any tool whose response carries a `dataset` field has staged its full result as a DuckDB dataframe (`df_<id>`), while the inline list stays capped at `limit`; inspect it with `secedgar_dataframe_describe`, then query it with `secedgar_dataframe_query`
|
|
237
|
+
- Discriminated outputs and explicit gaps: `source` on filing-search rows, `search_mode`, 8-K item `regime`, typed `failed_companies` reasons, and `gaps` with `tags_tried` in place of zero-filled values
|
|
238
|
+
- Completeness disclosure: `history_scanned_through`, `total_is_exact`, `publication_lag_days`, and `dataset.truncated` tell agents how deep a scan went and what it left out
|
|
239
|
+
- One parameter name per concept: `company`, `filed_after` / `filed_before`, and `forms` mean the same thing on every tool, and common alternate spellings (`ticker`, `cik`, `start_date`, `end_date`, `form_types`, and others) are accepted as aliases
|
|
291
240
|
|
|
292
241
|
## Getting started
|
|
293
242
|
|
|
@@ -372,8 +321,8 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
|
|
|
372
321
|
|
|
373
322
|
### Prerequisites
|
|
374
323
|
|
|
375
|
-
- [Bun v1.
|
|
376
|
-
- A
|
|
324
|
+
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
325
|
+
- A User-Agent string in SEC's `"AppName contact@email.com"` format; no account or key required.
|
|
377
326
|
|
|
378
327
|
### Installation
|
|
379
328
|
|
|
@@ -395,34 +344,32 @@ cd secedgar-mcp-server
|
|
|
395
344
|
bun install
|
|
396
345
|
```
|
|
397
346
|
|
|
398
|
-
4. **
|
|
347
|
+
4. **Configure environment:**
|
|
399
348
|
|
|
400
349
|
```sh
|
|
401
|
-
|
|
350
|
+
cp .env.example .env
|
|
351
|
+
# edit .env and set EDGAR_USER_AGENT
|
|
402
352
|
```
|
|
403
353
|
|
|
404
354
|
## Configuration
|
|
405
355
|
|
|
406
|
-
All configuration is validated at startup via Zod schemas in `src/config/server-config.ts`. Key environment variables:
|
|
407
|
-
|
|
408
356
|
| Variable | Description | Default |
|
|
409
357
|
|:---|:---|:---|
|
|
410
|
-
| `EDGAR_USER_AGENT` | **Required.** User-Agent
|
|
411
|
-
| `EDGAR_RATE_LIMIT_RPS` | Max requests
|
|
412
|
-
| `EDGAR_RATE_LIMIT_COOLDOWN_SECONDS` | Seconds to
|
|
413
|
-
| `EDGAR_TICKER_CACHE_TTL` | Seconds to cache the company
|
|
414
|
-
| `EDGAR_DATASET_TTL_SECONDS` | Per-table TTL for canvas
|
|
415
|
-
| `EDGAR_DATAFRAME_DROP_ENABLED` | Set
|
|
416
|
-
| `EDGAR_MIRROR_ENABLED` | Enable the local SQLite mirror of
|
|
417
|
-
| `EDGAR_MIRROR_PATH` | Directory holding the mirror
|
|
418
|
-
| `EDGAR_MIRROR_REFRESH_CRON` |
|
|
419
|
-
| `EDGAR_MIRROR_FALLBACK_LIVE` |
|
|
420
|
-
| `CANVAS_PROVIDER_TYPE` | Canvas engine
|
|
421
|
-
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http
|
|
422
|
-
| `MCP_HTTP_PORT` | HTTP server port | `3010` |
|
|
423
|
-
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth
|
|
424
|
-
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.) | `info` |
|
|
425
|
-
| `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
|
|
358
|
+
| `EDGAR_USER_AGENT` | **Required.** User-Agent sent to SEC, as `"AppName contact@email.com"`. SEC blocks IPs without one. | — |
|
|
359
|
+
| `EDGAR_RATE_LIMIT_RPS` | Max requests per second to SEC (1–10). | `10` |
|
|
360
|
+
| `EDGAR_RATE_LIMIT_COOLDOWN_SECONDS` | Seconds to refuse calls locally after a 429 before one probe goes out. SEC lifts a block only after ten quiet minutes, so a shorter value probes into it. | `600` |
|
|
361
|
+
| `EDGAR_TICKER_CACHE_TTL` | Seconds to cache the company and fund ticker files. A failed fund-file load is retried after a minute (or the rate-limit cool-down) instead of standing for the whole TTL. | `3600` |
|
|
362
|
+
| `EDGAR_DATASET_TTL_SECONDS` | Per-table TTL for canvas dataframes, a sliding window renewed on every dataframe operation. | `86400` |
|
|
363
|
+
| `EDGAR_DATAFRAME_DROP_ENABLED` | Set `true` to expose `secedgar_dataframe_drop`, the only destructive tool. | `false` |
|
|
364
|
+
| `EDGAR_MIRROR_ENABLED` | Enable the local SQLite mirror of company tickers and XBRL company-facts. Node/Bun only; bootstrap once with `bun run mirror:init`. | `false` |
|
|
365
|
+
| `EDGAR_MIRROR_PATH` | Directory holding the mirror databases. | `./data/edgar-mirror` |
|
|
366
|
+
| `EDGAR_MIRROR_REFRESH_CRON` | In-process refresh cron (HTTP transport only), e.g. `0 9 * * *`. Omit to refresh with `bun run mirror:refresh`. | — |
|
|
367
|
+
| `EDGAR_MIRROR_FALLBACK_LIVE` | Fall back to the live SEC API on a mirror miss. Set `false` for mirror-only reads. | `true` |
|
|
368
|
+
| `CANVAS_PROVIDER_TYPE` | Canvas engine; `none` disables dataframes. | `duckdb` |
|
|
369
|
+
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
370
|
+
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
371
|
+
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
|
|
372
|
+
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
|
|
426
373
|
|
|
427
374
|
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
428
375
|
|
|
@@ -450,25 +397,28 @@ docker build -t secedgar-mcp-server .
|
|
|
450
397
|
docker run -e EDGAR_USER_AGENT="MyApp my@email.com" -p 3010:3010 secedgar-mcp-server
|
|
451
398
|
```
|
|
452
399
|
|
|
453
|
-
The image ships the mirror CLI, so
|
|
400
|
+
The image defaults to HTTP on port 3010 and ships the mirror CLI, so a running container can bootstrap and refresh its own mirror:
|
|
454
401
|
|
|
455
402
|
```sh
|
|
456
403
|
docker exec <container> bun run mirror:verify # sync status + sample reads
|
|
457
|
-
docker exec <container> bun run mirror:init # one-time bootstrap
|
|
458
|
-
docker exec <container> bun run mirror:refresh # re-ingest
|
|
404
|
+
docker exec <container> bun run mirror:init # one-time bootstrap from the SEC bulk archive
|
|
405
|
+
docker exec <container> bun run mirror:refresh # re-ingest after SEC rebuilds the archive
|
|
459
406
|
```
|
|
460
407
|
|
|
461
408
|
## Project structure
|
|
462
409
|
|
|
463
410
|
| Directory | Purpose |
|
|
464
411
|
|:---|:---|
|
|
465
|
-
| `src/
|
|
466
|
-
| `src/
|
|
467
|
-
| `src/mcp-server/
|
|
468
|
-
| `src/
|
|
469
|
-
| `src/
|
|
470
|
-
| `src/
|
|
471
|
-
| `
|
|
412
|
+
| `src/index.ts` | `createApp()` entry point: registers resources and prompts, starts the SEC client, canvas, and optional mirror. |
|
|
413
|
+
| `src/config` | Server environment variable parsing and validation with Zod. |
|
|
414
|
+
| `src/mcp-server/tools/definitions` | Tool definitions (`*.tool.ts`) and the `buildToolDefinitions()` registration list. |
|
|
415
|
+
| `src/mcp-server/resources/definitions` | Resource definitions: XBRL concepts and filing types. |
|
|
416
|
+
| `src/mcp-server/prompts/definitions` | Prompt definitions: company analysis. |
|
|
417
|
+
| `src/services/edgar` | Paced SEC client, CIK resolution, XBRL concept mapping and series dedup, ownership / 13D / 13G / NPORT-P parsers, 8-K item tables, HTML-to-text. |
|
|
418
|
+
| `src/services/edgar/mirror` | Opt-in local SQLite mirror of company tickers and XBRL company-facts. |
|
|
419
|
+
| `src/services/canvas-bridge` | Adapter over the framework `DataCanvas`: `df_<id>` naming, per-table TTL, system-catalog SQL deny. |
|
|
420
|
+
| `scripts` | Build, devcheck, and lint tooling, plus the `mirror:*` commands. |
|
|
421
|
+
| `tests` | Unit and integration tests, mirroring the `src/` structure. |
|
|
472
422
|
|
|
473
423
|
## Development guide
|
|
474
424
|
|
|
@@ -476,7 +426,7 @@ See [`CLAUDE.md`](./CLAUDE.md) and [`AGENTS.md`](./AGENTS.md) for development gu
|
|
|
476
426
|
|
|
477
427
|
- Handlers throw, framework catches — no `try/catch` in tool logic
|
|
478
428
|
- Use `ctx.log` for logging, `ctx.state` for storage
|
|
479
|
-
- Register new tools and resources in the `createApp()` arrays
|
|
429
|
+
- Register new tools in `buildToolDefinitions()` (`src/mcp-server/tools/definitions/index.ts`), and resources and prompts in the `createApp()` arrays in `src/index.ts`
|
|
480
430
|
- Wrap external SEC EDGAR calls: validate the raw response → normalize to a domain type → return the output schema; never fabricate a missing XBRL field — report it under `gaps` instead
|
|
481
431
|
|
|
482
432
|
## Contributing
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "secedgar_get_insider_transactions takes a filed_after / filed_before window reading Form 4 history back to mid-2003, and secedgar_get_institutional_holdings finds quarters older than the recent submissions window. Fixes fund-ticker resolution after a failed or mirror-served load, deeply nested filings overflowing the stack, and archive paging."
|
|
3
|
+
security: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.15.8 — 2026-09-24
|
|
7
|
+
|
|
8
|
+
## Added
|
|
9
|
+
|
|
10
|
+
- **`secedgar_get_insider_transactions` `filed_after` / `filed_before`** ([#127](https://github.com/cyanheads/secedgar-mcp-server/issues/127)) — inclusive, either alone, with the `start_date` / `date_from` / `end_date` / `date_to` aliases. A window reads the in-window Form 4 / 4-A filings from the recent window and the archive pages it overlaps, parsing up to 100 with a canvas. Only filings whose primary document is ownership XML are read, so the reachable history starts in mid-2003, when structured Form 4 filing began. `history_scanned_through` names the oldest filing parsed, and a window holding none returns a notice rather than `no_filings_found`.
|
|
11
|
+
|
|
12
|
+
## Changed
|
|
13
|
+
|
|
14
|
+
- **`secedgar_get_filing` fills `form`, `filing_date`, and `period_ending` for filings outside the recent submissions window** ([#126](https://github.com/cyanheads/secedgar-mcp-server/issues/126)) — read from the filing's own SEC header: the `index-headers.html` page the tool already fetches, or `<accession>.hdr.sgml` when that page is missing (one extra request, only then).
|
|
15
|
+
- **One archive-page walk for every tool that reads past the recent window** — `SubmissionsArchiveWalk` (`src/services/edgar/submissions-archive.ts`) serves `secedgar_company_search`, `secedgar_search_filings`' pre-2001 arm, `secedgar_get_material_events`, `secedgar_get_insider_transactions`, and `secedgar_get_institutional_holdings`, with one 10-page cap.
|
|
16
|
+
- Tool descriptions state the recent window as SEC sizes it — the last year or 1,000 filings, whichever holds more — instead of "~1,000 filings".
|
|
17
|
+
|
|
18
|
+
## Fixed
|
|
19
|
+
|
|
20
|
+
- **`secedgar_get_institutional_holdings` finds a `quarter` older than the recent window** ([#117](https://github.com/cyanheads/secedgar-mcp-server/issues/117)) — the archive is read forward from the quarter end, up to 10 pages, instead of failing as `no_filings_found`.
|
|
21
|
+
- **A quarter covered by a 13F-NT notice fails naming that notice** ([#133](https://github.com/cyanheads/secedgar-mcp-server/issues/133)) — the error carries its accession number, form, and period (`notice_accession_number`, `notice_form`, `notice_period`); a manager with no `quarter` whose recent 13F filings are all notices gets the same message instead of the not-a-13F-filer routing.
|
|
22
|
+
- **`filer_name` decodes XML entities** ([#132](https://github.com/cyanheads/secedgar-mcp-server/issues/132)) — `JPMORGAN CHASE & CO` is now `JPMORGAN CHASE & CO` on every surface.
|
|
23
|
+
- **A failed load of `company_tickers_mf.json` no longer stands for the whole ticker-cache TTL** ([#119](https://github.com/cyanheads/secedgar-mcp-server/issues/119)) — the fund slice is refetched once, single-flight, after 60 s (or the rate-limit cool-down, if longer), without refetching the equity index; each failed load logs a warning.
|
|
24
|
+
- **Fund tickers keep `seriesId` / `classId` on the mirror path** ([#135](https://github.com/cyanheads/secedgar-mcp-server/issues/135)) — a live fund entry supersedes the mirror's bare fund row for the same symbol, and mirror fund rows never enter the CIK index, so `secedgar_get_fund_holdings` routes a multi-series trust's ticker through its series.
|
|
25
|
+
- **`rate_limited` errors carry `data.retryable: true`** ([#122](https://github.com/cyanheads/secedgar-mcp-server/issues/122)) — both the upstream 429 and the local cool-down refusal, matching the declared contract, so the text ends `(reason rate_limited · retryable)`.
|
|
26
|
+
- **Deeply nested markup no longer overflows the stack in filing text conversion** ([#118](https://github.com/cyanheads/secedgar-mcp-server/issues/118)) — `html-to-text` stops at 512 levels and prints `[…]` there, and legacy SGML `<PAGE>` markers are replaced with line breaks before parsing; one such document no longer fails `secedgar_search_filings`' pre-2001 text scan.
|
|
27
|
+
- **`secedgar_get_material_events` stops the undated archive walk once `limit` is filled** ([#134](https://github.com/cyanheads/secedgar-mcp-server/issues/134)) — when the recent window under-filled `limit`, an undated call previously read every archive page up to the cap; a date window still reads every page it overlaps. `dataset.truncated` is true whenever selected pages went unread, including when the recent window alone fills `limit`.
|
|
28
|
+
- **Archive page selection tolerates a one-day drift in the manifest's `filingFrom` / `filingTo`** ([#137](https://github.com/cyanheads/secedgar-mcp-server/issues/137)) — a window whose bound falls on a page's first or last filing day no longer skips that page.
|
|
29
|
+
- **`secedgar_dataframe_query` renders a line break inside a table cell or column name as `<br>`** ([#121](https://github.com/cyanheads/secedgar-mcp-server/issues/121)) — it previously split the Markdown row.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;iBAiEtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAc9C"}
|