Skip to main content
Glama
cyanheads

@cyanheads/secedgar-mcp-server

by cyanheads

npm License Docker MCP SDK TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://secedgar.caseyjhand.com/mcp


Tools

Fourteen tools for querying SEC EDGAR data, plus three for SQL analytics over the DuckDB-backed canvas dataframes those tools materialize:

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 (2001+) plus archive-backed browse for pre-2001 ranges

secedgar_get_filing

Fetch a specific filing's metadata and document content

secedgar_get_financials

Get historical XBRL financial data for a company

secedgar_get_snapshot

One-call financial profile — the latest value of every supported concept, grouped by statement

secedgar_get_material_events

8-K filings with item codes decoded and filterable — earnings, officer changes, non-reliance

secedgar_get_insider_transactions

Form 4 / 4-A insider transactions (buys, sells, grants, exercises) parsed from ownership XML

secedgar_get_institutional_holdings

13F-HR quarterly institutional holdings parsed from the information table

secedgar_find_holders

Reverse 13F lookup — which institutional managers reported holding an issuer

secedgar_get_beneficial_owners

5%+ blockholders of an issuer, parsed 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

Fetch SEC XBRL frames for one concept × one period across all reporting companies

secedgar_compare_companies

Compare named companies across several concepts, aligned on calendar periods

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 — off by default since TTL already handles cleanup, and uncallable until the flag is set

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); a multi-class share ticker resolves in either form (BRK-B or BRK.B)

  • ETFs and mutual funds resolve by ticker via company_tickers_mf.json; fund results include series_id and class_id for downstream scoping

  • Current and former company names both resolve (Facebook → Meta Platforms, Square → Block)

  • Corporate suffix form does not have to match the registry (Beacon Financial CorporationBeacon Financial Corp); Corp, Inc, Co, and Ltd stay distinct from one another, since separate registrants differ only by which one they use (TORO CO vs TORO CORP.)

  • Near-match suggestions on a zero-result name or ticker search (e.g. MicrosfotMICROSOFT CORP / MSFT, CSWICSW INDUSTRIALS, INC. / CSW)

  • 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_through discloses the scan depth, and the full filtered history materializes as a df_<id> dataframe when it exceeds the inline filing_limit

  • Returns 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:320193 or ticker:AAPL) — scoped server-side by CIK, so filings made under a former company name (same CIK) are included; a multi-class share ticker resolves in either form (ticker:BRK-B or ticker:BRK.B)

  • Browse mode: omit query to 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 targeting

  • Pre-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. Each row carries a source field (efts / submissions / full-index), preserved into the df_<id> dataframe

  • Pre-2001 free text is matched by reading documents, so it needs ticker:/cik: scope to bound the work: the form + date pre-filter picks candidates, up to 50 are read, and scan reports candidates / scanned / matched rather than presenting a partial read as a complete one. SEC's request rate is the cost — roughly 5s for a full 50-document scan. Each read covers the whole accession .txt (pre-1997 filings expose no per-document URL), so a match can sit in an attached exhibit rather than the body of the requested form

  • A range crossing 2001-01-01 is split at the boundary and merged: the full-text index serves 2001 onward, the archives serve the rest. period_ending, ticker, file_description, sic, and location exist only on source: efts rows, so a merged result carries them on some rows and not others

  • Date 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 with secedgar_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

  • Binary entries — scanned pages, PDF exhibits, packaged archives and spreadsheets — are marked binary in the document catalog and rejected with a binary_document error instead of being returned as decoded bytes

  • Offset paging for large documents (10-K, S-1/A can exceed 1M chars): pass next_offset from a truncated response as offset on the next call to continue reading; first-page truncated responses include a detected outline (headings with offsets) for targeted navigation

  • Section targeting via the section param: jumps directly to a named heading by substring match that ignores case, whitespace style, and quote style (e.g. "risk factors", "item 7", "certain relationships"), so a heading copied from the outline resolves whether it carries the filing's non-breaking spaces and curly quotes or plain ones; on a miss, the error carries the detected outline so you can pick the correct heading

  • Extracted 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 tags

  • Handles 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 limit caps the inline series to the most-recent N periods; the full series stays queryable via the df_<id> dataframe

  • Quarterly results carry a caveats entry naming every calendar quarter absent from the frame-tagged series — SEC reports fiscal Q4 as the 10-K residual, so the calendar quarter that fiscal Q4 spans has no discrete quarterly value (calendar-year filers included), and a filer whose other fiscal quarters span non-calendar durations loses a second quarter the same way

  • A further caveats entry 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 stop years short

  • See secedgar://concepts resource for the full mapping


