Secedgar Mcp Server

io.github.cyanheads由 GitHub 账号 cyanheads 发布v0.18.1更新于 Nov 4, 2026

Query SEC EDGAR filings, XBRL financials, and company data via MCP. STDIO or Streamable HTTP.

已验证Streamable HTTP可网页运行网页搜索与抓取金融数据与分析

概览

AI 生成的概览

查询 SEC EDGAR 文件、XBRL 财务数据、内部人交易与机构持股,并可对暂存结果执行 SQL 查询。

可以做什么
  • 按股票代码、名称或 CIK 查找公司,并按 accession number 搜索或读取文件,支持分页与按章节定位
  • 获取 XBRL 财务数据、单次调用的公司财务快照、多公司对比以及全市场概念数据
  • 读取 8-K 重大事件、Form 4 内部人交易、13F 机构持仓与反向持有人查询、13D/13G 大额股东,以及基金 NPORT-P 持仓
  • 查看并以只读 SQL 查询暂存的结果集,并浏览概念与文件类型参考资源
适用场景
适用于股票研究、尽职调查和财务分析,让助手直接阅读 SEC 原始文件与披露财务数据,而非依赖二手摘要。也适合查询机构、发行人与基金之间的持股及内部人交易情况。
运行要求
可作为远程 Streamable HTTP 端点直接连接、无需安装,也可通过 npx 在本地运行(需 Node.js)。SEC 要求设置 EDGAR_USER_AGENT,格式为「应用名 联系邮箱」;无需账号或 API 密钥。其余可选变量涉及限流、传输方式、认证模式、日志与本地镜像。
安装前请注意
EDGAR_USER_AGENT 必须填写真实联系邮箱,否则 SEC 会封禁 IP。远程端点由第三方运营,查询内容会经过该服务。数据框 SQL 为只读,删除数据框需通过 EDGAR_DATAFRAME_DROP_ENABLED 显式开启。文件文本属于不可信数据,结果可能被截断或不完整,工具会对此作出提示。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Secedgar Mcp Server,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/io.github.cyanheads%2Fsecedgar-mcp-server/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "secedgar": {
      "type": "http",
      "url": "https://secedgar.caseyjhand.com/mcp"
    }
  }
}

README

@cyanheads/secedgar-mcp-server

Query SEC EDGAR filings, XBRL financials, and company data via MCP. STDIO or Streamable HTTP.

16 Tools (+1 opt-in) • 2 Resources • 1 Prompt

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

ToolDescription
secedgar_company_searchFind companies and retrieve entity info with optional recent filings
secedgar_search_filingsSearch EDGAR filings since 1993: full text from 2001, archive browse before that
secedgar_get_filingFetch a filing's metadata and document text, paged or by section
secedgar_get_financialsGet historical XBRL financial data for one company and concept
secedgar_get_snapshotOne-call financial profile: the latest value of every supported concept
secedgar_get_material_events8-K filings with item codes decoded and filterable
secedgar_get_insider_transactionsForm 4 / 4-A insider transactions parsed from ownership XML
secedgar_get_institutional_holdings13F-HR quarterly holdings of one institutional manager
secedgar_find_holdersReverse 13F lookup: which managers reported holding an issuer
secedgar_get_beneficial_owners5%+ blockholders of an issuer from structured SCHEDULE 13D / 13G filings
secedgar_get_fund_holdingsETF and mutual fund portfolio holdings from the quarterly NPORT-P report
secedgar_fetch_framesOne XBRL concept × one period across every reporting company
secedgar_compare_companiesCompare named companies across several concepts, aligned on fiscal years or calendar quarters
secedgar_search_conceptsDiscover supported XBRL concept names or reverse-lookup a raw tag
secedgar_dataframe_describeList canvas dataframes with provenance, TTL, and schema
secedgar_dataframe_queryRun a single-statement SELECT across dataframes
secedgar_dataframe_dropDrop a canvas dataframe by name; opt-in via EDGAR_DATAFRAME_DROP_ENABLED=true

Resources

ResourceDescription
secedgar://conceptsXBRL financial concepts grouped by statement, mapping friendly names to XBRL tags
secedgar://filing-typesCommon 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

PromptDescription
secedgar_company_analysisStructured analysis of a company's SEC filings: financial trends, risk factors, material events

Skills

SkillDescription
secedgar-guideWhich tool answers each ownership question, where CIKs, accession numbers, CUSIPs, and fund series IDs come from, and the coverage limits to check before reporting data absent

The skill ships in skills/secedgar-guide/SKILL.md. Plugin hosts load it from the package, and MCP clients that support the Skills extension read it from the server as skill://secedgar-guide/SKILL.md.

Capability reference

