
A production-grade interface to the SEC's EDGAR archive with serious depth. You get full-text search across 30 years of filings, XBRL financials with friendly concept names that handle historical tag changes, parsed insider transactions from Forms 3/4/5, and institutional holdings from 13F-HR filings. The frames tool pulls cross-company datasets for a single metric across all filers. Results beyond inline limits land in DuckDB-backed dataframes you can query with SQL. Runs locally via stdio or connects to a public HTTP endpoint. Reach for this when you need structured financial data or filing content without writing your own EDGAR parser or wrestling with raw XBRL taxonomies.
claude mcp add --transport http secedgar https://secedgar.caseyjhand.com/mcpRun in your terminal. Add --scope user to make it available in every project.
Review the command, arguments, and environment values before installing — MCP servers run with your local permissions.
Verified live against the running server on Jun 10, 2026.
secedgar_company_searchFind companies and retrieve entity info with optional recent filings. Entry point for most EDGAR workflows — resolves tickers, names, or CIKs to entity details, with accession numbers in the result feeding secedgar_get_filing for document content.4 paramsFind companies and retrieve entity info with optional recent filings. Entry point for most EDGAR workflows — resolves tickers, names, or CIKs to entity details, with accession numbers in the result feeding secedgar_get_filing for document content.
filing_limitintegerform_typesarrayinclude_filingsbooleanquery*stringsecedgar_search_filingsSearch the full-text index of EDGAR filings since 1993. Supports exact phrases, boolean operators, wildcards, and entity targeting (ticker:AAPL or cik:320193 in query).7 paramsSearch the full-text index of EDGAR filings since 1993. Supports exact phrases, boolean operators, wildcards, and entity targeting (ticker:AAPL or cik:320193 in query).
end_datevalueformsarraylimitintegeroffsetintegerquery*stringsortstringfiling_date_desc · filing_date_asc · relevancedefault: filing_date_descstart_datevaluesecedgar_get_filingFetch a specific filing's metadata and document content by accession number. Returns the primary document as readable text, with option to fetch specific exhibits.5 paramsFetch a specific filing's metadata and document content by accession number. Returns the primary document as readable text, with option to fetch specific exhibits.
accession_number*stringcikstringcontent_limitintegerdocumentstringinclude_xbrlbooleansecedgar_get_financialsGet historical XBRL financial data for a company. Accepts friendly concept names (e.g., "revenue", "net_income", "assets") or raw XBRL tags. Discover available friendly names with secedgar_search_concepts. Handles historical tag changes and deduplicates data automatically.5 paramsGet historical XBRL financial data for a company. Accepts friendly concept names (e.g., "revenue", "net_income", "assets") or raw XBRL tags. Discover available friendly names with secedgar_search_concepts. Handles historical tag changes and deduplicates data automatically.
company*stringconcept*stringlimitintegerperiod_typestringannual · quarterly · alltaxonomystringus-gaap · ifrs-full · deidefault: us-gaapsecedgar_get_insider_transactionsFetch Form 4 insider transactions (purchases, sales, grants, exercises) for a company by parsing SEC EDGAR ownership XML. Returns the reporting person, their relationship to the issuer, transaction date, type, shares traded (absolute magnitude), direction (acquire/dispose), pr...3 paramsFetch Form 4 insider transactions (purchases, sales, grants, exercises) for a company by parsing SEC EDGAR ownership XML. Returns the reporting person, their relationship to the issuer, transaction date, type, shares traded (absolute magnitude), direction (acquire/dispose), pr...
limitintegerticker_or_cik*stringtransaction_typestringpurchase · sale · alldefault: allsecedgar_get_institutional_holdingsFetch 13F-HR quarterly institutional holdings by parsing the SEC EDGAR information table XML. Use ticker_or_cik to look up an institution (e.g., "Vanguard Group" or its CIK) and see what it holds — or pass a company ticker/CIK to find which institutions filed 13Fs covering tha...4 paramsFetch 13F-HR quarterly institutional holdings by parsing the SEC EDGAR information table XML. Use ticker_or_cik to look up an institution (e.g., "Vanguard Group" or its CIK) and see what it holds — or pass a company ticker/CIK to find which institutions filed 13Fs covering tha...
consolidatebooleanlimitintegerquarterstringticker_or_cik*stringsecedgar_fetch_framesFetch SEC XBRL frames for one concept × one period across all reporting companies. Inline response returns the top N ranked companies; the full frames response (all reporters) is materialized as df_<id> when a canvas is available, queryable via secedgar_dataframe_query. Accept...5 paramsFetch SEC XBRL frames for one concept × one period across all reporting companies. Inline response returns the top N ranked companies; the full frames response (all reporters) is materialized as df_<id> when a canvas is available, queryable via secedgar_dataframe_query. Accept...
concept*stringlimitintegerperiod*stringsortstringdesc · ascdefault: descunitstringUSD · USD-per-shares · USD/shares · shares · puredefault: USDsecedgar_search_conceptsSearch supported XBRL financial concepts by keyword, statement group, or taxonomy. Use before secedgar_get_financials or secedgar_fetch_frames to discover the right friendly name, or pass a raw XBRL tag (e.g., "NetIncomeLoss") to reverse-lookup which friendly names map to it....3 paramsSearch supported XBRL financial concepts by keyword, statement group, or taxonomy. Use before secedgar_get_financials or secedgar_fetch_frames to discover the right friendly name, or pass a raw XBRL tag (e.g., "NetIncomeLoss") to reverse-lookup which friendly names map to it....
groupstringincome_statement · balance_sheet · cash_flow · per_share · entity_infosearchstringtaxonomystringus-gaap · ifrs-full · deisecedgar_dataframe_describeList dataframes (df_XXXXX_XXXXX) materialized by secedgar_fetch_frames, secedgar_search_filings, secedgar_get_financials, secedgar_get_insider_transactions, and secedgar_get_institutional_holdings. Each entry surfaces source tool, query parameters, creation/expiry timestamps,...1 paramsList dataframes (df_XXXXX_XXXXX) materialized by secedgar_fetch_frames, secedgar_search_filings, secedgar_get_financials, secedgar_get_insider_transactions, and secedgar_get_institutional_holdings. Each entry surfaces source tool, query parameters, creation/expiry timestamps,...
namestringsecedgar_dataframe_queryRun a single-statement SELECT against the canvas dataframes registered by secedgar_fetch_frames, secedgar_search_filings, and secedgar_get_financials. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, and external-file table functions are rejected. System catalogs (informati...4 paramsRun a single-statement SELECT against the canvas dataframes registered by secedgar_fetch_frames, secedgar_search_filings, and secedgar_get_financials. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, and external-file table functions are rejected. System catalogs (informati...
previewintegerregister_asstringrow_limitintegersql*stringQuery SEC EDGAR filings, XBRL financials, and company data via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://secedgar.caseyjhand.com/mcp
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.
| Tool | Description |
|---|---|
secedgar_company_search | Find companies and retrieve entity info with optional recent filings |
secedgar_search_filings | Search EDGAR filings since 1993: full text from 2001, archive browse before that |
secedgar_get_filing | Fetch a filing's metadata and document text, paged or by section |
secedgar_get_financials | Get historical XBRL financial data for one company and concept |
secedgar_get_snapshot | One-call financial profile: the latest value of every supported concept |
secedgar_get_material_events | 8-K filings with item codes decoded and filterable |
secedgar_get_insider_transactions | Form 4 / 4-A insider transactions parsed from ownership XML |
secedgar_get_institutional_holdings | 13F-HR quarterly holdings of one institutional manager |
secedgar_find_holders | Reverse 13F lookup: which managers reported holding an issuer |
secedgar_get_beneficial_owners | 5%+ blockholders of an issuer from structured SCHEDULE 13D / 13G filings |
secedgar_get_fund_holdings | ETF and mutual fund portfolio holdings from the quarterly NPORT-P report |
secedgar_fetch_frames | One XBRL concept × one period across every reporting company |
secedgar_compare_companies | Compare named companies across several concepts, aligned on fiscal years or calendar quarters |
secedgar_search_concepts | Discover supported XBRL concept names or reverse-lookup a raw tag |
secedgar_dataframe_describe | List canvas dataframes with provenance, TTL, and schema |
secedgar_dataframe_query | Run a single-statement SELECT across dataframes |
secedgar_dataframe_drop | Drop a canvas dataframe by name; opt-in via EDGAR_DATAFRAME_DROP_ENABLED=true |
| Resource | Description |
|---|---|
secedgar://concepts | XBRL financial concepts grouped by statement, mapping friendly names to XBRL tags |
secedgar://filing-types | 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.
| Prompt | Description |
|---|---|
secedgar_company_analysis | Structured analysis of a company's SEC filings: financial trends, risk factors, material events |
secedgar_company_search toolquery takes a ticker (equities, ETFs, and mutual funds; BRK-B or BRK.B), a current or former company name (the corporate suffix can be left off: Apple finds Apple Inc.), or a CIK; include_filings (default on) adds up to filing_limit filings (default 10; at most 50 inline, and a larger value up to 1000 is served at 50 with a notice naming where the rest are), filtered by exact forms match and filed_after / filed_beforeseries_id / class_id for a fund ticker; fails as no_match (with near matches in data.suggestions when a name scores close enough) or multiple_matcheshistory_scanned_through reports how far it reachedsecedgar_search_filings toolquery (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, where it counts matching documents, so a filing can recur across pagesticker: / cik: scope and reads up to 50 documents (scan reports candidates, scanned, and matched)matched_documents (filenames for secedgar_get_filing)source (efts, submissions, full-index); the response carries total and form_distribution (both counting filings), total_is_exact, and, when full text was searched, total_documentssecedgar_get_filing toolaccession_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)offset / next_offset until content_truncated is false, or jump with section (matched against every detected heading, an exact heading first, then a line of the document such as a subheading the outline omits; item 1 never lands on Items 10–16, nor part i on Part II); the first page of a truncated document carries an outline of up to 50 headings with offsets — every Item and Part heading kept when it is cut (bare table-of-contents copies of titled ones aside), and the notice says how many were detected — plain-text legacy filings included; when section lands on a document line rather than a detected heading, the notice says sodocuments 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<accession>.txt is read instead and the response notice says sosecedgar_get_financials toolcompany (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 — annual reads an instant (balance-sheet) concept at the filer's fiscal-year ends, and when omitted it defaults to annual with the full series for instant concepts; unit picks one SEC unit key (ZAR, USD, USD/EUR) for a concept reported in several; limit 1–100 trims the inline seriesform, filed date, accession_number, and tag, all in one unit — by default the unit of the newest value, with any other units named in caveats; tags_tried names the tags walked, and an empty result fails as no_concept_data (naming the taxonomy to retry when another one carries the concept), no_frame_data, or no_period_data (no_unit_data, listing the reported units, for a unit the concept isn't reported in)concept that is neither a friendly name nor an UpperCamelCase tag fails as unknown_concept before any SEC request, with a formula for standard combinations (free_cash_flow, ebitda, working_capital) or up to three closest friendly namessecedgar_get_snapshot toolcompany, 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 dataframelines entry reports the latest annual and quarterly value for a duration concept, or the latest instant value for balance-sheet and entity-info concepts, each with the tag it came from, under the line's taxonomy; concepts the filer doesn't report land in gaps with tags_triedsecedgar_get_material_events toolcompany 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)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 removedlimit with filings passing the items filter, stopping on the page that fills it; history_scanned_through and dataset.truncated report what went unread, and a result short of limit with pages unread names how far the scan reached and the date window that goes furthersecedgar_get_insider_transactions toolcompany 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 5filed_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 parsedtransaction_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, and filings_other_issuer counts scanned Form 4s the company filed as a reporting owner of another issuer, which add no rowssecedgar_get_institutional_holdings toolcompany 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 valuequarter 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 periodmarket_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_entitysecedgar_find_holders toolissuer 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 filingsfiler_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 positionsecedgar_get_beneficial_owners toolissuer 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 fetchreporting_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 nonelegacy_filings_before_coverage counts the issuer's older SC 13D / SC 13G text filingssecedgar_get_fund_holdings toolfund 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_offsetpercent_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 liabilitiesreport_period_date, roughly two months before filing_date; publication_lag_days states the gapsecedgar_fetch_frames toolconcept as a friendly name or raw tag, period as CY2023, CY2024Q2, or CY2023Q4I, taxonomy us-gaap (default) or dei (SEC publishes frames for no other namespace; IFRS filers are read per company), unit (default USD), sort desc / asc; limit 1–100 (default 25) with offset / next_offset down the rankingunqueried_tags lists same-meaning variants to fetch separately and related_tags alternate-definition tags; value_distribution.max_to_p95_ratio flags scale-factor outliers, period_end_range shows fiscal-year mixing, and caveats flags fiscal-Q4 gaps in quarterly frames and annual frames a proxy statement or a 10-Q can holdconcept that is neither a friendly name nor an UpperCamelCase tag fails as unknown_concept before the frames request, with the same formula or closest-name hint as secedgar_get_financials; a well-formed tag with no frame is no_datasecedgar_compare_companies toolcompanies × 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 largecells align each value on a calendar period and keep its frame, period_end, and source tag; an annual matrix holds each company's fiscal year under the calendar-year key SEC frames it as, with balance-sheet values taken only at fiscal-year ends and placed beside the income lines of the year they close (interim and cover-date snapshots stay out, and a call left with nothing else fails no_comparable_data with a hint naming period_type: "quarterly"), while a quarterly matrix aligns snapshots on the calendar quarter they fall in; failed_companies (reason not_found, ambiguous, or no_company_facts) and gaps (no value in any period) report what's missing, and caveats flag differing period ends in any inline column, unit mismatches between companies (each company reads one unit per concept), and, once per concept, the companies whose values all predate the inline window, each with its newest periodunknown_concepts with its hint, never as a gap per company; the call fails as unknown_concept only when every concept is onesecedgar_search_concepts toolsearch 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 catalogtags, ifrs_tags (only where an IFRS element was confirmed in live 20-F filings), related_tags, unit, and groupsecedgar_dataframe_describe toolname for one dataframe; omit it to list the tenant's active dataframes, newest firstsource_tool, query_params, created_at / expires_at, row_count, truncated, and the column_schema that SQL for secedgar_dataframe_query has to matchsecedgar_dataframe_query toolsql (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 dataframecolumns, rows, row_count, and row_count_capped, which when true means row_count is the cap, not a total; BIGINT columns serialize as stringsnon_select_statement, denied_function, multi_statement, system_catalog_access, and others)secedgar_dataframe_drop toolname of the dataframe to drop; idempotent, returning dropped: false when nothing matchedEDGAR_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 itsecedgar://concepts resourcetext/markdown, with the us-gaap, IFRS, and alternate-definition tags for each conceptsecedgar_get_financials, secedgar_compare_companies, and secedgar_fetch_frames accept as conceptssecedgar://filing-types resourcetext/markdownsecedgar_get_material_events' items filtersecedgar_company_analysis promptcompany required; focus_areas optional free textsecedgar_get_snapshot with trends via secedgar_get_financials, filing review, material events, and a peer comparison via secedgar_compare_companies with secedgar_fetch_frames for a market-wide ranking) and a findings template; insider, institutional, or blockholder terms in focus_areas add those ownership steps, and "ownership" adds all threeBuilt 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:
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 answeringsecedgar_get_financials, secedgar_get_snapshot, and secedgar_compare_companies share one frame dedup and tag priority, so their numbers agree; a series never mixes units, so a 20-F filer's convenience translation stays out of its reporting-currency series; a period whose frame SEC assigned to a proxy statement's pay-versus-performance figure is answered from the filer's own report instead, an annual frame holding a 10-Q's trailing-twelve-month figure is left out of the annual series, 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 shortEDGAR_MIRROR_ENABLED) that serves CIK resolution and financials from diskAgent-friendly output:
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_querysource on filing-search rows, search_mode, 8-K item regime, typed failed_companies reasons, and gaps with tags_tried in place of zero-filled valueshistory_scanned_through, total_is_exact, publication_lag_days, and dataset.truncated tell agents how deep a scan went and what it left outcompany, 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 — the company spellings also reach secedgar_company_search's query and the issuer of secedgar_find_holders and secedgar_get_beneficial_ownersA 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"
}
}
}
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/mcp
"AppName contact@email.com" format; no account or key required.git clone https://github.com/cyanheads/secedgar-mcp-server.git
cd secedgar-mcp-server
bun install
cp .env.example .env
# edit .env and set EDGAR_USER_AGENT
| Variable | Description | Default |
|---|---|---|
EDGAR_USER_AGENT | Required. User-Agent sent to SEC, as "AppName contact@email.com". SEC blocks IPs without one. | — |
EDGAR_RATE_LIMIT_RPS | Max requests per second to SEC (1–10). | 10 |
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 |
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 |
EDGAR_DATASET_TTL_SECONDS | Per-table TTL for canvas dataframes, a sliding window renewed on every dataframe operation. | 86400 |
EDGAR_DATAFRAME_DROP_ENABLED | Set true to expose secedgar_dataframe_drop, the only destructive tool. | false |
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 |
EDGAR_MIRROR_PATH | Directory holding the mirror databases. | ./data/edgar-mirror |
EDGAR_MIRROR_REFRESH_CRON | In-process refresh cron (HTTP transport only), e.g. 0 9 * * *. Omit to refresh with bun run mirror:refresh. | — |
EDGAR_MIRROR_FALLBACK_LIVE | Fall back to the live SEC API on a mirror miss. Set false for mirror-only reads. | true |
CANVAS_PROVIDER_TYPE | Canvas engine; none disables dataframes. | duckdb |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
OTEL_ENABLED | Enable OpenTelemetry instrumentation (spans, metrics, completion logs). | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP base URL; traces go to /v1/traces and metrics to /v1/metrics. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT / OTEL_EXPORTER_OTLP_METRICS_ENDPOINT override either one. | — |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Opt-in OTLP log export (e.g. http://localhost:4318/v1/logs); the base endpoint never enables it. | — |
See .env.example for the full list of optional overrides.
Build and run the production version:
bun run rebuild
bun run start:http # or start:stdio
Run checks and tests:
bun run devcheck # Lints, formats, type-checks
bun run test # Runs test suite
docker build -t secedgar-mcp-server .
docker run -e EDGAR_USER_AGENT="MyApp my@email.com" -p 3010:3010 secedgar-mcp-server
The 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 archive
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point: registers resources and prompts, starts the SEC client, canvas, and optional mirror. |
src/config | Server environment variable parsing and validation with Zod. |
src/mcp-server/tools/definitions | Tool definitions (*.tool.ts) and the buildToolDefinitions() registration list. |
src/mcp-server/resources/definitions | Resource definitions: XBRL concepts and filing types. |
src/mcp-server/prompts/definitions | Prompt definitions: company analysis. |
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. |
src/services/edgar/mirror | Opt-in local SQLite mirror of company tickers and XBRL company-facts. |
src/services/canvas-bridge | Adapter over the framework DataCanvas: df_<id> naming, per-table TTL, system-catalog SQL deny. |
scripts | Build, devcheck, and lint tooling, plus the mirror:* commands. |
tests | Unit and integration tests, mirroring the src/ structure. |
See CLAUDE.md and AGENTS.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for logging, ctx.state for storagebuildToolDefinitions() (src/mcp-server/tools/definitions/index.ts), and resources and prompts in the createApp() arrays in src/index.tsgaps insteadIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
EDGAR_USER_AGENT*User-Agent header for SEC compliance. Format: "AppName contact@email.com".
MCP_LOG_LEVELdefault: infoSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_PUBLIC_URLPublic-facing origin override for TLS-terminating proxies (e.g., https://mcp.example.com). Leave unset for direct deployments.
MCP_AUTH_MODEdefault: noneAuthentication mode to use: 'none', 'jwt', or 'oauth'.