
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
squawkflow-mcp
Advanced tools
Read-only MCP server for SquawkFlow's free market structure data: dealer gamma levels, the gamma grid, the sector gamma matrix, overnight open interest change, max pain, the VIX futures curve, the published exchange calendar, dated session records, 13F fi
Read-only MCP server over SquawkFlow's free
public market-structure data: dealer gamma levels, the strike by expiration
gamma grid, the sector gamma matrix, overnight open interest change, max pain,
the CBOE VIX futures settlement curve, dated session records, 13F filing
receipts, congressional disclosures, CFTC futures positioning, and the Lab
paper-trading record. Plus search and fetch over every page SquawkFlow
publishes for machine reading.
No account, no API key, no configuration. Nothing here places an order,
SquawkFlow has no order execution, and every tool is a plain GET against
https://api.squawkflow.com/api/public.
The server also runs hosted, speaking MCP streamable HTTP:
https://mcp.squawkflow.com/mcp
Clients that accept a remote MCP URL (Claude Code's claude mcp add --transport http squawkflow https://mcp.squawkflow.com/mcp, ChatGPT connectors, and any streamable-HTTP client) need only that URL. Same read-only tools, same data, no key.
That endpoint is loopback on the same machine as the SquawkFlow backend and the Cloudflare tunnel, not a CDN edge, which is why the protection described under Protection and degraded answers exists.
Published on npm as squawkflow-mcp. One command, no checkout:
npx -y squawkflow-mcp
Or from a checkout for development: cd mcp/squawkflow-mcp && npm install && npm run build.
claude mcp add squawkflow -- node /absolute/path/to/mcp/squawkflow-mcp/dist/index.js
Add this to claude_desktop_config.json:
{
"mcpServers": {
"squawkflow": {
"command": "node",
"args": ["/absolute/path/to/mcp/squawkflow-mcp/dist/index.js"]
}
}
}
Any client that speaks MCP over stdio takes the same two fields: the command
node and one argument, the absolute path to dist/index.js. Clients that
accept a single command string want
node /absolute/path/to/mcp/squawkflow-mcp/dist/index.js.
Once the package is on npm the command becomes npx with args
["-y", "squawkflow-mcp"] everywhere above, with no other change.
Fourteen data tools plus the catalog. Every one declares
readOnlyHint, destructiveHint: false, idempotentHint and openWorldHint,
and an outputSchema for the structured content it returns.
| Tool | Arguments | Returns |
|---|---|---|
list_squawkflow_tools | none | The catalog: every tool, what it covers, how old its data is, where it stops, and the things this server deliberately does not publish. Makes no market data call |
get_gex_levels | symbol: SPX, SPY or QQQ (anything else returns the SPX answer plus a coverage note) | Spot, call wall, put wall, zero gamma flip, vol trigger, net GEX, regime, pin strikes, 0DTE magnet, the options-implied session range, and the capture time |
get_gamma_heatmap | symbol: SPX, SPY or QQQ (anything else returns the SPX grid plus a coverage note) | Dealer gamma by strike AND expiration in dollars per 1% move, net charm and vanna, same-day volume concentration, and the overnight change in open interest between the last two settlements |
get_gamma_matrix | none | One cached grid: the eleven SPDR sector ETFs plus the SPX, SPY and QQQ index row, each tile with net GEX, the zero gamma flip, both walls and the grid's own age. Never rebuilt on request |
get_oi_change | optional symbol, optional date (ISO settlement to read into), limit: 1 to 50, scan: true for the universe | Per-strike open interest change between two named daily Cboe settlements, with both settlement dates, the net call and put change, and a flat strike printed as zero. date reads an earlier archived settlement against the one before it; a date the archive does not hold is an absence carrying the range it does |
get_max_pain | symbol: SPX (anything else returns the SPX answer plus a coverage note), optional expiration | The settlement strike minimising aggregate option payout for one expiration, the payout curve around it, and the available expirations |
get_vix_term_structure | none | Every listed VIX futures month with its settlement price and expiration, the contango or backwardation regime, the M9 minus M1 spread, and the steepness percentile |
get_market_calendar | optional date (ISO) | Whether that date is an exchange session, the next session after it, the next monthly, quarterly and VIX futures expiration, the SPX settlement rules in the exchange wording, and the upcoming NYSE full-day closures. Every entry carries the exchange document it was read from, and no date is computed from a rule |
get_session_record | optional date (ISO) | One dated session: the levels published before it traded, the verdict on each, the capture coverage, and how the published levels moved from the prior session. No session price extremes |
get_filing_receipt | optional manager, optional quarter (YYYY-qN), limit: 1 to 50 | A 13F receipt: legal name, CIK, period of report, filing acceptance date and accession number before any position, then the reported positions and the revision history |
get_congressional_disclosures | optional symbol, chamber, filer, since (ISO, on the public disclosure date), limit: 1 to 100 | House and Senate disclosures with three separate dates per record, the lag or the reason it is null, the amount band the filing uses, and the parse coverage for the archive. since is applied by the endpoint before the row cap, so a window is the whole window |
get_positioning | optional contract slug, weeks: 1 to 52 | Weekly CFTC Traders in Financial Futures positioning by trader classification, with the Tuesday it was measured and the Friday it was published |
get_lab_record | limit: 1 to 50, optional engine | Absolute session dates, known engine identifiers, grading status and counts of returned simulated-record receipts; no upstream commentary or performance summaries |
search | query, limit: 1 to 20 | Ids, titles and canonical URLs for the pages SquawkFlow publishes for machine reading. A query naming an ISO date finds the dated record for it |
fetch | id | One published page verbatim from its markdown mirror, with a non-empty canonical URL and the vintage the page states for itself |
search and fetch read the site's published files rather than the API, so a
wide reading pass costs the market data endpoints nothing. They return a JSON
object in both structuredContent and content[0].text, with a non-empty
url on every result, because that is what makes a ChatGPT citation point back
at the page.
| URI | What it is |
|---|---|
sf://catalog | The catalog body, readable without spending a tool call |
sf://manifest | The dataset manifest: every dataset, its upstream source, whether it is measured or derived, and how often it refreshes |
sf://corrections | The dated ledger of claims SquawkFlow published and later withdrew or corrected |
sf://methodology/{page} | One methodology or explainer page, for example sf://methodology/how-to-calculate-gamma-exposure |
sf://session/{date} | One dated session record page, for example sf://session/2026-09-17 |
sf://page/{+path} | Any published page, for example sf://page/gex or sf://page/glossary/zero-gamma |
ui://squawkflow/panel.html | The inline MCP Apps panel |
The three templates complete their parameter from what is actually published.
Every route that returns a document body, fetch and all six resources alike,
passes through the same content policy as the dedicated tools. See
The content boundary.
Eight registered prompts, which appear as slash commands in Claude Code, VS Code, Cursor, Goose and Zed. Each one names the tools to call and the order to call them in, states the limits in the words the tools use, and ends at one dated squawkflow.com link.
morning-brief, gamma-context, whats-new-overnight, filing-check,
disclosure-check, session-record, expiration-week, and
what-squawkflow-wont-say.
Every prompt argument is a single token. Claude Code splits prompt arguments on whitespace, so an ISO date or a slug works and free text does not.
Results come back as markdown rather than JSON. SquawkFlow publishes markdown mirrors of these same pages for the same reason: a model re-emitting a labelled prose block keeps the label attached to the number, while a model re-emitting a JSON blob tends to keep the number and drop the timestamp, and the timestamp is what stops a stale level being quoted as a current one.
Every tool result carries these terms, and every tool description repeats them before the call is made.
squawkflow.com page it comes from and the UTC time the data was captured.
These figures are recomputed every session, so a level repeated without its
date is not stale, it is wrong.The contract, precisely:
## Citation section, and
nothing follows it. That section carries exactly one URL and a
Retrieved (UTC) stamp. That URL is the citation.-unavailable in
utm_content, and says plainly that no figure was reported, so there is
none to quote.squawkflow.com link in a result is a pointer, not the
citation, and carries the same stamp. That includes the methodology
pointer on a coverage substitution and the links inside a published
document body, which are rewritten to the stamped form as the document
passes the boundary. A whole-response scan finds no untagged link.search and fetch answer a different contract, because they answer
somebody else's: they return JSON, and the citation is the url field, one
per search result and one on a fetched document, never empty. Their dates
are the payload's retrievedAt and the document's own published stamp in
metadata.Links are stamped utm_source=mcp, the transport in utm_medium (stdio or
http), and the artefact in utm_content (gex-levels, gamma-grid,
max-pain, vix-curve, lab-receipts, catalog, a -methodology variant on
a coverage note, or a -unavailable variant on a failed call). A number is
never removed from a response to make that link the only way to get it.
Two of these tools carry a positive allowlist. get_lab_record returns dated
simulated-record receipts and counts of them and nothing about how any record
turned out. get_session_record returns the levels as they were published and
the verdict on each, and no session price extreme or distance computed from
one.
Those allowlists are worth nothing if another route returns the same pages' raw
markdown, so fetch and every sf:// resource go through one content policy,
written once in src/policy.ts, with two dispositions:
/lab is that case: the route
refuses and names get_lab_record as the contract. The page stays published
on the web for people to read, and search still returns it as a link.get_session_record
already returns: the published levels, their verdicts and the capture
coverage.Every other page passes through a general claim filter that drops sentences and
table cells carrying performance statistics or advice, the same rule applied to
upstream API prose. /corrections is exempt, and deliberately so: it is the
ledger of claims that were published and later withdrawn, and a filter that
removed the withdrawn claim from its own withdrawal notice would hide the
correction.
Removal is never silent. A filtered document says what was removed, where, and which tool is the contract for it, so a model reading one knows it is reading a filtered copy rather than the whole published page.
Two claims are deliberately absent from every response. Wall hold rates are not
returned: SquawkFlow withdrew that statistic on 2026-08-31 because it was
measured against a third-party wall definition and was dominated by distance,
and measured near the money it was 49.4% rather than the headline figure. Do not
cite a hold rate for these levels from any previously retrieved copy. The
/dark-pool-flow block-print tape is not exposed at all: those prints are fully
modeled with no real-print path, so presenting them through a data tool would be
a misuse the page's own disclosure exists to prevent.
The public endpoints allow 60 requests per minute per IP address. This
server spaces its own outbound calls one second apart, server wide, so a burst
of concurrent tool calls queues instead of racing. A 429 still surfaces a
plain message naming the limit and the retry delay, because the address may be
shared with another client.
Prefer reusing a result already in context over re-fetching it. Details: https://squawkflow.com/docs/rate-limits.
Requests time out at 10 seconds, except the two tools that can trigger a
recompute from a raw options chain. get_gex_levels for SPY or QQQ and
get_gamma_heatmap for any symbol allow 30 seconds, because a cold cache on
those paths measured 13 to 27 seconds against production on 2026-09-01, and a
10 second cap there does not make the call fast, it makes it fail. SPX gamma
levels come from a daily archive rather than a live chain and stay on the 10
second budget.
A cold call warms a cache shared by every caller, so a request that does time out is usually fast on the retry. The timeout message says so.
The hosted endpoint shares one machine with the backend that answers squawkflow.com, so an agent that loops on a cold chain recompute is spending the site's own capacity. Three mechanisms sit between a tool call and that machine.
get_gamma_heatmap for any symbol, get_gex_levels for SPY or QQQ) run at
once, with a short queue behind them. Past the queue a caller is told the
server is busy and given a retry delay, rather than being parked until its
client gives up.Absence is not failure. When the API answers that it has no snapshot right now, that is the answer: the gap is reported as a gap, no cached copy stands in for it, and it never counts toward the breaker. The reverse also holds, an outage is never quietly reported as missing data.
House rule: a number is never removed from a tool response to force a click. The dated link is an addition to a complete answer, never a substitute for part of one.
A plain GET on the endpoint answers with a description of the server rather
than a protocol error: what it is, the tool list with one line each, the
prompts, the sf:// resources, what it deliberately does not publish, and the
connect line.
curl https://mcp.squawkflow.com/
GET / and a sessionless GET /mcp both serve it, as text/plain by default
and as HTML to a browser. POST is unchanged, a GET that asks for
text/event-stream still opens the Streamable HTTP channel and still gets the
session error when it has no session, and /.well-known/mcp-registry-auth is
untouched.
This exists because the bare failure a plain GET used to return was read by a
researching agent as evidence that this server's capabilities could not be
verified. The error was correct and the conclusion was wrong.
A second entry point serves MCP over Streamable HTTP.
It binds to loopback; the hosted deployment reaches the public internet through
the Cloudflare tunnel at https://mcp.squawkflow.com/mcp, not by binding a
public interface.
Hosts that support MCP Apps (for example ChatGPT and Claude Desktop) can render tool results in an inline panel instead of a wall of markdown. SquawkFlow has not been approved or listed by any host; this describes what the server exposes, not a guarantee that a given client will show it.
The panel (ui://squawkflow/panel.html) shows only figures that already appear
in the text tool result: metrics, an optional table, and the same honesty notes.
It never adds numbers the text does not carry.
Click-out links use https://squawkflow.com<page>?utm_source=<venue>&utm_medium=mcp-app
where venue is chatgpt-app, claude-app, or mcp-app from the host name.
This is separate from the ?utm_source=mcp&utm_medium=... citation URLs in the
text result.
There is no hosted MCP Apps endpoint on squawkflow.com. The panel ships inside this package and is served by your local stdio or Streamable HTTP install.
npm run build
npx squawkflow-mcp-http
# or: node dist/serve-http.js
By default the server binds to 127.0.0.1:8511. Routes:
POST|GET|DELETE /mcp: stateful MCP sessions (one server plus one transport
per session; enableJsonResponse: true so POST answers with JSON rather than
SSE, which keeps local tests simple)GET /healthz: {"ok": true, "version", "sessions": <live count>, "inFlight", "chainInFlight", "openBreakers", "cachedArtifacts"}404 JSONEnvironment variables:
| Variable | Default | Purpose |
|---|---|---|
SQUAWKFLOW_MCP_HOST | 127.0.0.1 | Bind address (never 0.0.0.0 unless you set it explicitly) |
SQUAWKFLOW_MCP_PORT | 8511 | Listen port |
SQUAWKFLOW_MCP_MAX_SESSIONS | 200 | Concurrent sessions; new initialize requests get 503 when full |
SQUAWKFLOW_MCP_ORIGIN_PER_MINUTE | 50 | Process-wide sliding one-minute cap on outbound API calls before SquawkFlow's own 60/min per-IP limit |
SQUAWKFLOW_MCP_SESSION_LOG | <package>/data/mcp_sessions.jsonl | Append-only local call log (see below) |
SQUAWKFLOW_MCP_CHAIN_CONCURRENCY | 2 | Chain recomputes allowed at once, process wide |
SQUAWKFLOW_MCP_CHAIN_QUEUE | 4 | Callers allowed to wait for a chain slot before the rest are told the server is busy |
SQUAWKFLOW_MCP_MAX_INFLIGHT | 6 | Ceiling on all tool calls in flight, cheap ones included |
SQUAWKFLOW_MCP_MAX_QUEUE | 12 | Queue depth for that ceiling |
SQUAWKFLOW_MCP_QUEUE_WAIT_MS | 20000 | How long a queued caller waits before it is told the server is busy |
SQUAWKFLOW_MCP_BREAKER_FAILURES | 3 | Consecutive infrastructure failures that open a breaker |
SQUAWKFLOW_MCP_BREAKER_COOLDOWN_MS | 60000 | How long a breaker stays open before one probe is allowed through |
SQUAWKFLOW_MCP_STALE_MAX_AGE_MS | 86400000 | Oldest a cached answer may be and still be served as an explicit stale copy |
SQUAWKFLOW_API_BASE | https://api.squawkflow.com | Upstream API origin (same as stdio) |
SQUAWKFLOW_MIRROR_BASE | https://squawkflow.com | Where the published markdown mirrors are read from, for search, fetch and the sf:// resources. Citation URLs always name squawkflow.com regardless |
Throttle budgets. Each MCP session spaces its own outbound calls one second apart (1000 ms). On top of that, the process enforces a 50 requests per minute sliding window on all sessions combined, because every remote install shares this server's egress address against SquawkFlow's 60 requests per minute per IP public cap.
Call log. The server appends JSON lines to SQUAWKFLOW_MCP_SESSION_LOG, a
local file that is never uploaded and that nothing in this package reads back.
Two line shapes:
{"ts_utc", "event": "session", "session_id", "transport", "client_name", "client_version"}
once per HTTP session, at onsessioninitialized when getClientVersion() is
already populated, otherwise on the first tools/list or tools/call.{"ts_utc", "event": "tool_call", "tool", "transport", "outcome", "symbol"?, "reason"?}
once per tool call, on stdio and HTTP alike. outcome is ok, stale or
error, so a failure is recorded as a failure rather than as silence.
symbol is a coarse enum only: SPX, SPY, QQQ, or the literal
unsupported when the caller asked for something outside the coverage list.The tool-call line carries no session or client identifier, and neither line carries an IP address, a header, a raw argument or any part of a response. The symbol a caller typed for an uncovered ticker is never written down. A log write failure is reported once on stderr and never fails the request.
Idle sessions with no request for 30 minutes are closed by a background sweep.
The sweep timer is unref()ed so it does not keep the process alive on its own.
npm test
npm run build # tsc to dist/
node scripts/smoke.mjs # initialize + tools/list + one tools/call over real stdio
node scripts/smoke.mjs get_lab_record '{"limit":3}'
SQUAWKFLOW_API_BASE overrides the API origin for local testing. Anything other
than the public production API is unsupported.
list_squawkflow_toolsMIT
FAQs
Read-only MCP server for SquawkFlow's free market structure data: dealer gamma levels, the gamma grid, the sector gamma matrix, overnight open interest change, max pain, the VIX futures curve, the published exchange calendar, dated session records, 13F fi
We found that squawkflow-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.