secedgar_company_search tool

  • query takes a ticker (equities, ETFs, mutual funds), a current or former company name, or a CIK; include_filings (default on) adds up to filing_limit filings (default 10; a value up to 1,000 is served at the 50 inline cap), filtered by forms, filed_after, and filed_before
  • Returns SIC code, exchanges, fiscal year end, and state of incorporation, plus series_id / class_id for a fund ticker; fails as no_match (with near matches in data.suggestions) or, for a name matching several companies, ambiguous_company, listing up to 10 by CIK, name, and ticker to retry with, plus the full count in total_matches
  • A date or form filter can reach into the archive (up to 10 pages); history_scanned_through reports how far it got

secedgar_search_filings tool

  • Full-text query (phrases, OR, -exclusion, wildcard*, ticker: / cik: scoping), or browse by forms and/or entity with no query; filed_after / filed_before work alone (a lone filed_after runs through today, a lone filed_before starts at 2001-01-01, or at 1993-01-01 before 2001); limit up to 100 (default 20; a value up to 1,000 is served at 100), offset up to 9,999; only sort: "relevance" on a 2001-onward search without ticker: / cik: scope pages EDGAR server-side, where offsets reach 9,900 (a page of 100 there ends at its 10,000-document cap) and a deeper one fails as offset_out_of_range
  • 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 (scan reports the counts)
  • One row per filing, tagged with source (efts, submissions, full-index); full-text rows list matched_documents (filenames for secedgar_get_filing); the response carries total, form_distribution, total_is_exact, and total_documents for full-text searches

secedgar_get_filing tool

  • accession_number in dash or 18-digit form, optional cik to speed the lookup, document for an exhibit, include_xbrl for XBRL artifacts; content_limit 1,000–200,000 characters per page (default 50,000; up to 1,000,000 is served at 200,000)
  • Page with offset / next_offset until content_truncated is false, or jump with section (a heading such as item 1 or part i), then drop section to page on, since it outranks offset; the first page of a truncated document carries an outline of up to 50 headings with offsets
  • documents splits the filing into primary, exhibits, and auxiliary; binary entries (scans, PDFs, archives) fail as binary_document, and a section miss returns section_not_found with the outline
  • A structured XML form the filer's submissions feed records through SEC's stylesheet (a recent Form 4, SCHEDULE 13D, or NPORT-P) reads as SEC renders it, with its labels and line breaks, so section reaches its Items; documents still lists the raw XML, which document returns as filed. A holdings report whose raw file passes 600 KB reads as filed
  • A 2000–2001 document the archive doesn't serve under its listed name (0001.htm) is read under its accession-prefixed name, and document takes either spelling; if neither is served, the full submission <accession>.txt is read instead and the notice says so
  • Without cik, the filer is found through EDGAR full-text search and the accession number's prefix, then, when none of those directories serves the filing, through the filers on the filing's index page — so a pre-2001 filing made through a filing agent resolves on its own, and a full-text search outage falls back instead of failing the call

secedgar_get_financials tool

  • company (ticker or CIK) plus concept as a friendly name or raw XBRL tag; taxonomy us-gaap (default), ifrs-full, or dei; period_type annual, quarterly, or all; unit picks one SEC unit key (ZAR, USD, USD/EUR) when a concept is reported in several; limit 1–100 trims the inline series
  • Returns a deduplicated series, newest first, one value per calendar period with form, filed, accession_number, and tag, all in one unit (other units are named in caveats); tags_tried lists the tags checked, and an empty result fails as no_concept_data, no_frame_data, no_period_data, or no_unit_data
  • A concept that is neither a friendly name nor an UpperCamelCase tag fails as unknown_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, taxonomy us-gaap (default) or ifrs-full, and period_type annual, quarterly, or both (default); one companyfacts read covers every supported concept, and nothing is staged as a dataframe
  • Each lines entry reports the latest annual and quarterly value for a duration concept, or the latest instant value for balance-sheet and entity-info concepts, each with the tag it came from, under the line's taxonomy; concepts the filer doesn't report land in gaps with tags_tried

secedgar_get_material_events tool

  • company plus up to 20 items codes, dotted (2.02) since 2004-08-23 and single integers (12) before; the two regimes don't overlap, so pair them across the changeover; filed_after / filed_before work alone and reach into the archive; limit 1–1,000, served at 100 (default 20)
  • Each filing decodes its items to code, label, and regime (current / legacy); item_distribution counts every code in the window, and total_8k_scanned against total_matched shows what the filter removed
  • A date window reads up to 10 archive pages; history_scanned_through and dataset.truncated report what went unread

