SEC Ownership Graph
This server provides a curated set of read-only tools for querying a graph of SEC ownership filings (13D/13G, Form 3/4/5, 13F) to analyze corporate control, activism, and board interlocks.
influence_map: Identify issuers where a large stakeholder also holds a current board seat (combining 13D ownership and Form 3/4/5 activity), filterable by Fed control-presumption tier and minimum issuer size.activist_convergence: Screen for issuers targeted by multiple known activist investors (e.g., Icahn, Elliott, Starboard) within a specified time window, returning filing sequences with days-apart spans.campaign_timeline: Show the chronological sequence of 13D/13G filings for an issuer, classifying filers as activist, insider, passive index fund, or custodian, and highlight first movers.control_chain: Trace transitive ≥50% ownership control chains upward or downward through an issuer, with citations to SEC accession numbers; abstains when no verified edge exists.board_interlock_path: Identify directors who bridge the boards of two companies, returning the shortest path and the connecting directors.activist_coalition: Discover de-facto activist coalitions (wolf-packs) by linking activists that co-target multiple issuers, with custodial/broker hubs scrubbed.ownership_snapshot: Provide a quick overview of an issuer's top beneficial holders, director/officer counts, and control status.get_secgraph_schema: Retrieve a curated summary of the graph schema, relationship semantics, and available tools for agent grounding.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@SEC Ownership Graph Which companies have multiple activists and who moved first?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
SEC Ownership Graph
Every SEC ownership filing as one graph, keyed on CIK — so you can ask who moved first on a target, who operates as a coalition, and who really controls an issuer. Built on Neo4j. Every answer cites an SEC accession number, and abstains when the data doesn't support one.
Data: Schedule 13D/13G (beneficial ownership), Form 3/4/5 (insiders and directors), Form 13F (institutional holdings). Straight from EDGAR — no paid data vendor. Depth per layer, and what that does and doesn't support, is in Honest limits.
New to SEC filings? docs/data_sources_and_forms.md explains
what each form is, what triggers it, and exactly which node or edge it became — including which
four edges are derived rather than filed. Start there if the form names above aren't familiar.
The demo in one question
"Who can actually move this company — and can you prove it from two independent filings?"
$ make demo
Stake + board seat — issuers where a holder clears a Fed control-presumption tier
AND currently sits on the board. Two independent filing types agreeing:
TICKER SIZE STAKE 13D SEAT HOLDER
BRK-B $479.9B 37.0% 2024 2026-05 BUFFETT WARREN E
TMUS $92.6B 74.3% 2013 2025-10 DEUTSCHE TELEKOM AG
CHTR $22.4B 26.1% 2014 2026-06 Liberty Broadband Corp
RDDT $20.5B 61.5% 2024 2026-06 Huffman Steve LaddThe stake comes from a Schedule 13D; the board seat comes from Form 3/4/5. A screener sells you either list — the pairing is the finding, and it is the join a single-table query cannot do for you. Note the two date columns: Liberty Broadband declared 26.1% of Charter in 2014, and its director was on file in 2026. A 13D has no exit obligation below 5%, so an old stake proves nothing alone; the current seat is what corroborates it.
And when there is nothing to say, it says so — ten of ten mega-caps abstain on control:
$ control_chain("AAPL")
No graph-grounded answer for 'Apple Inc.' (no_verified_control_chain).
Issuer has no >=50% verified 13D control edge on a chain.→ Full walkthrough: docs/demo_script_governance_desk.md
(five questions, ~10 minutes, including what this cannot tell you.)
Related MCP server: edgar-mcp
What is actually graph-native here
Three questions need a relationship followed to a depth the data decides — one declarative Cypher pattern, executed next to the data:
Question | Cypher | Why a traversal |
Who ultimately controls this issuer? |
| chain depth is unknown up front |
Who operates as a coalition with X? |
| the component is emergent, not a fixed join |
Who bridges these two boards? |
| reachability, not "who sits on ≥2 boards" |
Stated precisely, because overclaiming here loses technical audiences: SQL cannot express a single query whose traversal depth is decided by the data. A warehouse can still reach the same answers with a recursive CTE. The advantage is one indexed declarative pattern next to the data — not that the answer is unobtainable elsewhere.
make prove runs both legs for real — Cypher inside Neo4j, and genuine WITH RECURSIVE CTEs over
identical rows — and publishes the agreement, with timings for both. At this scale SQL is
faster on the chain (~0.7 ms vs ~13 ms) and slower on the path (~20 ms vs ~1 ms). Anyone
evaluating this should read those numbers before the prose.
Quickstart
# 1. Install (project-local venv — required for the MCP launcher)
uv venv && source .venv/bin/activate
uv pip install -e ".[dev,llm]" # or: make install
cp .env.sample .env # fill in NEO4J_PASSWORD and SEC_USER_AGENT
# 2. Check preconditions in seconds, before committing to a multi-hour build
make preflight
# 3. Point at a graph. Either build it (hours — downloads from EDGAR):
make build # dry-run: prints the full phased plan + preflight, writes nothing
make build-exec # for real
# 4. Ask it things
make demo # the activist convergence screen
make prove # graph vs SQL, head to head
make smoke-mcp # curated tool catalog + demo queries against the live DB
make serve # curated MCP tools over stdio (or use Cursor — see below)What a build needs
Neo4j | Enterprise (or Docker |
| Required. SEC fair access rejects generic agents with HTTP 403. The built-in default is a placeholder on the reserved domain |
OpenAI key | Effectively required for a fresh build. |
Time & disk | Several hours, ~20 GB. Dominated by the ~8,000-issuer 13D/G crawl and the 13F load, both bounded by SEC's 10 req/s ceiling. |
make preflight checks every one of these. Each used to surface only after minutes-to-hours of
crawling, as a generic non-zero exit from a child script.
Curated MCP (Cursor / Claude Desktop)
Seven read-only tools — activist_convergence, campaign_timeline, activist_coalition,
ownership_snapshot, control_chain, board_interlock_path, get_secgraph_schema. Curated,
not raw text2cypher: no Cypher passthrough and no write path, so the scrubs and thresholds that
make the answers correct cannot be bypassed.
Cursor: open this repo; .cursor/mcp.json uses ${workspaceFolder} and
scripts/run_ownership_mcp.sh so a clone needs no path edits
after .venv exists. Enable secgraph-ownership, reload MCP, then ask the demo questions in
natural language. If a user-level ~/.cursor/mcp.json also defines secgraph-ownership, point
it at the same launcher (bare python scripts/serve_ownership_mcp.py fails outside the venv).
Claude Desktop: root .mcp.json is the same shape; Claude does not expand
${workspaceFolder} — set command once to the absolute path of
scripts/run_ownership_mcp.sh (the launcher still finds .venv relative to the repo).
make smoke-mcp proves the tool catalog and the demo queries against a live secgraph DB
without an MCP client.
How it is built
Layer 3 MCP server curated read-only tools ─► Claude Desktop / any agent
Layer 2 Query core traversals + evidence + abstain (transport-agnostic)
Layer 1 Reproducible DB phased build, density gate, freshness manifestmake build runs the phases in order, aborting on the first failure: create the database →
load the filer universe → stage and load Form 3/4/5 insiders → density GO/NO-GO gate
(fail-closed: the build stops if the insider layer is too sparse to support the wins) →
materialize the derived edges (SHARES_DIRECTOR, CONTROLS, SAME_ENTITY_AS, CO_TARGETS) →
load 13D/13G and 13F. A successful run writes results/secgraph_freshness.json, which every
served answer reports as its "as of" date.
schema/graph_schema.yaml is the single source of truth — 4 node labels, 10 relationship types.
tests/unit/test_schema_consistency.py scans every .py file and fails the build if any Cypher
references something undeclared.
Architecture detail: docs/reference_architecture_secgraph.md
What the forms mean and where each element came from: docs/data_sources_and_forms.md
Bloom demo runbook (six acts, ~18 min): docs/demo_runbook_bloom.md
Bloom visualization build brief: docs/bloom_perspective_spec.md
Field-level schema (generated): docs/graph_schema.md
Honest limits
Read these before demoing — they are part of what makes the rest credible.
No prediction. The alpha question was tested and came back null. Efficient markets; 13Ds are public. This is a structural and temporal map, not a signal.
Size is a threshold, not a market cap — and there are two measures.
size_usdis what filters and ranks: it preferstotal_assets_usd, a filed balance-sheet total from the SEC Financial Statement Data Sets (63% of the universe), and falls back toinstitutional_value_usd, one quarter of 13F holdings (75%). Combined coverage is 83%;size_sourcerecords which applied, and 17% have neither and are excluded from size-filtered results rather than ranked. Each has a distinct limit: 13F measures free float, so it understates concentrated-ownership issuers and counts ETFs; total assets are not comparable across sectors — a bank's assets are its balance sheet, so JPMorgan's $4.4T is not "bigger than" a $200B industrial in any meaningful sense. Still no revenue and no true market cap, so this does not support leverage or coverage ratios.Activist screens trade recall for precision. Gated to a curated franchise list; ungated detection is dominated by micro-cap founders crossing 5% and by filing-group artifacts (one manager filing through seven affiliated vehicles). First-time activists are missed by design.
Only 13D/13G dates are a time series. Board and officer edges are a keep-latest snapshot; 13F has a 2024 coverage step-up. Don't read trends into them.
13D/13G history is capped, so "1994→present" holds only for light filers. The crawl reads each issuer's
filings.recent(~1,000 most recent filings) and takes at most 40 Schedule 13D/G per subject. For a company that files hundreds of Form 4s a year,recentmay reach back only a year or two, and its older 13D/Gs are invisible. Small caps — where the control chains are — get the full history; mega caps do not.At this scale, a warehouse is a real alternative. The three wins run on ~1,100 derived edges, measured depth is overwhelmingly 1-2 hops with a maximum of 3, and a recursive CTE answers all three in single-digit milliseconds. The honest case for a graph here is authoring cost per new question, GDS algorithms with no SQL equivalent (Louvain, betweenness), and the curated serving layer — not tractability. Tractability would only become the argument at a far larger universe than 8,000 tickered issuers.
Rebuilds drift, by construction. The staging window resolves against the run date, and EDGAR keeps accruing filings, so a rebuild today will not reproduce the figures below exactly. New 13Ds can also remove a convergence hit, because the screen measures a total span rather than a rolling window. Compare against
results/secgraph_freshness.json(as_of) before concluding something broke.Chain depth is a small-cap signal; single-hop control is not. 39 of 825 controlled issuers are ≥$10B by
size_usdand 150 are ≥$1B — Deutsche Telekom holds 74.3% of T-Mobile US ($219.2B in assets), Ergen holds 51.8% of EchoStar ($43.0B). Those counts were 20 and 97 when size was 13F float alone: a controlled issuer has little float by definition, so the float proxy was hiding exactly this population — EchoStar has no 13F coverage at all. But the multi-hop pyramids top out near $1.5B, so read a deep chain as a small-cap governance screen and a single-hop one as general-purpose. Most large caps have no ≥50% holder and correctly abstain.Board-interlock path existence is uninformative. Measured: every well-connected pair links within 4 hops. The named bridging director is the signal, not the connection.
CIK-keyed only. Deliberately conservative — understates family/affiliate structure rather than inventing links through fuzzy name matching.
Custodians and index funds are labelled, not deleted. They're excluded at query time so the underlying co-filing facts stay in the graph and the precision choice stays auditable.
Development
make test # unit suite: fully mocked, no database needed
make check # lint + tests (note: `make lint` rewrites source via ruff --fix)
make preflight # verify build preconditions against your Neo4jThere is no CI. make check is the stand-in; pre-commit install gets ruff + gitleaks on
commit.
License
MIT. SEC filing data is public domain.
Available Tools
8 toolsactivist_coalitionARead-onlyIdempotent
Find the de-facto activist coalition (wolf-pack) around a named 13D filer.
Two activists are linked when they co-target >=min_shared_targets of the same issuers via 13D. Custodial/broker hubs are scrubbed before the connected component is formed (precision discipline), so the coalition reflects coordinated activists, not shared custodians. Returns members + coalition diameter. Abstains when the filer sits in no multi-member coalition (or was scrubbed as a custodial hub).
Example: activist_coalition("ICAHN CARL C") -> the ~22-member scrubbed cluster (Bulldog/Goldstein, GAMCO/Gabelli, Karpus, Icahn, Dolan family).
Inputs: activist (required), min_shared_targets. Returns: object
activist_convergenceARead-onlyIdempotent
Screen for issuers where several known activist franchises filed 13D close together.
The "what is heating up" screen, and the best starting point for an event-driven
question. since is an ISO date (e.g. '2025-01-01'). Returns each issuer with the
franchises involved, the span in days, and the filing sequence with accession numbers.
Gated to a curated list of recognised activist franchises (Icahn, GAMCO/Gabelli, Saba, Bulldog, Karpus, Elliott, Starboard, RA Capital, OrbiMed, ...) — precision over recall. Without that gate the output is dominated by micro-cap founders crossing 5% and by filing-group artifacts where one manager files through several affiliated entities.
Example: activist_convergence(since='2023-01-01') surfaces MNRO (GAMCO then Icahn, 96 days apart) and SION (OrbiMed then RA Capital, 5 days apart).
Inputs: since, window_days, min_activists. Returns: object
board_interlock_pathARead-onlyIdempotent
Name the director(s) who bridge two companies' boards.
Walks the persisted SHARES_DIRECTOR edge, returning the alternating company chain and the bridging director(s) per hop. Abstains when no shared-director path exists within max_hops.
Read the bridging director, not the path's existence. In this dataset every well-connected pair of companies is linked within 4 hops (measured), so "are these boards connected?" is essentially always yes and carries no information. The useful output is who the named connector is, and how short the link is.
Example: board_interlock_path("AAPL", "JPM") -> AAPL — JPM via BELL JAMES A (1 hop).
Inputs: to_company (required), from_company (required), max_hops. Returns: object
campaign_timelineARead-onlyIdempotent
Show who moved first on an issuer, who followed, and who is merely index money.
Returns every dated 13D/13G filing on the issuer in chronological order, each filer
classified as activist / insider_or_other / passive_index / custodian,
with percent_of_class and the accession number. Also reports the first mover and, for
each follower, how many days later they arrived. Abstains when the issuer has no dated
ownership filing.
Only 13D/13G filing dates are used — they are the one trustworthy time series in this graph (board and 13F layers are snapshots).
Example: campaign_timeline("MNRO") shows GAMCO at 4.0% on 2025-08-01, then ICAHN CARL C at 14.79% exactly 96 days later.
Inputs: company (required), since. Returns: object
control_chainARead-onlyIdempotent
Trace the transitive >=50% ownership control chain through a public issuer.
direction='up' = who ultimately controls this issuer; 'down' = what this
issuer controls. Each step carries percent_of_class and the 13D accession number as
citation. Abstains (abstained=True) when the issuer has no verified control edge —
never fabricates a chain from sub-50% or unclassified stakes.
Scope, precisely. Single-hop control reaches large caps: 20 of 825 controlled issuers carry >=$10B of institutional ownership and 97 carry >=$1B. It is the MULTI-HOP pyramids that are small-cap — those top out around $1.5B — so treat chain depth as a small-cap governance signal while single-hop control is general-purpose. Most large caps still have no >=50% holder and correctly abstain.
Each step carries institutional_value_usd (a 13F size proxy, null for the ~25% of issuers with no institutional coverage) so results can be ranked by materiality.
Examples: control_chain("TMUS") returns Deutsche Telekom -> T-Mobile US (74.3%, $95B). control_chain("Income Opportunity Realty") returns the 3-hop pyramid Basic Capital -> American Realty (62%) -> Transcontinental (83%) -> Income Opportunity (85%).
Inputs: company (required), max_hops, direction. Returns: object
get_secgraph_schemaARead-onlyIdempotent
Return the curated secgraph ownership schema and tool catalog for grounding.
A hand-curated summary (nodes, relationship semantics, temporal-trust caveats, and the curated tool catalog) — not a raw graph dump — so an agent can pick the right tool. Includes the honest limits (CIK-only, snapshot vs dated layers, no prediction).
Inputs: no named parameters. Returns: object
influence_mapARead-onlyIdempotent
Issuers where a holder has a big stake AND currently sits on the board.
The strongest single output here, and the best place to start a governance or counterparty question. It is the two-limb test from 12 CFR 225.2(e) — the Federal Reserve's control presumptions — and its force comes from the conjunction of two independent filing types: a Schedule 13D on one side, Form 3/4/5 board activity on the other. A screener can give you either list; the pairing is the finding.
Requiring a current board seat also fixes a freshness problem. 13D carries no exit obligation below 5%, so half the stakes on file predate 2020 and are last-known rather than current — but board activity runs to the present, so a recent seat corroborates an old declaration. Liberty Broadband's 26.1% of Charter was declared in 2014; its director was seen in 2026.
min_tier is a presumption tier (10/15/25/50). min_value_usd filters on the 13F
size proxy so results are recognizable names rather than nano-caps.
Caveat to state if asked: percent_of_class is percent of the class covered by the filing, NOT voting power — and several of the largest names are dual-class (Berkshire, the Liberty complex, Carvana, Sea), where economic and voting stakes diverge.
Example: influence_map() returns Buffett/Berkshire 37% (seat 2026-05), Liberty Broadband/Charter 26.1% (seat 2026-06), Huffman/Reddit 61.5% (seat 2026-06).
Inputs: limit, min_tier, min_value_usd. Returns: object
ownership_snapshotARead-onlyIdempotent
Supporting ownership context for one issuer: top holders, board size, control status.
Grounds the headline chain/path/coalition answers with the surrounding facts — the largest 13D/13G beneficial owners (with percent_of_class), director/officer counts, and whether any verified >=50% control edge exists. Abstains only if the company can't be resolved.
Inputs: company (required), top_n. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool targets a distinct analytical question: convergence screens issuers, coalition groups filers, influence_map pairs stake with board seat, timeline sequences filings, chain traces control, path finds board bridges, snapshot gives current context, and schema is meta. Even the two activist-related tools differ by perspective (issuer-centric vs. filer-centric), and descriptions reinforce those boundaries.
Nearly all names follow a consistent snake_case noun_phrase pattern (activist_convergence, influence_map, campaign_timeline, control_chain, board_interlock_path, activist_coalition, ownership_snapshot). The lone 'get_secgraph_schema' deviates with a verb prefix, but it is a minor exception and still recognizable within the same style.
Eight tools is well within the ideal 3-15 range and each tool covers a meaningful aspect of ownership analysis without redundancy. The count feels intentional for the scope: screening, historical timeline, control tracing, interlocks, coalitions, and snapshot context.
The tool set covers the core workflows for SEC ownership graph analysis: identifying activist activity (convergence, coalition), assessing influence (influence_map, snapshot), understanding control (control_chain), and analyzing board connections (interlock_path). A schema grounding tool fills the meta-need, and there are no obvious dead ends. One could imagine a search tool, but the descriptions imply names are directly resolvable, so gaps are minimal.
Maintenance
Related MCP Connectors
SEC & financial-data MCP: filings, financials, ownership, factors, fund letters, prompts.
Financial data and research MCP for US/CN/JP equities: filings, statements, ownership, signals.
SEC insider intelligence: trades, 8-K events, planned sales, activist stakes - one entity graph.
Research US-listed companies and funds with the StockPortfolio.pro MCP server. Tools cover SEC filing-grounded financials, filing timelines, company comparisons, watchlist screens, fund profiles, and cited filing questions. Filed figures include their fiscal period and EDGAR source URL; missing filing data is returned as null. Public tools work without authentication; optional API-key or OAuth access is supported. US-listed, USD-reporting companies only. Keyless access includes 3 free AI questions per rolling 30-day window. To get more now, choose the Dev plan ($19.99/month, 200 credits) at https://www.stockportfolio.pro/register?plan=dev, then follow https://www.stockportfolio.pro/docs/api-mcp to connect with OAuth or an API key. Or wait for the free allowance to renew.
Related MCP Servers
AlicenseAqualityDmaintenanceMCP server for the Graphite Financial Knowledge Graph, enabling natural language queries about companies, supply chains, executives, regulations, and patents via MCP-compatible clients.7MIT- AlicenseAqualityCmaintenanceMCP server providing read-only access to SEC EDGAR filings, allowing LLMs to look up companies, search filings, and retrieve securities offering data.380 PyPI2MIT
- AlicenseNot gradedqualityDmaintenanceHosted MCP server that gives AI agents real-time access to SEC EDGAR filings search, 10-K/8-K reading, XBRL financial facts, and insider-trade (Form 4) alerts.12 npm1MIT
- FlicenseAqualityDmaintenanceEnables AI clients to retrieve SEC company profiles, filing listings, and structured XBRL financial statements via MCP tools.13-