OpenInsider-MCP
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 67 GitHub stars
Code Fail
- exec() — Shell command execution in scripts/capture-yahoo-fixtures.mjs
- network request — Outbound network request in scripts/capture-yahoo-fixtures.mjs
Permissions Pass
- Permissions — No dangerous permissions requested
This MCP server acts as a pure data layer, exposing 16 free public investment research signals—such as SEC filings, insider trades, and live Yahoo Finance quotes—to any compatible LLM client.
Security Assessment
Overall risk: Low. The tool accesses strictly public financial data and does not read, store, or persist any sensitive personal information. It correctly sets a `User-Agent` header and relies on temporary in-memory caching rather than local file storage to prevent redundant API calls. The automated code scan flagged a shell command execution and an outbound network request, but both are safely contained strictly within a developer test script (`scripts/capture-yahoo-fixtures.mjs`). The live server package itself does not execute arbitrary shell commands. There are no hardcoded secrets, and it requests zero dangerous system permissions.
Quality Assessment
Overall quality: High. The project is actively maintained, with repository activity as recent as today. It uses the highly permissive and standard MIT license, which is ideal for open-source adoption. The developer communication is transparent, clearly outlining what the tool does and the boundaries of what it will not do. With 67 GitHub stars, it shows early but solid community trust and validation. Furthermore, it includes a continuous integration (CI) pipeline and seamless installation links for popular editors.
Verdict
Safe to use.
MCP server that exposes live insider trading data to any LLM.
OpenInsider MCP
An MCP server that exposes 16 free-data investment-research signals to any MCP-compatible LLM client — Form 4 insider trades, SEC corporate-event filings, FINRA / SEC short data, and live Yahoo Finance quotes. Drop it into your MCP client (Cursor, Claude Desktop, VS Code, Claude Code, Codex, etc.) and your LLM can query these signals directly during long research sessions, without burning context on web browsing.
The server exposes 16 tools across four free public data sources: OpenInsider (Form 4 insider trades), SEC EDGAR (8-K material events, late-filing notices, 13D activist filings, S-3 / 424B5 dilution), FINRA / SEC (short interest, daily short volume, failures-to-deliver), and Yahoo Finance (live quote: price, valuation, dividend, earnings calendar).
The server is positioned as a pure data layer: no scoring, no compositing, no editorialization. Each tool returns clean, well-typed observations with citations and gotchas baked into the tool descriptions. The orchestrator LLM decides what is significant.
What it won't do: recommend buys / sells, combine signals into a score, run scheduled jobs, or persist anything between sessions. The MCP gives your LLM raw observations; you and the LLM reason from there.
Be polite. This scrapes a free public site and uses public SEC / FINRA / Yahoo endpoints. The server identifies itself with a
User-Agent(override viaOPENINSIDER_MCP_UAenv var) and caches every response in memory for 5 minutes (60 seconds for Yahoo Finance quotes, which are time-sensitive). Repeated queries in a research session don't re-fetch.
Install
OpenInsider MCP is a stdio server distributed on npm — pick your client below.
Claude Code (CLI)claude mcp add openinsider -- npx -y openinsider-mcp
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"openinsider": {
"command": "npx",
"args": ["-y", "openinsider-mcp"]
}
}
}
Restart Claude Desktop after saving.
Codex CLI (OpenAI)codex mcp add openinsider -- npx -y openinsider-mcp
Or edit ~/.codex/config.toml directly:
[mcp_servers.openinsider]
command = "npx"
args = ["-y", "openinsider-mcp"]
Other MCP clients (Windsurf, Cline, Continue, Zed, …)
Most clients share the same generic stdio config — paste this into your client's MCP config (path varies by client; check their docs):
{
"mcpServers": {
"openinsider": {
"command": "npx",
"args": ["-y", "openinsider-mcp"]
}
}
}
That's it — npx fetches and runs the server on demand.
Quick start. Once installed, ask your LLM something like "What's recent insider activity at NVDA?" — it calls search_by_ticker and returns the last 90 days of Form 4 filings. Follow up with "Why did Mark Stevens sell on March 20?" and your LLM pulls the relevant 8-K via recent_sec_filings. The in-memory cache makes follow-ups instant.
How to talk to it
You don't call these tools directly — your LLM does, based on what you ask. Representative prompts:
Single-source:
- "What's recent insider activity at NVDA?" →
search_by_ticker - "Are there any notable cluster buys this week?" →
cluster_buys - "What happened with NVDA recently?" →
recent_sec_filings(8-K item codes) - "Is BIOX raising money?" →
dilution_filings(S-3 / 424B5 takedowns) - "How heavily is GME shorted? Is short interest rising?" →
short_interest(with delta andpctOfFloat) - "Is shorting picking up on TICKER lately?" →
daily_short_volume(daily flow, not standing position) - "What's AAPL trading at right now?" →
get_quote(price + valuation + dividend in one call)
Multi-tool (the LLM composes these automatically):
- "Is TICKER a short squeeze setup?" →
short_interest+failures_to_deliver+search_by_ticker - "Why did TICKER drop today?" →
recent_sec_filings+dilution_filings+search_by_ticker - "How does TICKER's valuation compare to its insider activity?" →
get_quote(P/E, market cap) +search_by_ticker(recent insider buys/sells)
The more specific the question, the better the tool selection. See the Quick reference table below for the full mapping.
Quick reference: which tool answers what?
| Question | Tool | Output type |
|---|---|---|
| Insider trades for one company | search_by_ticker |
Trade[] |
| One insider's trades across companies | search_by_insider |
Trade[] |
| Market-wide insider firehose | latest_trades |
Trade[] |
| Biggest insider buys/sells per period | top_buys, top_sells |
Trade[] |
| Multiple insiders buying same name | cluster_buys |
Trade[] (with industry, insiderCount) |
| Officer buys ≥$25k | officer_buys |
Trade[] |
| Custom multi-filter screener | screen |
Trade[] |
| Recent 8-K material events | recent_sec_filings |
EdgarFiling[] |
| NT-10K / NT-10Q late-filing notices | late_filings |
EdgarFiling[] |
| Schedule 13D activist filings | activist_filings |
EdgarFiling[] |
| S-3 / 424B5 dilution / shelf takedowns | dilution_filings |
EdgarFiling[] |
| Bi-monthly short interest snapshot + delta + % of shares | short_interest |
ShortSnapshot[] |
| Daily short-sale flow (different from above!) | daily_short_volume |
{date, shortVolume, totalVolume, shortRatio}[] |
| SEC failures-to-deliver + Reg SHO threshold list | failures_to_deliver |
{date, ftdShares, ftdValue, onThresholdList}[] |
| Live stock quote (price, valuation, dividend, earnings) | get_quote |
Quote |
Tool reference
Each tool returns JSON with a typed payload. OpenInsider tools return { count, trades: Trade[] }; SEC EDGAR tools return { count, filings: EdgarFiling[] }; FINRA / SEC short-data tools return { count, snapshots: ShortSnapshot[] } or { count, rows: ... } depending on cadence; Yahoo Finance returns a single Quote object (no array, no count). See output types below for the exact field shapes.
OpenInsider — Form 4 insider trades
search_by_ticker
All insider trades for one company.
| Param | Type | Required | Notes |
|---|---|---|---|
ticker |
string | yes | Stock symbol, e.g. "NVDA". Case-insensitive. |
daysBack |
int | no | Only return trades filed within the last N days. |
Example call:
{ "ticker": "NVDA", "daysBack": 90 }
search_by_insider
All trades by a specific insider across all companies, identified by their SEC CIK.
| Param | Type | Required | Notes |
|---|---|---|---|
cik |
string | yes | Numeric SEC CIK of the insider. See Finding a CIK below. |
daysBack |
int | no | Filter by filing date. |
Example call:
{ "cik": "1214156", "daysBack": 365 }
latest_trades
Most recent insider filings across the whole market — the live firehose.
| Param | Type | Notes |
|---|---|---|
daysBack |
int (optional) | |
transactionType |
"all" | "buys" | "sells" (optional) |
"buys" keeps only P- rows, "sells" keeps S- rows. |
Use when: you want a market-wide pulse. Pair with daysBack: 1 to see what was filed today.
top_buys
Largest insider purchases by dollar value over a fixed window.
| Param | Type | Notes |
|---|---|---|
period |
"day" | "week" | "month" | "quarter" | "year" |
Required. |
Example call:
{ "period": "week" }
top_sells
Largest insider sales by dollar value over a fixed window. Same period argument as top_buys.
Note: most large sales are routine 10b5-1 plans or option exercises. The signal in
top_sellsis usually pattern (multiple insiders, late filings) more than absolute size.cluster_buyshas historically been the higher-signal page.
cluster_buys
Companies where multiple insiders bought stock in a short window. The strongest open-market signal on OpenInsider.
| Param | Type | Notes |
|---|---|---|
daysBack |
int (optional) |
Trades returned by this tool include two extra fields: industry (SIC industry name) and insiderCount (how many insiders are clustered).
officer_buys
Recent purchases of ≥$25k by company officers (CEO, CFO, COO, etc.). Filters out 10%-owner buys, which are often institutional and lower-signal.
| Param | Type | Notes |
|---|---|---|
daysBack |
int (optional) |
screen
Generic OpenInsider screener with flexible filters. Use this when none of the named tools fit.
| Param | Type | Notes |
|---|---|---|
ticker |
string | Filter to one symbol. |
insiderCik |
string | Filter to one insider by CIK. |
daysBack |
int | |
transactionTypes |
Array<"P"|"S"|"A"|"D"|"M"> |
Applied client-side after fetch. See transaction codes. |
minTradeValue / maxTradeValue |
number (USD) | Trade value range. |
minPrice / maxPrice |
number (USD) | Per-share price range. |
isCeo, isCfo, isDirector, isOfficer, isTenPercentOwner |
boolean | Role filters. |
excludeDerivativeRelated |
boolean | Hide option-exercise / award-related rows. |
limit |
int | Max rows (capped at 1000). |
Example call — CEO open-market buys over $500k in the last 30 days:
{
"isCeo": true,
"transactionTypes": ["P"],
"minTradeValue": 500000,
"daysBack": 30
}
SEC EDGAR — corporate event filings
These four tools are ticker-scoped and source from the SEC EDGAR submissions API plus filing-body parsing. They return EdgarFiling[].
recent_sec_filings
Recent 8-K material event filings for a ticker, with parsed item codes (e.g. 1.02 contract terminated, 4.02 restatement, 5.02 officer change, 2.06 impairment).
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
daysBack |
int (optional) | default 30 |
itemCodes |
string[] (optional) | filter to specific 8-K items, e.g. ["4.02", "5.02"] |
late_filings
NT-10K / NT-10Q late filing notices, with parsed reason text and a heuristic category (accounting / corporate / multiple / unspecified). Accounting reasons are the strongest bearish variant.
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
daysBack |
int (optional) | default 365 |
activist_filings
Schedule 13D activist filings (initial + amendments). Returns filer name, ownership pct, and Item 4 'Purpose of Transaction' excerpt when parseable.
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
daysBack |
int (optional) | default 365 |
includeAmendments |
bool (optional) | default true |
dilution_filings
S-3 shelf registrations and 424B5 takedowns, with parsed shelf amount and use-of-proceeds excerpt.
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
daysBack |
int (optional) | default 365 |
FINRA / SEC — short interest, volume, FTD
These three tools are ticker-scoped and source from FINRA's CDN plus SEC bi-monthly FTD files.
short_interest
FINRA bi-monthly short interest snapshots with delta vs prior period. Returns ShortSnapshot[].
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
periodsBack |
int (optional) | default 6 (~3 months of bi-monthly periods) |
Bi-monthly cadence: settlement on 15th + last business day, published ~7 business days later — most recent snapshot may lag spot price by 1-2 weeks.
daily_short_volume
FINRA Reg SHO daily short-sale volume — daily flow (not standing position; for that, use short_interest). Aggregated across CNMS, FNRA, FNYX, FNQC venues. Returns {date, shortVolume, totalVolume, shortRatio}[].
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
daysBack |
int (optional) | default 30 |
failures_to_deliver
SEC failures-to-deliver per bi-monthly period plus current Reg SHO threshold-list flag (>10K shares + >0.5% of TSO failed for 5 consecutive settlement days). Returns {date, ftdShares, ftdValue, onThresholdList}[].
| Param | Type | Notes |
|---|---|---|
ticker |
string | yes |
periodsBack |
int (optional) | default 4 (~2 months) |
ETF FTDs are largely market-maker operational; post-T+1 settlement (May 2024) aggregate FTD volumes have decreased. Interpret with caution.
Yahoo Finance — live stock quote
Single tool, single round-trip per ticker. Sourced by scraping the structured JSON Yahoo embeds in its public HTML quote page (the same page your browser loads at finance.yahoo.com/quote/<TICKER>/). No api keys, no auth, no third-party brokers — direct fetch from your machine to Yahoo's web frontend.
get_quote
Live stock quote for a ticker — price, previous close, 52-week range, today's volume + 3-month average volume, market cap, beta, trailing/forward P/E, dividend yield, ex-dividend date, next earnings date, currency, exchange, ISO timestamp.
| Param | Type | Required | Notes |
|---|---|---|---|
ticker |
string | yes | Stock ticker, e.g. "AAPL", "BRK.B" (normalized to "BRK-B"). Case-insensitive. Must match /^[A-Z0-9.\-]{1,10}$/ after normalization; tickers with other characters are rejected. |
Example call:
{ "ticker": "AAPL" }
The tool throws a clean ticker not found error for tickers that don't resolve, and a generic Yahoo Finance: error response for <ticker> when Yahoo returns an error (Yahoo's verbatim error text is intentionally not passed through, as a prompt-injection defense in an LLM tool surface).
Trade object
OpenInsider tools return { count, trades: Trade[] }. Key fields: filingDate / tradeDate (ISO), ticker, insiderName, insiderCik, title, transactionType (SEC code + label like "P - Purchase"), price, quantity (signed), value (signed), formUrl. cluster_buys adds industry + insiderCount.
{
filingDate: string; // ISO 8601, with time, e.g. "2026-04-24T21:35:59"
tradeDate: string; // ISO 8601 date, e.g. "2026-04-22"
ticker: string;
companyName: string;
insiderName: string;
insiderCik: string | null; // SEC CIK of the insider, when present
title: string; // role(s), e.g. "CEO", "Dir", "10%", "CEO, Pres"
transactionType: string; // SEC code + label, e.g. "P - Purchase", "S - Sale+OE"
price: number | null; // $/share, null on award/grant rows
quantity: number; // signed: positive = acquired, negative = disposed
sharesOwnedAfter: number | null;
ownershipDelta: number | null; // % change in stake, signed (e.g. -10 means -10%)
value: number; // signed dollar value (matches quantity sign)
formUrl: string | null; // direct link to the SEC Form 4 XML
industry?: string; // cluster_buys only
insiderCount?: number; // cluster_buys only — how many insiders clustered
}
Transaction codes
The transactionType string is the SEC's two-character code plus its label. Common ones:
| Code | Meaning | Signal |
|---|---|---|
P |
Open-market purchase | Strongest bullish signal — insider used real cash. |
S |
Open-market sale | Sell signal, but often diluted by 10b5-1 plans. |
S - Sale+OE |
Sale tied to an option exercise | Mostly mechanical; weaker signal than a clean S. |
A |
Grant / award | Compensation, not a trade. Ignore for sentiment. |
M |
Option exercise (no open-market component) | Mechanical. |
F |
Tax withholding | Mechanical. |
D |
Disposition (non-open-market) | Usually a transfer, not a trade. |
When filtering for "real" buying or selling activity, prefer P and unqualified S.
Finding a CIK
search_by_insider needs a numeric SEC CIK (Central Index Key). Two easy ways:
- Search SEC EDGAR: https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&type=4. Type the person's name, copy the 10-digit CIK from the result.
- Click through OpenInsider: any name in
latest_tradesorcluster_buysresults includesinsiderCikdirectly. Pull a few trades, grab the CIK, then callsearch_by_insider.
In practice, when you ask your LLM something like "Has Tim Cook sold any AAPL recently?", it will look up the CIK on the web and then call search_by_insider itself — you don't need to do this manually.
EdgarFiling object
The 4 EDGAR-sourced tools return { count, filings: EdgarFiling[] }. Common fields: ticker, cik (zero-padded), formType, filingDate, accessionNumber, primaryDocUrl. Each tool also populates form-specific optional fields (itemCodes for 8-K, reasonCategory for NT, filerName / pctOwned / purposeExcerpt for 13D, shelfAmount / useOfProceedsExcerpt for S-3).
{
ticker: string;
cik: string; // 10-digit zero-padded
formType: string; // "8-K", "13D", "NT-10Q", "S-3", etc.
filingDate: string; // ISO YYYY-MM-DD
acceptanceDateTime: string;
accessionNumber: string; // canonical "0000XXXXXX-YY-NNNNNN"
primaryDocUrl: string; // direct link to the filing body
// Form-specific (optional, populated by the relevant tool):
itemCodes?: string[]; // 8-K item codes
reasonText?: string | null; // NT-10K/Q narrative excerpt
reasonCategory?: "accounting" | "corporate" | "multiple" | "unspecified";
filerName?: string; // 13D
pctOwned?: number | null; // 13D, % of class
purposeExcerpt?: string | null; // 13D Item 4
isAmendment?: boolean; // 13D, S-3
shelfAmount?: number | null; // S-3, dollars
useOfProceedsExcerpt?: string | null; // S-3
}
Sample output — recent_sec_filings ticker=NVDA daysBack=180:
[
{
"ticker": "NVDA",
"cik": "0001045810",
"formType": "8-K",
"filingDate": "2026-03-06",
"acceptanceDateTime": "2026-03-06T16:11:25.000Z",
"accessionNumber": "0001045810-26-000024",
"primaryDocUrl": "https://www.sec.gov/Archives/edgar/data/1045810/000104581026000024/nvda-20260302.htm",
"itemCodes": ["5.02", "9.01"]
}
]
ShortSnapshot object
short_interest returns { count, snapshots: ShortSnapshot[] } — fields: ticker, reportDate, sharesShort, pctOfFloat (via SEC XBRL), daysToCover, optional delta block. daily_short_volume returns { count, rows: { date, shortVolume, totalVolume, shortRatio }[] }. failures_to_deliver returns { count, rows: { date, ftdShares, ftdValue, onThresholdList }[] }.
{
ticker: string;
reportDate: string; // ISO YYYY-MM-DD
sharesShort: number;
pctOfFloat: number | null; // sharesShort / sharesOutstanding via SEC XBRL (dei:EntityCommonStockSharesOutstanding); null when the company has no XBRL filings
daysToCover: number | null;
delta?: { // present for entries that have a prior period
sharesShortDelta: number;
pctDelta: number; // decimal, e.g. 0.12 = +12%
};
}
Sample output — short_interest ticker=NVDA periodsBack=2:
[
{
"ticker": "NVDA",
"reportDate": "2026-03-31",
"sharesShort": 280872588,
"pctOfFloat": 0.01156,
"daysToCover": 1.51,
"delta": {
"sharesShortDelta": 32531716,
"pctDelta": 0.131
}
},
{
"ticker": "NVDA",
"reportDate": "2026-02-27",
"sharesShort": 248340872,
"pctOfFloat": 0.01022,
"daysToCover": 1.31
}
]
Quote object
get_quote returns a single Quote object directly (no array, no count wrapper). Always populated: identity (ticker, currency, timestamp, dataAsOf, marketState), price, previousClose, 52-week range, volume. timestamp is the time of the last actual tick (Yahoo's regularMarketTime); dataAsOf is when the MCP assembled the response — comparing the two reveals tick lag for illiquid securities or sessions outside regular hours. marketState is one of regular | pre | post | prepre | postpost | closed. Nullable in cases where the metric doesn't apply: exchange (null if Yahoo returned an unexpected exchange code), averageVolume (null for illiquid issues without a 3-month average), marketCap / beta / trailingPE / forwardPE (null for ETFs and instruments where N/A), dividendYield / exDividendDate (null for non-dividend payers), earningsDate (null when no upcoming consensus).
{
ticker: string;
exchange: string | null;
currency: string; // ISO-4217, e.g. "USD" — local for foreign tickers
timestamp: string; // ISO 8601 of regularMarketTime — last actual tick
dataAsOf: string; // ISO 8601 of when the MCP assembled this object
marketState: string; // regular | pre | post | prepre | postpost | closed
price: number;
previousClose: number;
fiftyTwoWeekHigh: number;
fiftyTwoWeekLow: number;
volume: number; // today's regular-session volume (shares)
averageVolume: number | null; // 3-month average; null for illiquid issues
marketCap: number | null;
beta: number | null; // 5y monthly
trailingPE: number | null;
forwardPE: number | null;
dividendYield: number | null; // decimal (0.0042 = 0.42%); null for non-payers
exDividendDate: string | null; // ISO YYYY-MM-DD; null for non-payers
earningsDate: string | null; // ISO YYYY-MM-DD; null when no upcoming consensus
}
Tips & gotchas
daysBackfilters by filing date, not trade date. Insiders have up to 2 business days to file (sometimes longer when they file late). For "what trades happened in the last week," usedaysBack: 14to be safe.- Filing-vs-trade-date gap is itself a signal. A large gap (especially weeks or months) often means a late filing — historically correlated with insider behavior worth a closer look. Both dates are in every trade row.
- Pure
P-Purchaserows are the highest signal.S-Sale+OE("sale on option exercise") is mostly mechanical and dominates the sales firehose. - The
valuefield is signed. Negative for sales, positive for buys. When summing portfolios, this gives you net flow for free. - Cluster buys page returns
industryandinsiderCount. These don't appear in the standardTradeshape — they're optional fields specific to that tool. top_sellsis mostly noise. For real sell-side signal, usescreenwithtransactionTypes: ["S"]plus role filters (e.g.,isCeo: true) and a dollar threshold.- Cache is per-process and per-URL. Repeat queries in the same MCP client session are instant. Restart the server (close your MCP client) to bust it, or wait for the 5-minute TTL (60 seconds for Yahoo Finance quotes).
- Tool returns empty? Widen
daysBack, confirm the ticker exists in SEC EDGAR (some OTC names don't), or check timing — FINRA bi-monthly files lag ~7 business days after settlement, so the most recent period may not be published yet. short_interestis bi-monthly with a publication lag. FINRA settles on the 15th + last business day of each month and publishes ~7 business days later. The most recent snapshot may lag spot price by 1–2 weeks.daily_short_volume≠short_interest. The first is daily flow (shares sold short that day, summed across CNMS/FNRA/FNYX/FNQC venues); the second is a standing position snapshot. Numbers are not directly comparable — they measure different things.pctOfFloatissharesShort / sharesOutstandingfrom SEC XBRL. Commonly conflated with "public float" in retail data feeds; true public float requires restricted-share data not available via free SEC data. Returnsnullwhen the company doesn't file XBRL or uses a non-standard tag.- 8-K item codes are populated from the SEC submissions API metadata (no body fetch needed). The most useful items:
1.02contract terminated,2.02earnings,2.06impairment,4.01auditor changed,4.02non-reliance / restatement,5.02officer/director departure or appointment. - NT-10K/Q reason classification is a keyword heuristic. "Accounting" reasons (restatement, audit, internal control, material weakness) are the strongest bearish variant per Bartov-DeFond-Konchitchki (2017). "Corporate" reasons (CFO transition, ERP migration) are common and weaker.
- Activist 13D filings include amendments by default. Pass
includeAmendments: falseto get only the initial filing — that's the highest-impact event-day per the Brav-Jiang-Partnoy-Thomas literature. - S-3 vs 424B5. S-3 = the shelf authorization itself (lower immediate impact); 424B5 = the actual sale off that shelf (higher impact, often −3% announcement reaction per Loughran-Ritter). Small-cap biotech S-3 takedowns are frequently pre-PDUFA capital raises — interpret in context.
- ETF FTDs are largely operational. Authorized-participant create/redeem flows generate failures that aren't directional. Post-T+1 settlement (May 2024), aggregate FTD volumes have decreased materially.
- Reg SHO threshold-list inclusion = >10K shares AND >0.5% of TSO failed for 5 consecutive settlement days. Stratmann-Welborn (2016) document negative drift on inclusion.
- EDGAR tools cap at
limit: 50filings by default.recent_sec_filings,late_filings,activist_filings, anddilution_filingseach accept alimitarg. Body-fetching tools (late_filings/activist_filings/dilution_filings) fetch one filing body per result — banks and other very-active 424B-prospectus filers can have hundreds of dilution filings per year and would exceed the MCP client's request timeout if all bodies were fetched. If you setlimithigher than what's available, the tool just returns what's there (no padding, no error). get_quotereturnsdividendYieldas a decimal, not a percent.0.0042means 0.42%, matching Yahoo's wire format. Multiply by 100 if you want to display it as "0.42%".- ETFs typically have null
trailingPE/forwardPE— those metrics don't apply to fund structures. Many ETFs still exposedividendYield. get_quotetimestampis the last actual tick — not "now". For illiquid securities (low daily volume, after-hours, weekends, halted) it can be hours or days behind wall clock. To detect this, comparetimestamptodataAsOf(when the MCP served the object) and checkmarketState(regularmeans the session is open, so a large gap there is the "this just hasn't traded recently" case;closed/pre/postexplain expected gaps).get_quoteis hardened against prompt injection through the response. Every string field surfaced to the LLM is validated:tickercomes from the validated input (never from Yahoo's response),exchangemust match/^[A-Z0-9_-]{1,12}$/or returnsnull,currencymust be a 3-letter ISO-4217 code or the call throws,marketStatemust match a known enum or the call throws, dates are constrained toYYYY-MM-DD, and numerics must be finite. Yahoo's own error text is never passed through verbatim. Numeric fields can't carry text instructions; the only strings reachable from a Yahoo response are tightly-bounded short codes.
Develop
git clone https://github.com/btopn/OpenInsider-MCP
cd openinsider-mcp
npm install
npm run build
npm test # unit tests against checked-in fixtures (offline)
SMOKE=1 npm test # live tests against OpenInsider, SEC EDGAR, FINRA
node dist/index.js # run the MCP server on stdio
To exercise the server interactively, use the official MCP inspector:
npx @modelcontextprotocol/inspector node dist/index.js
For a one-shot dump of representative output from every tool against a known ticker (useful for verifying a deployment or eyeballing what each tool returns):
npm run build
node scripts/exercise-tools.mjs NVDA # or any ticker
For targeted queries against a specific tool (full output, no truncation; pipe through jq for date or field filtering):
node scripts/run-tool.mjs <tool-name> [key=value ...]
# Examples:
node scripts/run-tool.mjs search_by_ticker ticker=NVDA daysBack=7 \
| jq '.[] | select(.tradeDate == "2026-04-22")'
node scripts/run-tool.mjs daily_short_volume ticker=GME daysBack=10
node scripts/run-tool.mjs recent_sec_filings ticker=AAPL daysBack=730 itemCodes=5.02 \
| jq '.[] | {filingDate, primaryDocUrl}'
node scripts/run-tool.mjs short_interest ticker=NVDA periodsBack=6 > nvda_si.json
Status messages go to stderr; tool output goes to stdout.
To identify your deployment to SEC EDGAR with your own contact info (polite practice), set the OPENINSIDER_MCP_UA env var:
OPENINSIDER_MCP_UA="my-app 1.0 [email protected]" node dist/index.js
Notes & non-goals
- Cache is in-memory and per-process. The server has no database and no scheduled polling — each tool call fetches the relevant page or file on demand and caches by URL for 5 minutes (60 seconds for Yahoo Finance quotes).
- This is not a Form 4 ingestion pipeline. The OpenInsider tools scrape the rendered tables from openinsider.com rather than parsing SEC Form 4 XML directly. The SEC EDGAR tools use the public submissions API + filing-body parsing for the form types they cover (8-K, NT-10K/Q, 13D, S-3 / 424B5).
- HTML, file formats, and CDN paths can change. If a tool starts failing, run
SMOKE=1 npm testto confirm whether the parser, URL pattern, or upstream availability is the problem, then open an issue or PR.
License
MIT
Star history
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found