secedgar_get_insider_transactions tool

  • company is the issuer, as a ticker, CIK, or current or former name; a name matching several companies fails as ambiguous_company, listing up to 10 by CIK, name, and ticker to retry with, plus the full count in total_matches; transaction_type purchase (code P), sale (code S), or all (default); limit 1–1,000, served at 100 (default 20); does not cover Forms 3 or 5
  • Without a date window it scans up to 100 of the newest Form 4 / 4-A filings; filed_after / filed_before (inclusive, either alone) read any period since mid-2003, up to 100 filings; history_scanned_through names the oldest filing parsed
  • Each transaction carries the reporting person, relationship, transaction_code and transaction_type, is_derivative, unsigned shares_traded with direction (acquire / dispose), price per share, and shares owned after; dataset.truncated flags filings beyond those parsed, and filings_other_issuer counts 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

  • company is the 13F filer (a CIK is most reliable), not a portfolio company, which is secedgar_find_holders' job; quarter as YYYY-QN, defaulting to the newest filing; limit 1–500 (default 20; up to 1,000 is served at 500) with offset / next_offset; consolidate (default true) merges sub-lines into positions sorted by value
  • Older quarters are found in the archive (up to 10 pages); a quarter covered only by a 13F-NT notice fails as no_filings_found naming the notice's accession number and period
  • Holdings, paging, consolidation, and the dataframe cover the quarter composed from the original 13F-HR and its same-period 13F-HR/A amendments, applied in acceptanceDateTime order: a RESTATEMENT replaces everything composed so far (earlier NEW HOLDINGS included); a NEW HOLDINGS amendment appends its rows, unless its table repeats every composed row (CUSIP, class, put/call, discretion, shares, share type), in which case it applies as a restatement. Values normalize by each accession's own filing date. Only tables from the last readable restatement on are read
  • amendments (newest filed first) discloses each accession, filing date, and cover-page type (UNAVAILABLE with a reason when unreadable), plus applied_as: restatement, new_holdings, superseded, or not_applied (type or table unreadable, named in the notice); inspect any with secedgar_get_filing. composed_accessions lists the filings the rows come from, in application order. amendment_search_complete=false warns that bounded or unavailable archive discovery may have missed amendments
  • Holdings carry issuer, CUSIP, market_value_usd in whole USD, shares or principal, put_call, and source_accession_numbers (a consolidated position lists each filing it came from; also a dataframe column); total_holdings_in_filing counts the original 13F-HR's raw rows, total_holdings_composed the composed raw rows, and total_positions distinct composed positions; accession_number and filing_date name the original 13F-HR; a shared legal name fails as ambiguous_entity, listing up to 10 by CIK and name plus the full count in total_matches (EDGAR's own count for a name found by entity search, a floor when total_matches_is_lower_bound is true)

secedgar_find_holders tool

  • issuer as a ticker, CIK, or name, plus an optional 9-character cusip, the precise match key (a name phrase-match both over- and under-matches); quarter as YYYY-QN, defaulting to the newest quarter past its 45-day filing deadline; limit 1–100 (default 20; up to 1,000 is served at 100) from up to 500 fetched filings
  • Without cusip, the phrase is the issuer's registry name minus a trailing qualifier such as \DE\ or /OH/; when another registrant's name reduces to the same phrase (FIRST BANCORP /PR/ and /NC/), the holders can include that company's, and the notice names it with CIK and ticker and points to cusip
  • Rows carry filer_cik, accession_number, and form, while search_mode (cusip / name), total_filings, fetched, and holders_in_quarter size the result; the list is unranked, so pass a filer_cik to secedgar_get_institutional_holdings to read the position
  • When matching documents exceed the fetch budget, the notice gives an exact secedgar_search_filings continuation with the query, dates and document offset. Filter results.period_ending to the quarter end, then deduplicate across the sample's filer_cik and search results' cik, keeping the latest filing_date. Advance by 100 documents per full page up to offset 9,900, whose page ends at EDGAR's 10,000-document paging ceiling; lower-bound totals may hide more beyond it

secedgar_get_beneficial_owners tool

  • issuer is the company being held; form_kind all (default), 13D, or 13G; include_amendments (default true); limit 1–20 matching parsed filings (default 10; up to 1,000 is served at 20). The newest-first scan examines at most 50 documents, excluding schedules naming another issuer; exclusions and failed documents do not consume limit
  • Each filing lists reporting_persons with voting power, dispositive power, and percent_of_class per person, which joint filers report for the same shares, so they don't sum; a 13D carries purpose_of_transaction, a 13G has none
  • purpose_of_transaction and each person's notes come back as filed, line breaks included, up to 5,000 and 2,000 characters; a longer value keeps its first characters and sets purpose_truncated or notes_truncated, and the notice points to the whole text in the dataframe's purpose_of_transaction and notes columns or through secedgar_get_filing
  • Coverage starts 2024-12-18 with the structured XML schedules; legacy_filings_before_coverage counts the issuer's older SC 13D / SC 13G text filings
  • total_structured_filings partitions into filings_examined + filings_not_examined; examined documents partition into filings_parsed + filings_excluded_other_issuer + filings_failed.length. Failed accessions carry document_not_found (404) or unparseable (including missing issuer identity), and set partial. truncated means indexed schedules remain unexamined after the result limit or scan ceiling, independently of failures

