@cyanheads/secedgar-mcp-server
Provides SQL analytics over materialized dataframes using DuckDB, enabling queries, joins, and aggregations on SEC filing data.
Click on "Install 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
Tools
Eight tools for querying SEC EDGAR data, plus three for SQL analytics over the DuckDB-backed canvas dataframes those tools materialize:
Tool | Description |
| Find companies and retrieve entity info with optional recent filings |
| Search EDGAR filings since 1993 — full-text (2001+) plus archive-backed browse for pre-2001 ranges |
| Fetch a specific filing's metadata and document content |
| Get historical XBRL financial data for a company |
| Form 3/4/5 insider transactions (buys, sells, grants, exercises) parsed from ownership XML |
| 13F-HR quarterly institutional holdings parsed from the information table |
| Fetch SEC XBRL frames for one concept × one period across all reporting companies |
| 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 |
secedgar_company_search
Entry point for most EDGAR workflows — resolve tickers, names, or CIKs to entity details.
Supports ticker symbols (
AAPL,VOO), company names (Apple), or CIK numbers (320193)ETFs and mutual funds resolve by ticker via
company_tickers_mf.json; fund results includeseries_idandclass_idfor downstream scopingCurrent and former company names both resolve (
Facebook→ Meta Platforms,Square→ Block)Near-match suggestions on zero-result name search (e.g.
Microsfot→MICROSOFT CORP / MSFT)Optionally includes recent filings with form type filtering
Date filtering (
filed_after/filed_before) and under-filled form filters page into the older submissions archive, reaching filings that predate the ~1000-entry recent window (e.g. a 2005 10-K);history_scanned_throughdiscloses the scan depth, and the full filtered history materializes as adf_<id>dataframe when it exceeds the inlinefiling_limitReturns entity metadata: SIC code, exchanges, fiscal year end, state of incorporation
secedgar_search_filings
Search EDGAR filings since 1993. Full-text search covers 2001-present (the EFTS index floor); pre-2001 date ranges are served from the archives — pre-2001 full-text matching requires entity scope.
Exact phrases (
"material weakness"), boolean operators (revenue OR income), wildcards (account*)Entity targeting within query string (
cik:320193orticker:AAPL) — scoped server-side by CIK, so filings made under a former company name (same CIK) are includedBrowse mode: omit
queryto list filings by form type (forms=["S-1"]) and/or entity (ticker:/cik:), optionally narrowed by date — a bare date range is not a valid search and must be paired with forms or entity targetingPre-2001 date ranges (back to 1993) route to the archives: an entity-scoped range reads the filer's full submissions history; an unscoped forms/date range browses the quarterly full-index. A range straddling 2001-01-01 is rejected with a split instruction, and pre-2001 full-text (no entity scope) is unsupported. Each row carries a
sourcefield (efts/submissions/full-index), preserved into thedf_<id>dataframeDate range filtering, form type filtering, pagination up to 10,000 results
Returns form distribution for narrowing follow-up searches
When the entity-scoped window exceeds the inline limit, the already-fetched EFTS window is materialized as a
df_<id>dataframe — query it withsecedgar_dataframe_query
secedgar_get_filing
Fetch a specific filing's metadata and document content by accession number.
Accepts accession numbers in dash or no-dash format
Converts HTML filings to readable plain text
Configurable content limit (1K–200K characters, default 50K)
Can fetch specific exhibits by document name
Offset paging for large documents (10-K, S-1/A can exceed 1M chars): pass
next_offsetfrom a truncated response asoffseton the next call to continue reading; first-page truncated responses include a detectedoutline(headings with offsets) for targeted navigationSection targeting via the
sectionparam: jumps directly to a named heading by case-insensitive substring match (e.g."risk factors","item 7","certain relationships"); on a miss, the error carries the detected outline so you can pick the correct headingExtracted text is cached per
accession + document(bounded LRU, 8 entries), making subsequent paged calls cheap
secedgar_get_financials
Get historical XBRL financial data for a company with friendly concept name resolution.
Friendly names like
"revenue","net_income","eps_diluted"auto-resolve to correct XBRL tagsHandles historical tag changes (e.g., ASC 606 revenue recognition)
Automatic deduplication to one value per standard calendar period
Filter by annual, quarterly, or all periods
Optional
limitcaps the inline series to the most-recent N periods; the full series stays queryable via thedf_<id>dataframeSee
secedgar://conceptsresource for the full mapping
secedgar_get_insider_transactions
Surface Form 3/4/5 insider activity for a company by parsing ownership XML.
Reporting person, relationship to issuer (director, officer + title, 10% owner), and transaction date
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
Filter by
transaction_type(purchase,sale,all); scans newest filings firstThe full set of transactions parsed from the scanned recent filings is materialized as a
df_<id>dataframe (the inline list is a preview capped atlimit) — query it withsecedgar_dataframe_queryto aggregate net buy/sell by insider
secedgar_get_institutional_holdings
Surface 13F-HR quarterly institutional holdings by parsing the information table.
Pass the institutional filer (CIK or full legal name, e.g.
0000102909for Vanguard) to see what it holds — reverse lookup from a portfolio company to its holders is not supported (EDGAR has no issuer→13F index); usesecedgar_search_filingswithforms=["13F-HR"]for issuer-side questionsEach holding: issuer name, CUSIP, market value (whole USD), shares/principal, and put/call; raw rows also carry investment discretion
Sub-lines for the same security (one per manager/account) are consolidated into distinct positions sorted by value by default — pass
consolidate: falsefor raw filing rowsResolves the filing-manager name and reporting quarter from the cover page; target a specific quarter with
quarter(e.g."2025-Q4")total_holdings_in_filingcounts raw info-table rows;total_positionscounts distinct positions after consolidation (both beforelimit)The full parsed holdings set is materialized as a
df_<id>dataframe (the inline list is a preview capped atlimit) — query it withsecedgar_dataframe_queryfor full-filing aggregation or cross-quarter joins oncusip+reporting_period
secedgar_fetch_frames
Fetch SEC XBRL frames for one concept × one period across all reporting companies.
Same friendly concept names as
secedgar_get_financialsSupports annual (
CY2023), quarterly (CY2024Q2), and instant (CY2023Q4I) periodsInline response returns the top N ranked companies (sort + limit), with ticker enrichment
The full frames response (all reporters, typically 2k–10k rows) is materialized as a
df_<id>dataframe — query it withsecedgar_dataframe_queryrelated_tagsflags 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
secedgar_search_concepts
Discover supported XBRL concept names before querying financials or cross-company comparisons.
Search by friendly name, label, or raw XBRL tag
Filter by statement group (
income_statement,balance_sheet,cash_flow,per_share,entity_info) or taxonomyReverse-lookup raw tags like
NetIncomeLossto the supported friendly namesSurfaces
related_tagsfor concepts with a high-coverage alternate-definition tag (e.g. restricted-cash-inclusive cash) so callers can discover them before screeningReturns the same catalog used by
secedgar_get_financials,secedgar_fetch_frames, andsecedgar://concepts
secedgar_dataframe_describe / secedgar_dataframe_query / secedgar_dataframe_drop
In-conversation SQL analytics over the dataframes that secedgar_fetch_frames, secedgar_search_filings, secedgar_get_financials, secedgar_get_insider_transactions, and secedgar_get_institutional_holdings materialize on a shared DuckDB-backed canvas. Each data-returning call adds a dataset field with a df_XXXXX_XXXXX handle; pass that handle to secedgar_dataframe_query for joins, aggregates, window functions, percentiles — standard DuckDB SQL.
Read-only by default. Writes, DDL, DROP, COPY, PRAGMA, ATTACH, and external-file table functions are rejected by the framework SQL gate. System catalogs (
information_schema,pg_catalog,sqlite_master,duckdb_*) are denied at the bridge layer so callers can't enumerate dataframes they don't already hold a handle for.secedgar_dataframe_dropis the only destructive tool and is opt-in (EDGAR_DATAFRAME_DROP_ENABLED=true); TTL handles cleanup otherwise.Per-table TTL. Each dataframe ages on its own clock (default 24h, override with
EDGAR_DATASET_TTL_SECONDS). The canvas itself uses the framework's sliding TTL.register_aschaining.secedgar_dataframe_querycan persist its result as a new dataframe (df_XXXXX_XXXXX) with a fresh TTL — pipe analyses without re-running the source query.
Related MCP server: edgar-mcp
Resources
URI | Description |
| Common XBRL financial concepts grouped by statement, mapping friendly names to XBRL tags |
| Common SEC filing types with descriptions, cadence, and use cases |
Prompts
Prompt | Description |
| Guides a structured analysis of a public company's SEC filings: identify recent filings, extract financial trends, surface risk factors, and note material events |
Features
Built on @cyanheads/mcp-ts-core:
Declarative tool definitions — single file per tool, framework handles registration and validation
Structured output schemas with automatic formatting for human-readable display
Unified error handling across all tools
Pluggable auth (
none,jwt,oauth)Structured logging with request-scoped context
Runs locally (stdio/HTTP) from the same codebase
SEC EDGAR–specific:
Rate-limited HTTP client respecting SEC's 10 req/s limit with automatic inter-request delay
CIK resolution from tickers (including ETFs and mutual funds via
company_tickers_mf.json), company names (current and former), or raw CIK numbers with local caching; near-match trigram suggestions on zero-result name queries; committedformer-names.jsonasset for prior-name resolution (Facebook→ Meta,Square→ Block)Friendly XBRL concept name mapping with historical tag change handling
Searchable concept catalog with statement-group metadata and reverse XBRL tag lookup
HTML-to-text conversion for filing documents via
html-to-textIn-conversation SQL analytics:
secedgar_fetch_frames,secedgar_search_filings,secedgar_get_financials,secedgar_get_insider_transactions, andsecedgar_get_institutional_holdingsmaterialize their full result as a DuckDB-backed canvas dataframe queryable viasecedgar_dataframe_queryNo API keys required — SEC EDGAR is a free, public API
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"
}
}
}
}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.3.0 or higher.
Installation
Clone the repository:
git clone https://github.com/cyanheads/secedgar-mcp-server.gitNavigate into the directory:
cd secedgar-mcp-serverInstall dependencies:
bun installBuild:
bun run buildConfiguration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
Variable | Description | Default |
| Required. User-Agent header for SEC compliance. Format: | — |
| Max requests/second to SEC APIs. Do not exceed 10. |
|
| Seconds to cache the company tickers lookup file. |
|
| Per-table TTL for canvas-registered dataframes. Sliding window touched on every dataframe op. |
|
| Set to |
|
| Enable the local SQLite mirror of |
|
| Directory holding the mirror SQLite databases. |
|
| Cron for the in-process nightly refresh (HTTP transport only). Recommended | — |
| When the mirror misses (not yet synced, or a filing newer than the last refresh), fall back to the live SEC API. Set |
|
| Canvas engine. Defaults to |
|
| Transport: |
|
| HTTP server port |
|
| Authentication: |
|
| Log level ( |
|
| Directory for log files (Node.js only). |
|
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 ships the mirror CLI, so the local mirror (EDGAR_MIRROR_ENABLED) can be bootstrapped, inspected, and refreshed inside a running container:
docker exec <container> bun run mirror:verify # sync status + sample reads
docker exec <container> bun run mirror:init # one-time bootstrap (downloads the SEC bulk archive)
docker exec <container> bun run mirror:refresh # re-ingest when the archive has been rebuiltProject structure
Directory | Purpose |
| Tool definitions ( |
| Resource definitions. XBRL concepts and filing types. |
| Prompt definitions. Company analysis prompt. |
| SEC EDGAR API client, XBRL concept mapping, HTML-to-text conversion. |
| Adapter over the framework |
| Server-specific environment variable parsing and validation with Zod. |
| 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 and resources in the
createApp()arrays
Contributing
Issues and pull requests 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 installed
Maintenance
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/cyanheads/secedgar-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server