secedgar_get_snapshot

Build a company financial profile in one call instead of a run of secedgar_get_financials calls.

  • Reads the filer's complete companyfacts payload once, then resolves every supported concept against it

  • Same frame dedup and tag priority as secedgar_get_financials, so the two agree for any concept they both cover

  • 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

  • Concepts the filer does not report are listed under gaps with the XBRL tags that were tried — never zero-filled or interpolated

  • IFRS filers resolve through the mapped IFRS tag variants via taxonomy: "ifrs-full", which covers the income statement, balance sheet, cash flow, and per-share concepts; each line reports the taxonomy its value came from

  • Compact single-record profile — no dataframe; reach for secedgar_get_financials when you need a time series


secedgar_get_insider_transactions

Surface Form 4 / 4-A insider activity for a company by parsing ownership XML. Form 3 initial statements and Form 5 annual statements are not covered — reach those with secedgar_search_filings (forms: ["3", "5"]) plus secedgar_get_filing.

  • 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 first

  • The full set of transactions parsed from the scanned recent filings is materialized as a df_<id> dataframe (the inline list is a preview capped at limit) — query it with secedgar_dataframe_query to 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. 0000102909 for Vanguard) to see what it holds; for the reverse direction — which managers hold a given company — use secedgar_find_holders, whose filer_cik results feed straight back into this tool

  • Each 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: false for raw filing rows

  • Resolves the filing-manager name and reporting quarter from the cover page; target a specific quarter with quarter (e.g. "2025-Q4")

  • total_holdings_in_filing counts raw info-table rows; total_positions counts distinct positions after consolidation (both before limit)

  • Page through a large information table with offset — the response echoes the effective offset and returns next_offset while rows remain, so every position stays reachable even when the canvas is disabled

  • The full parsed holdings set is materialized as a df_<id> dataframe (the inline list is one page of limit rows) — query it with secedgar_dataframe_query for full-filing aggregation or cross-quarter joins on cusip + reporting_period


secedgar_find_holders

Reverse 13F lookup: which institutional managers reported a position in an issuer, for one reporting quarter.

  • Searching by cusip matches the identifier the 13F information table itself carries — the precise path. Louisiana-Pacific Q1 2026 returns 451 filings by CUSIP 546347105 against 43 by the phrase "LOUISIANA-PACIFIC CORP"; the name path both under-matches (managers write the name differently) and over-matches (an unrelated issuer sharing a word)

  • A CUSIP is not derivable from a ticker anywhere in EDGAR — read one off any secedgar_get_institutional_holdings result, or fall back to the name path

  • 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

  • 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) do not land in the wrong quarter's holder list

  • Up to 500 filer rows are fetched per call; total_filings reports the full count and dataset.truncated flags when more exist

  • 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


secedgar_get_beneficial_owners

The 5%-and-over stakes in an issuer — the blockholder layer between Form 4 insiders and 13F portfolios. Input is the issuer, the company being held.

  • 13D is the activist form and carries the filer's stated purpose of the transaction; 13G is the passive form and has no purpose item at all, which is the substantive difference between a stake that intends to influence control and one that does not. Filter with form_kind

  • Every reporting person is listed separately. Voting power, dispositive power, and percent of class are reported per person even on a joint filing where several funds and their controlling principal report the same underlying shares — summing those percentages double-counts the position

  • Coverage starts 2024-12-18, when SEC replaced the legacy SC 13D / SC 13G text filings with structured XML under the current SCHEDULE 13D / SCHEDULE 13G names. Earlier stakes are readable but not parseable, and legacy_filings_before_coverage reports how many the issuer has — reach them with secedgar_search_filings and read them with secedgar_get_filing

  • Amendments carry the current position and are included by default; include_amendments=false leaves only the filings that opened a position

  • The full parsed set registers as a df_<id> dataframe at one row per reporting person, so it joins the insider and 13F dataframes on issuer CIK


secedgar_get_fund_holdings