secedgar_get_fund_holdings tool

  • fund as a ticker (VOO), series ID (S000002839), or CIK; a multi-series trust fails as series_required with the series listed, so pass series_id; report_date picks a period from available_report_periods; limit 1–100 (default 20; up to 1,000 is served at 100) with offset / next_offset
  • Positions come largest first by percent_of_net_assets, with name, CUSIP / ISIN / LEI, balance and units, value_usd, and asset and issuer category, alongside fund net assets, total assets, and total liabilities. Position currency is 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 before filing_date; publication_lag_days states the gap

secedgar_fetch_frames tool

  • concept as a friendly name or raw tag, period as CY2023, CY2024Q2, or CY2023Q4I, taxonomy us-gaap (default) or dei, unit (default USD), sort desc / asc; limit 1–100 (default 25; up to 1,000 is served at 100) with offset / next_offset
  • One call queries one tag: unqueried_tags and related_tags list variants to fetch separately; value_distribution.max_to_p95_ratio flags scale outliers, period_end_range shows fiscal-year mixing, and caveats flags fiscal-Q4 gaps
  • A concept that is neither a friendly name nor an UpperCamelCase tag fails as unknown_concept; one whose tag has no frame for the period is no_data, which names the tag read and lists a friendly name's other tags in unqueried_tags (older periods often sit under an older tag: revenue before CY2014 is under Revenues or SalesRevenueNet)

secedgar_compare_companies tool

  • 2–10 companies × 1–8 concepts (up to 100 of each are accepted: the first 10 companies and first 8 distinct concepts are compared, and the rest come back unread in deferred_companies / deferred_concepts, with a notice naming the re-calls that compare them); taxonomy us-gaap (default) or ifrs-full; period_type annual (default) or quarterly; periods 1–12 (default 4; up to 1,000 is served at 12), trimmed further when the inline matrix gets too large
  • cells align each value on a calendar period and keep its frame, period_end, and source tag; a call with no comparable annual data fails no_comparable_data
  • failed_companies (reason not_found, ambiguous, or no_company_facts) and gaps report what's missing, and caveats flag differing period ends and unit mismatches between companies; a company named twice (a ticker and its CIK) is read once, under the first input
  • An unrecognized concept is listed in unknown_concepts with a hint; the call fails as unknown_concept only when every concept is unrecognized

secedgar_search_concepts tool

  • search is a substring over friendly name, label, and tags, so a raw tag like NetIncomeLoss reverse-maps to its friendly name; group and taxonomy filter; no arguments returns the full catalog
  • Each concept lists tags, ifrs_tags (only where an IFRS element was confirmed in live 20-F filings), related_tags, unit, and group

secedgar_dataframe_describe tool

  • Optional name for one dataframe; omit it to list the tenant's active dataframes, newest first
  • Each entry carries source_tool, query_params, created_at / expires_at, row_count, truncated, and the column_schema that SQL for secedgar_dataframe_query has to match

secedgar_dataframe_query tool

  • One DuckDB SELECT in sql (joins, aggregates, window functions, CTEs); row_limit 1–10,000 (default 1,000) and preview for fewer inline rows, each served at 10,000 up to 100,000, and register_as (df_XXXXX_XXXXX) to save the result as a new dataframe
  • Returns columns, rows, row_count, and row_count_capped, which when true means row_count is the cap, not a total; BIGINT columns serialize as strings
  • Writes, DDL, file-reading functions, multiple statements, and system catalogs are rejected with typed reasons (non_select_statement, denied_function, multi_statement, system_catalog_access, and others); an unqualified current_schema is refused as denied_function even when it names a column, so read such a column as <table>.current_schema

secedgar_dataframe_drop tool

  • name of the dataframe to drop; idempotent, returning dropped: false when nothing matched
  • Off unless EDGAR_DATAFRAME_DROP_ENABLED=true; when disabled it is absent from tools/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 concept
  • The names are what secedgar_get_financials, secedgar_compare_companies, and secedgar_fetch_frames accept as concepts

secedgar://filing-types resource

  • Common SEC forms with cadence and use cases, as text/markdown
  • Includes the 8-K item-code tables for both numbering regimes, the vocabulary of secedgar_get_material_events' items filter

