sec-edgar-mcp
MCP server that lets an LLM read official SEC financial data, tracing every number back to a specific SEC filing, US-GAAP tag, and filing date.
README
SEC EDGAR MCP Server
A Model Context Protocol server that lets an LLM read official SEC financial data instead of recalling it from training. Every number an agent returns through these tools can be traced back to a specific SEC filing, a specific US-GAAP tag and a specific filing date.
Built against the 2026-07-28 MCP specification using the Python SDK v2.0.0.
Why this exists
Ask a language model for a company's revenue and it will answer from memory. The answer is often close, sometimes wrong, and never verifiable. For financial work that is unusable.
This server replaces recall with a lookup. But "just call the SEC API" is not enough either — SEC's XBRL data has several traps that produce silently wrong answers. The interesting part of this project is handling them.
Tools
| Tool | Purpose |
|---|---|
sec_edgar_get_company_profile |
Ticker → CIK, registrant name, SIC industry, fiscal year end |
sec_edgar_list_filings |
Recent filings with links, filterable by form type |
sec_edgar_get_concept_series |
Time series for one financial concept |
sec_edgar_list_available_concepts |
Which US-GAAP tags a company actually reports |
Every tool returns a Pydantic model, so MCP outputSchema is generated
automatically and clients consume the results type-safely.
Three traps this server handles
1. fy is the filing's year, not the data's year
SEC's companyconcept API attaches fy and fp to every fact. It is tempting
to read fy as the fiscal year of the value. It is not — it is the fiscal year
of the filing the value appeared in. A 10-K contains three years of
comparatives and all three carry the filing's fy.
Using fy naively shifted Apple's revenue series by two years without raising
an error. Periods here are determined only from start/end dates: annual is
300–400 days, quarterly is 60–120.
2. Fiscal year naming has no universal rule
Walmart's fiscal year ending 2026-01-31 is FY2026. Target's fiscal year ending 2026-01-31 is FY2025. Same end date, different label — Walmart names a fiscal year after the calendar year it ends in, Target after the year it starts in. No fixed rule gets both right.
So no rule is used. _fy_kaymasi() derives the offset per company from SEC's
own data: within each fy group, the latest-ending annual period is the
filing's own period, which anchors offset = fy − end_year. If no anchor
exists, the response sets fiscal_year_derived: false rather than guessing
silently.
3. Tag changes truncate history
Apple reported revenue under SalesRevenueNet before ASC 606 and under
RevenueFromContractWithCustomerExcludingAssessedTax after. Stopping at the
first tag that returns data silently dropped ten years of history.
Aliases merge every candidate tag. Where periods overlap, the most recently
filed value wins. Each point carries a source_tag so the provenance of every
number stays visible — different tags may not measure a concept identically,
and that difference is surfaced rather than hidden.
Usage
Concepts are requested by alias, not by raw XBRL tag:
sec_edgar_get_concept_series(ticker="MSFT", concept="revenue", limit=5)
Available aliases: capex, cash, eps_diluted, gross_profit,
net_income, operating_cash_flow, operating_income, revenue,
rnd_expense, stockholders_equity, total_assets, total_liabilities.
Raw US-GAAP tags are accepted too. When a concept is not found the error
message names the valid aliases and points at the discovery tool — errors are
written for the model to act on, not just to report failure.
Install
uv sync # or: pip install -e ".[dev]"
cp .env.example .env # set SEC_USER_AGENT to your name and email
The SEC requires automated clients to identify themselves with a contact email
in the User-Agent header and to stay under 10 requests per second
(SEC Webmaster FAQ). This
server self-limits to 8 req/s and refuses to start without SEC_USER_AGENT.
Where the variable comes from. The MCP server takes its environment from
whatever launches it — the env block in a Claude Desktop config, --env-file
in Docker, or your shell. The core package deliberately does not read .env;
that would add a runtime dependency that buys nothing on those paths. The local
scripts (dene.py, dogrula.py) do read .env, via python-dotenv from the
[dev] extra, so you don't have to export the variable in every new terminal.
Run
uv run mcp dev src/edgar_mcp/server.py # MCP Inspector
uv run sec-edgar-mcp # stdio, for Claude Desktop etc.
docker build -t sec-edgar-mcp . && docker run --env-file .env -p 8000:8000 sec-edgar-mcp
Claude Desktop config:
{
"mcpServers": {
"sec-edgar": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "edgar_mcp.server"],
"env": { "SEC_USER_AGENT": "Your Name you@example.com" }
}
}
}
Tests
pytest -q # HTTP layer mocked; never calls sec.gov
python arac/enjeksiyon.py # fault injection
python arac/sir_tarama.py --gecmis # secret scan, working tree + git history
python dogrula.py # live verification against real SEC data
Fault injection
A test that has never been observed to fail is not evidence. arac/enjeksiyon.py
deliberately breaks each protection in turn and asserts that the matching test
turns red, then restores the file and verifies the restore by hash.
This is not decorative. It caught two tests in this repo that passed while protecting nothing — in both cases the mock did not reproduce the real API's contract, so the code path under test was never exercised. It also catches injections that have gone stale after a refactor, which is why it runs in CI.
Secret scanning
arac/sir_tarama.py scans the working tree; --gecmis additionally scans git
history. The distinction matters: a secret that was committed and then removed
is gone from the files but still readable in history, and a working-tree-only
scan reports "clean" while the secret is public.
The scanner refuses to report clean when it cannot see full history — a shallow
clone returns exit code 2, not 0. CI therefore checks out with fetch-depth: 0.
A check that silently does nothing is worse than no check.
Live verification
Mocks cannot prove behaviour against the real system. dogrula.py checks the
fiscal-year derivation and the tag-merging logic against live SEC data for
companies with calendar-year, ending-year and starting-year fiscal conventions.
Project layout
src/edgar_mcp/server.py MCP tools and schemas
src/edgar_mcp/client.py SEC HTTP client, rate limiter, caching
tests/ mocked unit tests
arac/enjeksiyon.py fault-injection harness
arac/sir_tarama.py secret scanner
dogrula.py live verification against SEC
CLAUDE.md decision records (Turkish)
Code comments and decision records are in Turkish; the public interface — tool descriptions, schemas, error messages, this README — is in English.
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。