What an ETF or mutual fund owns, from the NPORT-P portfolio report it files each quarter — the inverse of the ownership tools, which answer who owns a company.

  • Input is the fund: a ticker (VOO), an SEC fund series ID (S000002839), or a CIK. Fund trusts are indexed by ticker and series rather than by name, so name the registrant by CIK unless the fund itself trades under that name (SPDR S&P 500 ETF Trust)

  • An NPORT-P covers exactly one fund series and a registrant trust files one report per series per period, so a trust running several funds needs the specific fund named. A registrant that resolves to more than one series comes back with the series listed, each with its ticker; one whose series carry no ticker is routed by reading the series off its newest report, because a trust's own filing history interleaves funds whose fiscal quarters end on different months

  • Every result is dated to report_period_date. Reports publish roughly two months after the period they cover, so the holdings are the portfolio as of that date, not as of today; publication_lag_days states the gap. Target an earlier period with report_date, chosen from the available_report_periods in any response

  • 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

  • Positions come back largest first by percent of net assets, one page of limit rows from offset. A broad index fund reports thousands — Vanguard Total Stock Market's most recent report carries 3,524 — so the full report registers as a df_<id> dataframe for aggregation and for joining the 13F and insider dataframes on CUSIP


secedgar_get_material_events

A company's 8-K history with item codes decoded and filterable — the only surface that can scope by what the event actually was rather than by form.

  • Filter with items (e.g. ["2.02"] for results of operations, ["5.02"] for officer departures, ["4.02"] for non-reliance); secedgar_search_filings and secedgar_company_search cannot see items at all

  • Two numbering regimes are both accepted and decoded: the dotted scheme in force since 2004-08-23, and the single integers before it (legacy 12 is the ancestor of 2.02, 9 of 7.01). Decoding keys off the code's shape, so a filing straddling the changeover is never mis-decoded, and a window spanning it needs both codes in the filter

  • item_distribution counts every code across the scanned window before the filter, so a zero-hit filter comes back with the items that are present rather than a dead end

  • A date window pages into the older submissions archive, reaching 8-K filings that predate the ~1000-filing recent window; history_scanned_through discloses the scan depth

  • The full decode table is in the secedgar://filing-types resource

  • The full filtered set materializes as a df_<id> dataframe with item codes on every row — item frequency over time is one secedgar_dataframe_query away


secedgar_fetch_frames

Fetch SEC XBRL frames for one concept × one period across all reporting companies.

  • Same friendly concept names as secedgar_get_financials

  • Supports annual (CY2023), quarterly (CY2024Q2), and instant (CY2023Q4I) periods

  • Inline response returns one page of the ranked companies (sort + limit), with ticker enrichment

  • Walk further down the ranking with offset — the response echoes the effective offset and returns next_offset while companies remain, so ranks past the first page stay reachable even when the canvas is disabled

  • The full frames response (all reporters, typically 2k–10k rows) is materialized as a df_<id> dataframe — query it with secedgar_dataframe_query

  • 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


secedgar_compare_companies

Compare 2-10 named companies across 1-8 concepts, aligned on calendar periods — the middle shape between secedgar_get_financials (one company over time) and secedgar_fetch_frames (one period across the market).

  • One companyfacts read per company, resolved through the same frame dedup and tag priority as secedgar_get_financials

  • 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

  • periods bounds the inline matrix (1-12, default 4) and the window shrinks further when companies x concepts x periods is too large to return in one response; the full aligned series is always materialized as a df_<id> dataframe for growth rates and spreads via secedgar_dataframe_query

  • A company that fails to resolve is reported in failed_companies with a machine-readable reason 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

  • caveats surface a filer missing one or two calendar quarters, a concept that resolved to a retired XBRL tag for one company, period ends that differ inside one aligned period, and concepts whose unit differs across companies


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 taxonomy

  • Reverse-lookup raw tags like NetIncomeLoss to the supported friendly names

  • 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

  • Filtering by taxonomy: "ifrs-full" narrows the catalog to concepts with an IFRS tag confirmed against live 20-F filings; a concept with no IFRS equivalent is left out rather than mapped to a guess

  • Returns the same catalog used by secedgar_get_financials, secedgar_fetch_frames, and secedgar://concepts


secedgar_dataframe_describe / secedgar_dataframe_query / secedgar_dataframe_drop

In-conversation SQL analytics over the dataframes that the data-returning secedgar_* tools materialize on a shared DuckDB-backed canvas. Any call whose response carries a dataset field holds a df_XXXXX_XXXXX handle: read its columns with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query — 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_drop is 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_as chaining. secedgar_dataframe_query can persist its result as a new dataframe (df_XXXXX_XXXXX) with a fresh TTL — pipe analyses without re-running the source query.

  • Capped results say so. When row_limit bounds the query, row_count_capped comes back true and row_count is that cap rather than a total — raise row_limit (max 10000) or use register_as to materialize the whole result, whose count is then exact.