secedgar_company_analysis prompt

  • Arguments: company required; focus_areas optional free text
  • Returns one user message with a numbered workflow (company search, a financial profile via secedgar_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 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_limited with a retryAfter countdown for EDGAR_RATE_LIMIT_COOLDOWN_SECONDS, then one probe goes out
  • CIK 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, and secedgar_compare_companies share the same dedup and tag priority, so their numbers agree, and caveats flag gaps in the series
  • Filing 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 dataset field has staged its full result as a DuckDB dataframe (df_<id>), while the inline list stays capped at limit; inspect it with secedgar_dataframe_describe, then query it with secedgar_dataframe_query
  • Discriminated outputs and explicit gaps: source on filing-search rows, search_mode, 8-K item regime, typed failed_companies reasons, and gaps with tags_tried in place of zero-filled values
  • Completeness disclosure: history_scanned_through, total_is_exact, publication_lag_days, and dataset.truncated tell agents how deep a scan went and what it left out
  • One parameter name per concept: company, filed_after / filed_before, and forms mean the same thing on every tool, and common alternate spellings (ticker, cik, start_date, end_date, form_types, and others) are accepted as aliases, as is filing_limit for limit on secedgar_search_filings, secedgar_get_material_events, and secedgar_get_beneficial_owners, where limit counts filings

Getting started

Connect to the hosted instance, no install:

sh
claude mcp add --transport http secedgar-mcp-server https://secedgar.caseyjhand.com/mcp

Or run it locally:

sh
claude mcp add secedgar-mcp-server -e EDGAR_USER_AGENT="YourAppName [email protected]" -- npx -y @cyanheads/secedgar-mcp-server

For other clients, add this to the MCP config file:

json
{  "mcpServers": {    "secedgar-mcp-server": {      "command": "npx",      "args": ["-y", "@cyanheads/secedgar-mcp-server"],      "env": { "EDGAR_USER_AGENT": "YourAppName [email protected]" }    }  }}

SEC requires a User-Agent in "AppName [email protected]" form on every request; no account or key is needed. For Docker or a source build, see Running the server.

Configuration

VariableDescriptionDefault
EDGAR_USER_AGENTRequired. User-Agent sent to SEC, as "AppName [email protected]". SEC blocks IPs without one. A value that can't be sent as an HTTP header (an em dash or curly quote, an inner line break) stops the server at startup with an error naming this variable.—
EDGAR_RATE_LIMIT_RPSMax requests per second to SEC (1–10).10
EDGAR_RATE_LIMIT_COOLDOWN_SECONDSSeconds to refuse calls locally after a 429 before one probe goes out.600
EDGAR_TICKER_CACHE_TTLSeconds to cache the company and fund ticker files.3600
EDGAR_DATASET_TTL_SECONDSSeconds a canvas dataframe lives, counted from when it is staged; queries do not extend it.86400
EDGAR_DATAFRAME_DROP_ENABLEDSet true to expose secedgar_dataframe_drop, the only destructive tool.false
EDGAR_CLAMP_OVERSIZED_LIMITSServe an over-cap limit, content_limit, periods, preview, or row_limit at its cap, and answer an over-cap companies or concepts list up to its cap with the rest returned unread, instead of rejecting it. Set false to reject above the cap.true
EDGAR_MIRROR_ENABLEDEnable the local SQLite mirror of company tickers and XBRL company-facts. Node/Bun only; bootstrap once with bun run mirror:init.false
EDGAR_MIRROR_PATHDirectory holding the mirror databases../data/edgar-mirror
EDGAR_MIRROR_REFRESH_CRONIn-process refresh cron (HTTP transport only), e.g. 0 9 * * *. Omit to refresh with bun run mirror:refresh.—
EDGAR_MIRROR_FALLBACK_LIVEFall back to the live SEC API on a mirror miss. Set false for mirror-only reads.true
CANVAS_PROVIDER_TYPECanvas engine; none disables dataframes.duckdb
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOG_TOOL_FAILURE_PAYLOADSLog 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_ENABLEDEnable OpenTelemetry instrumentation (spans, metrics, completion logs).false
OTEL_EXPORTER_OTLP_ENDPOINTOTLP base URL for traces and metrics.—
OTEL_EXPORTER_OTLP_LOGS_ENDPOINTOTLP log export endpoint (e.g. http://localhost:4318/v1/logs).—

See .env.example for the full list of optional overrides.

Running the server

Local development

Requires Bun v1.4.0 or higher (or Node.js v24+).

  • Clone and install:

    sh
    git clone https://github.com/cyanheads/secedgar-mcp-server.gitcd secedgar-mcp-server && bun installcp .env.example .env   # set EDGAR_USER_AGENT
  • Build and run:

    sh
    bun run rebuildbun run start:http   # or start:stdio; HTTP listens at http://localhost:3010/mcp
  • Run checks and tests:

    sh
    bun run devcheck     # Lint, format, typecheck, securitybun run test         # Vitest test suitebun run lint:mcp     # Validate MCP definitions against spec

Docker

sh
docker run --rm -e EDGAR_USER_AGENT="YourAppName [email protected]" -p 3010:3010 ghcr.io/cyanheads/secedgar-mcp-server:latest

The image defaults to HTTP transport on port 3010, stateless session mode, and logs to /var/log/secedgar-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "-e", "EDGAR_USER_AGENT=YourAppName [email protected]", "ghcr.io/cyanheads/secedgar-mcp-server:latest"]. To build locally, docker build -t secedgar-mcp-server .; OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

The image ships the mirror CLI, so a running container can bootstrap and refresh its own mirror:

sh
docker exec <container> bun run mirror:verify    # sync status + sample readsdocker exec <container> bun run mirror:init      # one-time bootstrap from the SEC bulk archivedocker exec <container> bun run mirror:refresh   # re-ingest after SEC rebuilds the archive

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point: registers resources and prompts, starts the SEC client, canvas, and optional mirror.
src/configServer environment variable parsing and validation with Zod.
src/mcp-server/tools/definitionsTool definitions (*.tool.ts) and the buildToolDefinitions() registration list.
src/mcp-server/resources/definitionsResource definitions: XBRL concepts and filing types.
src/mcp-server/prompts/definitionsPrompt definitions: company analysis.
src/services/edgarPaced 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/mirrorOpt-in local SQLite mirror of company tickers and XBRL company-facts.
src/services/canvas-bridgeAdapter over the framework DataCanvas: df_<id> naming, per-table TTL, system-catalog SQL deny.
scriptsBuild, devcheck, and lint tooling, plus the mirror:* commands.
testsUnit 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 in buildToolDefinitions() (src/mcp-server/tools/definitions/index.ts), and resources and prompts in the createApp() arrays in src/index.ts
  • Wrap external SEC EDGAR calls: validate the raw response → normalize to a domain type → return the output schema; never fabricate a missing XBRL field — report it under gaps instead

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheckbun run test

License

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

来源:README.md,提交 0a336f8

工具

16

secedgar_search_filings

Secedgar Search Filings

Search EDGAR filings since 1993. Full-text search covers 2001-present (the EFTS index floor); pre-2001 date ranges (to 1993) are served from the archives by form and entity/date. Pre-2001 free text needs entity scope (ticker:/cik:) — with it, the tool reads the entity's matching filings and matches the terms locally, which costs a few seconds (SEC's request rate caps the scan at roughly 5s for the 50-document maximum). A range crossing 2001-01-01 is split at the boundary and the two eras merged, each row tagged with its source. Supports exact phrases, boolean operators, wildcards, and entity targeting (ticker:AAPL or cik:320193 in query). When the match set outruns the inline list it is also staged as df_<id> — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query.

