@cyanheads/secedgar-mcp-server
Provides SQL analytics over materialized dataframes using DuckDB, enabling queries, joins, and aggregations on SEC filing data.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@cyanheads/secedgar-mcp-serverGet financial data for Microsoft"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://secedgar.caseyjhand.com/mcp
Overview
SEC EDGAR filings, financials, and ownership data. Look up companies by ticker, name, or CIK, search filings back to 1993, compare financials across companies, and trace ownership through insider trades, 13F holdings, 13D/13G blockholders, and fund portfolios. No API key required.
Tools
Tool | Description |
| Find companies and retrieve entity info with optional recent filings |
| Search EDGAR filings since 1993: full text from 2001, archive browse before that |
| Fetch a filing's metadata and document text, paged or by section |
| Get historical XBRL financial data for one company and concept |
| One-call financial profile: the latest value of every supported concept |
| 8-K filings with item codes decoded and filterable |
| Form 4 / 4-A insider transactions parsed from ownership XML |
| 13F-HR quarterly holdings of one institutional manager |
| Reverse 13F lookup: which managers reported holding an issuer |
| 5%+ blockholders of an issuer from structured SCHEDULE 13D / 13G filings |
| ETF and mutual fund portfolio holdings from the quarterly NPORT-P report |
| One XBRL concept × one period across every reporting company |
| Compare named companies across several concepts, aligned on fiscal years or calendar quarters |
| Discover supported XBRL concept names or reverse-lookup a raw tag |
| List canvas dataframes with provenance, TTL, and schema |
| Run a single-statement SELECT across dataframes |
| Drop a canvas dataframe by name; opt-in via |
Resources
Resource | Description |
| XBRL financial concepts grouped by statement, mapping friendly names to XBRL tags |
| Common SEC filing types, plus the 8-K item-code tables for both numbering regimes |
secedgar_search_concepts serves the same concept catalog to tool-only clients.
Prompts
Prompt | Description |
| Structured analysis of a company's SEC filings: financial trends, risk factors, material events |
Related MCP server: northbridge-diligence
Capability reference
secedgar_company_search tool
querytakes a ticker (equities, ETFs, mutual funds), a current or former company name, or a CIK;include_filings(default on) adds up tofiling_limitfilings (default 10, at most 50 inline), filtered byforms,filed_after, andfiled_beforeReturns SIC code, exchanges, fiscal year end, and state of incorporation, plus
series_id/class_idfor a fund ticker; fails asno_match(with near matches indata.suggestions) ormultiple_matchesA date or form filter can reach into the archive (up to 10 pages);
history_scanned_throughreports how far it got
secedgar_search_filings tool
Full-text
query(phrases,OR,-exclusion,wildcard*,ticker:/cik:scoping), or browse byformsand/or entity with no query;filed_after/filed_beforework alone (a lonefiled_afterruns through today, a lonefiled_beforestarts at 2001-01-01, or at 1993-01-01 before 2001);limitup to 100,offsetup to 9,999 (only withsort: "relevance"on a 2001-onward search)Full text covers 2001 onward; earlier ranges back to 1993 come from the archives, where free text needs
ticker:/cik:scope and reads up to 50 documents (scanreports the counts)One row per filing, tagged with
source(efts,submissions,full-index); full-text rows listmatched_documents(filenames forsecedgar_get_filing); the response carriestotal,form_distribution,total_is_exact, andtotal_documentsfor full-text searches
secedgar_get_filing tool
accession_numberin dash or 18-digit form, optionalcikto speed the lookup,documentfor an exhibit,include_xbrlfor XBRL artifacts;content_limit1,000–200,000 characters per page (default 50,000)Page with
offset/next_offsetuntilcontent_truncatedis false, or jump withsection(a heading such asitem 1orpart i), then dropsectionto page on, since it outranksoffset; the first page of a truncated document carries anoutlineof up to 50 headings with offsetsdocumentssplits the filing into primary, exhibits, and auxiliary;binaryentries (scans, PDFs, archives) fail asbinary_document, and asectionmiss returnssection_not_foundwith the outlineIf the archive doesn't serve a filing's primary document (common in 2000–2001), the full submission
<accession>.txtis read instead and the notice says so
secedgar_get_financials tool
company(ticker or CIK) plusconceptas a friendly name or raw XBRL tag;taxonomyus-gaap(default),ifrs-full, ordei;period_typeannual,quarterly, orall;unitpicks one SEC unit key (ZAR,USD,USD/EUR) when a concept is reported in several;limit1–100 trims the inline seriesReturns a deduplicated series, newest first, one value per calendar period with
form,filed,accession_number, andtag, all in oneunit(other units are named incaveats);tags_triedlists the tags checked, and an empty result fails asno_concept_data,no_frame_data,no_period_data, orno_unit_dataA
conceptthat is neither a friendly name nor an UpperCamelCase tag fails asunknown_concept, with a formula for standard combinations (free_cash_flow,ebitda,working_capital) or up to three closest friendly names
secedgar_get_snapshot tool
company,taxonomyus-gaap(default) orifrs-full, andperiod_typeannual,quarterly, orboth(default); one companyfacts read covers every supported concept, and nothing is staged as a dataframeEach
linesentry reports the latestannualandquarterlyvalue for a duration concept, or the latestinstantvalue for balance-sheet and entity-info concepts, each with thetagit came from, under the line'staxonomy; concepts the filer doesn't report land ingapswithtags_tried
secedgar_get_material_events tool
companyplus up to 20itemscodes, 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_beforework alone and reach into the archive;limit1–100 (default 20)Each filing decodes its
itemstocode,label, andregime(current/legacy);item_distributioncounts every code in the window, andtotal_8k_scannedagainsttotal_matchedshows what the filter removedA date window reads up to 10 archive pages;
history_scanned_throughanddataset.truncatedreport what went unread
secedgar_get_insider_transactions tool
companyis the issuer;transaction_typepurchase(code P),sale(code S), orall(default);limit1–100 (default 20); does not cover Forms 3 or 5Without 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, up to 100 filings;history_scanned_throughnames the oldest filing parsedEach transaction carries the reporting person, relationship,
transaction_codeandtransaction_type,is_derivative, unsignedshares_tradedwithdirection(acquire/dispose), price per share, and shares owned after;dataset.truncatedflags filings beyond those parsed, andfilings_other_issuercounts Form 4s the company filed as a reporting owner of another issuer, which add no rows. Price text preserves the numeric digits in fixed-point notation, with at least two decimal places.
secedgar_get_institutional_holdings tool
companyis the 13F filer (a CIK is most reliable), not a portfolio company, which issecedgar_find_holders' job;quarterasYYYY-QN, defaulting to the newest filing;limit1–500 (default 20) withoffset/next_offset;consolidate(default true) merges sub-lines into positions sorted by valueOlder quarters are found in the archive (up to 10 pages); a quarter covered only by a 13F-NT notice fails as
no_filings_foundnaming the notice's accession number and periodHoldings and dataframe rows come from the original report.
amendmentsdiscloses same-period accessions, filing dates and cover-page types (UNAVAILABLEwith a reason when unreadable); read them withsecedgar_get_filing.amendment_search_complete=falsewarns that bounded or unavailable archive discovery may have missed amendmentsHoldings carry issuer, CUSIP,
market_value_usdin whole USD, shares or principal, andput_call;total_holdings_in_filingcounts raw rows andtotal_positionsdistinct positions; a shared legal name fails asambiguous_entity
secedgar_find_holders tool
issueras a ticker, CIK, or name, plus an optional 9-charactercusip, the precise match key (a name phrase-match both over- and under-matches);quarterasYYYY-QN, defaulting to the newest quarter past its 45-day filing deadline;limit1–100 (default 20) from up to 500 fetched filingsRows carry
filer_cik,accession_number, andform, whilesearch_mode(cusip/name),total_filings,fetched, andholders_in_quartersize the result; the list is unranked, so pass afiler_ciktosecedgar_get_institutional_holdingsto read the positionWhen matching documents exceed the fetch budget, the notice gives an exact
secedgar_search_filingscontinuation with the query, dates and document offset. Filterresults.period_endingto the quarter end, then deduplicate across the sample'sfiler_cikand search results'cik, keeping the latestfiling_date. Advance by 100 documents per full page; EDGAR's paging ceiling is 10,000 documents, and lower-bound totals may hide more beyond it
secedgar_get_beneficial_owners tool
issueris the company being held;form_kindall(default),13D, or13G;include_amendments(default true);limit1–20 matching parsed filings (default 10). The newest-first scan examines at most 50 documents, excluding schedules naming another issuer; exclusions and failed documents do not consumelimitEach filing lists
reporting_personswith voting power, dispositive power, andpercent_of_classper person, which joint filers report for the same shares, so they don't sum; a 13D carriespurpose_of_transaction, a 13G has noneCoverage starts 2024-12-18 with the structured XML schedules;
legacy_filings_before_coveragecounts the issuer's olderSC 13D/SC 13Gtext filingstotal_structured_filingspartitions intofilings_examined+filings_not_examined; examined documents partition intofilings_parsed+filings_excluded_other_issuer+filings_failed.length. Failed accessions carrydocument_not_found(404) orunparseable(including missing issuer identity), and setpartial.truncatedmeans indexed schedules remain unexamined after the result limit or scan ceiling, independently of failures
secedgar_get_fund_holdings tool
fundas a ticker (VOO), series ID (S000002839), or CIK; a multi-series trust fails asseries_requiredwith the series listed, so passseries_id;report_datepicks a period fromavailable_report_periods;limit1–100 (default 20) withoffset/next_offsetPositions come largest first by
percent_of_net_assets, with name, CUSIP / ISIN / LEI,balanceandunits,value_usd, and asset and issuer category, alongside fund net assets, total assets, and total liabilities. Positioncurrencyis its denomination; valuations stay in USD. Percentage text preserves the numeric digits in fixed-point notation.Holdings are as of
report_period_date, roughly two months beforefiling_date;publication_lag_daysstates the gap
secedgar_fetch_frames tool
conceptas a friendly name or raw tag,periodasCY2023,CY2024Q2, orCY2023Q4I,taxonomyus-gaap(default) ordei,unit(defaultUSD),sortdesc/asc;limit1–100 (default 25) withoffset/next_offsetOne call queries one tag:
unqueried_tagsandrelated_tagslist variants to fetch separately;value_distribution.max_to_p95_ratioflags scale outliers,period_end_rangeshows fiscal-year mixing, andcaveatsflags fiscal-Q4 gapsA
conceptthat is neither a friendly name nor an UpperCamelCase tag fails asunknown_concept; a well-formed tag with no frame isno_data
secedgar_compare_companies tool
2–10
companies× 1–8concepts;taxonomyus-gaap(default) orifrs-full;period_typeannual(default) orquarterly;periods1–12 (default 4), trimmed further when the inline matrix gets too largecellsalign each value on a calendarperiodand keep itsframe,period_end, and sourcetag; a call with no comparable annual data failsno_comparable_datafailed_companies(reasonnot_found,ambiguous, orno_company_facts) andgapsreport what's missing, andcaveatsflag differing period ends and unit mismatches between companiesAn unrecognized concept is listed in
unknown_conceptswith a hint; the call fails asunknown_conceptonly when every concept is unrecognized
secedgar_search_concepts tool
searchis a substring over friendly name, label, and tags, so a raw tag likeNetIncomeLossreverse-maps to its friendly name;groupandtaxonomyfilter; no arguments returns the full catalogEach concept lists
tags,ifrs_tags(only where an IFRS element was confirmed in live 20-F filings),related_tags,unit, andgroup
secedgar_dataframe_describe tool
Optional
namefor one dataframe; omit it to list the tenant's active dataframes, newest firstEach entry carries
source_tool,query_params,created_at/expires_at,row_count,truncated, and thecolumn_schemathat SQL forsecedgar_dataframe_queryhas to match
secedgar_dataframe_query tool
One DuckDB SELECT in
sql(joins, aggregates, window functions, CTEs);row_limit1–10,000 (default 1,000),previewfor fewer inline rows, andregister_as(df_XXXXX_XXXXX) to save the result as a new dataframeReturns
columns,rows,row_count, androw_count_capped, which when true meansrow_countis the cap, not a total; BIGINT columns serialize as stringsWrites, 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)
secedgar_dataframe_drop tool
nameof the dataframe to drop; idempotent, returningdropped: falsewhen nothing matchedOff unless
EDGAR_DATAFRAME_DROP_ENABLED=true; when disabled it is absent fromtools/list
secedgar://concepts resource
The friendly-name catalog grouped by statement, as
text/markdown, with the us-gaap, IFRS, and alternate-definition tags for each conceptThe names are what
secedgar_get_financials,secedgar_compare_companies, andsecedgar_fetch_framesaccept as concepts
secedgar://filing-types resource
Common SEC forms with cadence and use cases, as
text/markdownIncludes the 8-K item-code tables for both numbering regimes, the vocabulary of
secedgar_get_material_events'itemsfilter
secedgar_company_analysis prompt
Arguments:
companyrequired;focus_areasoptional free textReturns one user message with a numbered workflow (company search, a financial profile via
secedgar_get_snapshotwith trends viasecedgar_get_financials, filing review, material events, and a peer comparison viasecedgar_compare_companieswithsecedgar_fetch_framesfor a market-wide ranking) and a findings template; insider, institutional, or blockholder terms infocus_areasadd those ownership steps, and "ownership" adds all three
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
EDGAR-specific:
Requests are paced under SEC's 10 req/s limit; after a 429, calls fail locally as
rate_limitedwith aretryAftercountdown forEDGAR_RATE_LIMIT_COOLDOWN_SECONDS, then one probe goes outCIK resolution from tickers (fund tickers included), current and former company names, or raw CIKs, with near-match suggestions on a miss
Friendly XBRL concept names that handle historical tag changes;
secedgar_get_financials,secedgar_get_snapshot, andsecedgar_compare_companiesshare the same dedup and tag priority, so their numbers agree, andcaveatsflag gaps in the seriesFiling documents converted from HTML to text, with heading detection and offset paging for oversized filings
Opt-in local SQLite mirror of company tickers and XBRL company-facts (
EDGAR_MIRROR_ENABLED) that serves CIK resolution and financials from disk
Agent-friendly output:
In-conversation SQL: any tool whose response carries a
datasetfield has staged its full result as a DuckDB dataframe (df_<id>), while the inline list stays capped atlimit; inspect it withsecedgar_dataframe_describe, then query it withsecedgar_dataframe_queryDiscriminated outputs and explicit gaps:
sourceon filing-search rows,search_mode, 8-K itemregime, typedfailed_companiesreasons, andgapswithtags_triedin place of zero-filled valuesCompleteness disclosure:
history_scanned_through,total_is_exact,publication_lag_days, anddataset.truncatedtell agents how deep a scan went and what it left outOne parameter name per concept:
company,filed_after/filed_before, andformsmean the same thing on every tool, and common alternate spellings (ticker,cik,start_date,end_date,form_types, and others) are accepted as aliases
Getting started
Public Hosted Instance
A public instance is available at https://secedgar.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"secedgar-mcp-server": {
"type": "streamable-http",
"url": "https://secedgar.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"secedgar-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/secedgar-mcp-server@latest"],
"env": {
"EDGAR_USER_AGENT": "YourAppName your-email@example.com",
"MCP_TRANSPORT_TYPE": "stdio"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"secedgar-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/secedgar-mcp-server@latest"],
"env": {
"EDGAR_USER_AGENT": "YourAppName your-email@example.com",
"MCP_TRANSPORT_TYPE": "stdio"
}
}
}
}Or with Docker:
{
"mcpServers": {
"secedgar-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "EDGAR_USER_AGENT=YourAppName your-email@example.com",
"ghcr.io/cyanheads/secedgar-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
A User-Agent string in SEC's
"AppName contact@email.com"format; no account or key required.
Installation
Clone the repository:
git clone https://github.com/cyanheads/secedgar-mcp-server.gitNavigate into the directory:
cd secedgar-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env and set EDGAR_USER_AGENTConfiguration
Variable | Description | Default |
| Required. User-Agent sent to SEC, as | — |
| Max requests per second to SEC (1–10). |
|
| Seconds to refuse calls locally after a 429 before one probe goes out. |
|
| Seconds to cache the company and fund ticker files. |
|
| TTL for canvas dataframes, renewed on every dataframe operation. |
|
| Set |
|
| Enable the local SQLite mirror of company tickers and XBRL company-facts. Node/Bun only; bootstrap once with |
|
| Directory holding the mirror databases. |
|
| In-process refresh cron (HTTP transport only), e.g. | — |
| Fall back to the live SEC API on a mirror miss. Set |
|
| Canvas engine; |
|
| Transport: |
|
| HTTP server port. |
|
| Authentication: |
|
| Log level ( |
|
| Log each failed tool call's arguments and result, redacted by key name and capped at |
|
| Enable OpenTelemetry instrumentation (spans, metrics, completion logs). |
|
| OTLP base URL for traces and metrics. | — |
| OTLP log export endpoint (e.g. | — |
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run the production version:
bun run rebuild bun run start:http # or start:stdioRun checks and tests:
bun run devcheck # Lints, formats, type-checks bun run test # Runs test suite
Docker
docker build -t secedgar-mcp-server .
docker run -e EDGAR_USER_AGENT="MyApp my@email.com" -p 3010:3010 secedgar-mcp-serverThe image defaults to HTTP on port 3010 and ships the mirror CLI, so a running container can bootstrap and refresh its own mirror:
docker exec <container> bun run mirror:verify # sync status + sample reads
docker exec <container> bun run mirror:init # one-time bootstrap from the SEC bulk archive
docker exec <container> bun run mirror:refresh # re-ingest after SEC rebuilds the archiveProject structure
Directory | Purpose |
|
|
| Server environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Resource definitions: XBRL concepts and filing types. |
| Prompt definitions: company analysis. |
| Paced SEC client, CIK resolution, XBRL concept mapping and series dedup, ownership / 13D / 13G / NPORT-P parsers, 8-K item tables, HTML-to-text. |
| Opt-in local SQLite mirror of company tickers and XBRL company-facts. |
| Adapter over the framework |
| Build, devcheck, and lint tooling, plus the |
| Unit and integration tests, mirroring the |
Development guide
See CLAUDE.md and AGENTS.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor logging,ctx.statefor storageRegister new tools in
buildToolDefinitions()(src/mcp-server/tools/definitions/index.ts), and resources and prompts in thecreateApp()arrays insrc/index.tsWrap 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
gapsinstead
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
SEC XBRL MCP — wraps SEC EDGAR XBRL API (data.sec.gov)
Financial data and research MCP for US/CN/JP equities: filings, statements, ownership, signals.
EDGAR MCP — SEC EDGAR public APIs (free, no auth)
SEC MCP — SEC EDGAR public APIs (free, no auth)
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables AI clients to retrieve SEC company profiles, filing listings, and structured XBRL financial statements via MCP tools.13-
- AlicenseAqualityDmaintenanceAn MCP server that wraps SEC EDGAR APIs to provide company financial data, screening metrics, and disclosure signals for investment diligence, with every figure traced to its source filing.8MIT
- AlicenseNot gradedqualityCmaintenanceProvides structured US SEC/EDGAR filing data, including filings index, XBRL-derived earnings, and Form 4 insider transactions, as clean JSON via MCP. Supports x402 payments (USDC on Base) and Stripe subscription for access.MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for SEC EDGAR data, providing tools to look up companies, retrieve filings and documents, and access XBRL financial facts.38 npmMIT