Related MCP server: northbridge-diligence

Resources

URI

Description

secedgar://concepts

Common XBRL financial concepts grouped by statement, mapping friendly names to XBRL tags

secedgar://filing-types

Common SEC filing types with descriptions, cadence, and use cases, plus the full 8-K item-code decode tables for both numbering regimes

Prompts

Prompt

Description

secedgar_company_analysis

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; corporate-suffix normalization on the name passes and dotted share-class tickers (BRK.B) resolved to SEC's hyphenated form; near-match trigram suggestions on zero-result name and ticker queries; committed former-names.json asset 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-text

  • In-conversation SQL analytics: the data-returning secedgar_* tools materialize their full result as a DuckDB-backed canvas dataframe — inspect its columns with secedgar_dataframe_describe, then query it with secedgar_dataframe_query

  • No 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/mcp

Prerequisites

Installation

  1. Clone the repository:

git clone https://github.com/cyanheads/secedgar-mcp-server.git
  1. Navigate into the directory:

cd secedgar-mcp-server
  1. Install dependencies:

bun install
  1. Build:

bun run build

Configuration

All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:

Variable

Description

Default

EDGAR_USER_AGENT

Required. User-Agent header for SEC compliance. Format: "AppName contact@email.com". SEC blocks IPs without a valid User-Agent.

EDGAR_RATE_LIMIT_RPS

Max requests/second to SEC APIs. Do not exceed 10.

10

EDGAR_TICKER_CACHE_TTL

Seconds to cache the company tickers lookup file.

3600

EDGAR_DATASET_TTL_SECONDS

Per-table TTL for canvas-registered dataframes. Sliding window touched on every dataframe op.

86400

EDGAR_DATAFRAME_DROP_ENABLED

Set to true to expose secedgar_dataframe_drop — the only destructive tool on this server. Off by default; TTL handles cleanup, and the tool is still listed on the HTTP landing page as disabled, with the flag that enables it.

false

EDGAR_MIRROR_ENABLED

Enable the local SQLite mirror of company_tickers + XBRL company-facts so CIK resolution and financials read from disk instead of the live API. Node/Bun only (skipped on Workers). Bootstrap once with bun run mirror:init.

false

EDGAR_MIRROR_PATH

Directory holding the mirror SQLite databases.

./data/edgar-mirror

EDGAR_MIRROR_REFRESH_CRON

Cron for the in-process nightly refresh (HTTP transport only). Recommended 0 9 * * *. Omit to refresh out-of-band via bun run mirror:refresh.

EDGAR_MIRROR_FALLBACK_LIVE

When the mirror misses (not yet synced, or a filing newer than the last refresh), fall back to the live SEC API. Set false for strict mirror-only reads.

true

CANVAS_PROVIDER_TYPE

Canvas engine. Defaults to duckdb; set to none to disable the canvas (e.g. when running on Cloudflare Workers, where DuckDB has no V8-isolate build).

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

LOGS_DIR

Directory for log files (Node.js only).

<project-root>/logs

Running the server

Local development

  • 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

docker build -t secedgar-mcp-server .
docker run -e EDGAR_USER_AGENT="MyApp my@email.com" -p 3010:3010 secedgar-mcp-server

The 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 rebuilt

Project structure

Directory

Purpose

src/mcp-server/tools/definitions/

Tool definitions (*.tool.ts). Ten SEC EDGAR tools plus three dataframe_* tools for SQL analytics.

src/mcp-server/resources/definitions/

Resource definitions. XBRL concepts and filing types.

src/mcp-server/prompts/definitions/

Prompt definitions. Company analysis prompt.

src/services/edgar/

SEC EDGAR API client, XBRL concept mapping, HTML-to-text conversion.

src/services/canvas-bridge/

Adapter over the framework DataCanvas: df_<id> minting, all-nullable schema derivation, per-table TTL bookkeeping, bridge-layer system-catalog SQL deny.

src/config/

Server-specific environment variable parsing and validation with Zod.

tests/

Unit and integration tests, mirroring the src/ structure.

Development guide

See CLAUDE.md and AGENTS.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic

  • Use ctx.log for logging, ctx.state for storage

  • Register 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 test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An 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.
    8
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides 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
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for SEC EDGAR data, providing tools to look up companies, retrieve filings and documents, and access XBRL financial facts.
    60 npm
    MIT