输入参数sortformslimitqueryoffsetfiled_afterfiled_before

secedgar_get_filing

Secedgar Get Filing

Fetch a specific filing's metadata and document content by accession number. Returns the primary document as readable text. Use offset/next_offset for multi-page access to large filings (10-K, S-1 can exceed 1M chars): pass the next_offset from a truncated response as offset, without section, to read the next page. Use section to jump directly to a heading (e.g. 'risk factors', 'item 7') without needing an offset.

输入参数cikoffsetsectiondocumentinclude_xbrlcontent_limitaccession_number必填

secedgar_get_financials

Secedgar Get Financials

Get 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. The full series is also staged as df_<id> when a canvas is available — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query.

输入参数unitlimitcompany必填concept必填taxonomyperiod_type

secedgar_get_snapshot

Secedgar Get Snapshot

Build a company financial profile in one call: the latest value of every supported XBRL concept, grouped by statement. Reads the filer's complete companyfacts payload once rather than one request per concept, so it replaces a run of secedgar_get_financials calls when the question is "what do this company's financials look like right now". Values use the 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, since that is the only form they are filed in. A concept the filer does not report is listed under gaps with the XBRL tags that were tried — never zero-filled or interpolated. Use secedgar_get_financials for a full time series of one concept, and secedgar_compare_companies to put several companies side by side.

输入参数company必填taxonomyperiod_type

secedgar_get_material_events

Get Material Events

Retrieve a company's 8-K filings with their item codes decoded, optionally filtered to specific items. 8-K item codes are how material events are actually scoped — 1.01 material agreements, 2.02 results of operations, 4.02 non-reliance on previously issued financials, 5.02 officer and director departures — and filtering by them is narrower than any form-level filter in secedgar_search_filings or secedgar_company_search, neither of which can see items. Each row carries the accession number and primary document for secedgar_get_filing; press releases usually ride as EX-99 exhibits rather than in the primary document. Two numbering regimes exist: filings from 2004-08-23 onward use the x.xx codes, earlier ones use single integers (12 was the old results-of-operations item, 9 the old Regulation FD item), and both are accepted as filters and decoded in the response. A date window reaches filings older than the recent submissions window by reading every archive page it overlaps, up to 10; without one, the scan reads back only as far as it needs to fill limit with filings passing the items filter, within the same 10 pages. Every scanned filing that passes the filter is materialized as df_<id> for item-distribution analysis over time (pass a date window to cover a longer span) — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query.

输入参数itemslimitcompany必填filed_afterfiled_before

secedgar_get_insider_transactions

Get Insider Transactions

Fetch 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), price per share, and shares owned after the transaction. Covers nonDerivative transactions (open-market buys/sells, gifts) and derivative transactions (option exercises, RSU vests). Without a date window it reads the newest Form 4 filings; filed_after / filed_before read any period since mid-2003, reaching past the recent submissions window into the archive (e.g. insider trades in the quarter before an earnings miss). When a canvas is available, the full set of transactions parsed from the scanned filings is materialized as df_<id> (the inline list is a preview capped at limit) — inspect it with secedgar_dataframe_describe, then query it with secedgar_dataframe_query to aggregate net buy/sell by insider: SUM(CASE WHEN direction='dispose' THEN -shares_traded ELSE shares_traded END). Use secedgar_search_filings with forms=["4"] to search Form 4 filings across all companies.

输入参数limitcompany必填filed_afterfiled_beforetransaction_type

secedgar_get_institutional_holdings

Get Institutional Holdings

Read an institutional manager's 13F-HR information table for a quarter. company is the filer: a 10-digit CIK, ticker, or name; names outside the ticker file use EDGAR entity search, and ambiguous names return candidates. For managers holding an issuer, use secedgar_find_holders and pass its filer_cik here. Positions carry issuer, CUSIP, shares, whole-USD market value, option designation, and the accessions they came from. Same-period 13F-HR/A amendments are composed in acceptance order: a RESTATEMENT replaces the rows, a NEW HOLDINGS amendment adds its rows, and each amendment reports how it was applied. By default, sub-lines consolidate into positions sorted by value; consolidate=false preserves raw filing order, each filing's rows after the last. Page with limit, offset and returned next_offset. When canvas is available, the full composed positions are staged as df_<id>: inspect with secedgar_dataframe_describe, then aggregate or join on cusip + reporting_period with secedgar_dataframe_query. Managers below $100M in 13(f) securities may not file. Use secedgar_search_filings with forms=["13F-HR"] for broader search.

输入参数limitoffsetcompany必填quarterconsolidate

secedgar_find_holders

Find Holders

Find which institutional managers reported holding an issuer, by searching 13F-HR information tables for one reporting quarter. This is the reverse direction of secedgar_get_institutional_holdings: that tool takes a manager and returns its portfolio, this one takes an issuer and returns its managers — pass a returned filer_cik plus the same quarter to read the actual position. Searching by cusip is the precise path, matching the identifier the information table itself carries; without it the issuer name is matched as a phrase against the filing text, which both over-matches (unrelated issuers sharing a word) and under-matches (managers writing the name differently), so prefer cusip whenever one is known. A CUSIP cannot be derived from a ticker here — read one off any 13F information table returned by secedgar_get_institutional_holdings. The returned list is unranked: the search index scores by text relevance, which carries no signal about position size, and no ordering by shares or market value is available without opening each filing. Managers holding under $100M in 13(f) securities are exempt from filing at all. When more managers match than fit inline, the full fetched set is staged as df_<id> — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query.

输入参数cusiplimitissuer必填quarter

secedgar_get_beneficial_owners

Get Beneficial Owners

List the 5%-and-over beneficial owners of a public company, parsed from the structured SCHEDULE 13D and SCHEDULE 13G filings made about it. The input is the ISSUER — the company being held — which is the opposite direction from secedgar_get_institutional_holdings, where the input is the manager. 13D is the activist form and carries the filer's stated purpose of the transaction; 13G is the passive form and has no purpose field at all, which is the substantive difference between a stake that intends to influence control and one that does not. Every filing is returned with each reporting person listed separately, because 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 overlapping shares — summing those percentages double-counts the same position. Coverage starts at 2024-12-18, when SEC replaced the legacy SC 13D / SC 13G text filings with this XML format; earlier stakes are readable but not parseable, and the response reports how many of them the issuer has. The full parsed set is materialized as df_<id> when a canvas is available, one row per reporting person, so it joins against the insider dataframes on issuer CIK and the 13F and NPORT-P dataframes on CUSIP — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query.

输入参数limitissuer必填form_kindinclude_amendments

secedgar_get_fund_holdings

Get Fund Holdings

List what an ETF or mutual fund holds, parsed from the NPORT-P portfolio report it files with the SEC every quarter. The input is the fund — a ticker like VOO, a fund series ID, or the registrant trust — which is the opposite direction from the ownership tools: secedgar_get_institutional_holdings and secedgar_find_holders answer who owns a company, this answers what a fund owns. Each position carries the security name, CUSIP/ISIN/LEI where the filer reports them, share balance, market value in USD, and percent of the fund's net assets, alongside fund-level net assets and total assets. Positions are returned largest-first by percent of net assets, one page of limit rows starting at offset; the full report registers as df_<id> when a canvas is available — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query, which is how a fund running to thousands of positions is aggregated or joined against the 13F and insider dataframes. An NPORT-P covers exactly one fund series and a registrant trust files one report per series, so a trust with several funds needs the specific fund named — pass its ticker or series_id. Reports publish roughly two months after the period they cover, so every result is dated: the holdings are the portfolio as of report_period_date, not as of today.

输入参数fund必填limitoffsetseries_idreport_date

secedgar_fetch_frames

Secedgar Fetch Frames

Fetch SEC XBRL frames for one concept × one period across all reporting companies. Inline response returns a page of the ranked companies — start at the top or pass offset/next_offset to walk further down the ranking; the full frames response (all reporters) is materialized as df_<id> when a canvas is available — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query. Accepts friendly names like "revenue" or "assets" (discover via secedgar_search_concepts) or raw XBRL tags. One call hits one XBRL tag — when a friendly name maps to multiple same-meaning tags, the response's `unqueried_tags` lists the others; call again per tag and UNION/COALESCE in SQL with an analysis-specific priority (e.g. SalesRevenueGoodsNet is goods-only). The response's `related_tags` separately flags alternate-DEFINITION tags a meaningful share of filers use as their primary line (e.g. cash incl. restricted cash, equity incl. noncontrolling interest) — a whole-universe screen on the base tag silently omits those filers; query them separately, but do not blindly union (the semantics differ). Response includes `value_distribution` and `period_end_range` to flag XBRL scale-factor anomalies and fiscal-year mixing. SEC publishes frames for us-gaap and dei tags only, and `taxonomy` picks which of the two a raw tag is read from (dei for cover-page tags such as EntityCommonStockSharesOutstanding); a friendly name keeps its own mapped taxonomy. There are no ifrs-full frames, so IFRS (20-F) filers are absent from every frame; read them per company with secedgar_get_financials or secedgar_compare_companies under taxonomy ifrs-full.

输入参数sortunitlimitoffsetperiod必填concept必填taxonomy

secedgar_compare_companies

Secedgar Compare Companies

Compare 2-10 named companies across 1-8 XBRL concepts, aligned on fiscal years (annual) or calendar quarters (quarterly). This is the middle shape between secedgar_get_financials (one company, one concept, full history) and secedgar_fetch_frames (one concept, one period, every reporting company) — reach for it when the question names the companies. One companyfacts read per company, resolved through the same frame dedup and tag priority as secedgar_get_financials so the numbers agree. Balance-sheet and entity-info concepts are filed as point-in-time values: an annual matrix takes each company’s fiscal-year-end snapshot and places it in the period of the fiscal year it closes, beside that year’s income-statement lines (interim and cover-date snapshots stay out), and a quarterly matrix aligns each snapshot on the calendar quarter it falls in. The inline matrix covers the most recent periods up to `periods`, trimmed further when companies x concepts x periods is too large to return in one response; the full aligned series is materialized as df_<id> for growth rates and spreads — inspect it with secedgar_dataframe_describe, then analyze it with secedgar_dataframe_query. A company that fails to resolve is reported in failed_companies and the comparison proceeds with the rest, and a company that does not report a concept is reported in gaps with the tags that were tried — never interpolated or zero-filled. A company named twice (a ticker and its CIK) is read once, under the first input. Where the schema accepts a longer list, companies past the first 10 and concepts past the first 8 distinct are not read; they come back in deferred_companies and deferred_concepts to re-call with. A company that reports a concept only for periods older than the inline window is named in caveats with its newest period. A concept that is neither a friendly name nor an XBRL tag is reported once in unknown_concepts with the closest supported names, and fails the call only when every concept is

输入参数periodsconcepts必填taxonomycompanies必填period_type

secedgar_search_concepts

Secedgar Search Concepts

Search supported XBRL financial concepts by keyword, statement group, or taxonomy. Use before secedgar_get_financials, secedgar_compare_companies, 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. Empty search with no filters returns the full catalog.

输入参数groupsearchtaxonomy

secedgar_dataframe_describe

Secedgar Dataframe Describe

List the dataframes (df_XXXXX_XXXXX) registered by the data-returning secedgar_* tools — any tool whose response carries a `dataset` handle stages its full result set here. Each entry surfaces source tool, query parameters, creation/expiry timestamps, row count, column schema, and whether the dataframe is truncated relative to the upstream source. Read the column schema here before writing SQL for secedgar_dataframe_query.

输入参数name

secedgar_dataframe_query

Secedgar Dataframe Query

Run a single-statement SELECT against the canvas dataframes registered by the data-returning secedgar_* tools — any tool whose response carries a `dataset` handle. Inspect a dataframe with secedgar_dataframe_describe first; its column schema is what the SQL has to match. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, and external-file table functions are rejected. System catalogs (information_schema, pg_catalog, sqlite_master, duckdb_*) are denied — list dataframes via secedgar_dataframe_describe. Optional register_as chains the result as a new dataframe with a fresh TTL.

输入参数sql必填previewrow_limitregister_as

版本历史

15
  1. v0.18.1最新Nov 4, 2026
  2. v0.18.0Nov 3, 2026
  3. v0.17.1Nov 3, 2026
  4. v0.17.0Oct 26, 2026
  5. v0.16.0Oct 25, 2026
  6. v0.15.15Oct 24, 2026
  7. v0.15.14Oct 23, 2026
  8. v0.15.13Oct 18, 2026
  9. v0.15.12Oct 14, 2026
  10. v0.15.11Oct 4, 2026
  11. v0.15.10Oct 4, 2026
  12. v0.15.9Sep 25, 2026
  13. v0.15.8Sep 24, 2026
  14. v0.15.7Sep 22, 2026
  15. v0.15.6Sep 16, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分