New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

squawkflow-mcp

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

squawkflow-mcp

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

latest
Source
npmnpm
Version
0.3.0
Version published
Maintainers
1
Created
Source

squawkflow-mcp

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.

Hosted endpoint (no install)

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.

Install

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 Code

claude mcp add squawkflow -- node /absolute/path/to/mcp/squawkflow-mcp/dist/index.js

Claude Desktop

Add this to claude_desktop_config.json:

{
  "mcpServers": {
    "squawkflow": {
      "command": "node",
      "args": ["/absolute/path/to/mcp/squawkflow-mcp/dist/index.js"]
    }
  }
}

ChatGPT, Cursor, Windsurf, and other MCP clients

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.

Tools

Fourteen data tools plus the catalog. Every one declares readOnlyHint, destructiveHint: false, idempotentHint and openWorldHint, and an outputSchema for the structured content it returns.

ToolArgumentsReturns
list_squawkflow_toolsnoneThe 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_levelssymbol: 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_heatmapsymbol: 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_matrixnoneOne 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_changeoptional symbol, optional date (ISO settlement to read into), limit: 1 to 50, scan: true for the universePer-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_painsymbol: SPX (anything else returns the SPX answer plus a coverage note), optional expirationThe settlement strike minimising aggregate option payout for one expiration, the payout curve around it, and the available expirations
get_vix_term_structurenoneEvery 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_calendaroptional 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_recordoptional 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_receiptoptional manager, optional quarter (YYYY-qN), limit: 1 to 50A 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_disclosuresoptional symbol, chamber, filer, since (ISO, on the public disclosure date), limit: 1 to 100House 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_positioningoptional contract slug, weeks: 1 to 52Weekly CFTC Traders in Financial Futures positioning by trader classification, with the Tuesday it was measured and the Friday it was published
get_lab_recordlimit: 1 to 50, optional engineAbsolute session dates, known engine identifiers, grading status and counts of returned simulated-record receipts; no upstream commentary or performance summaries
searchquery, limit: 1 to 20Ids, titles and canonical URLs for the pages SquawkFlow publishes for machine reading. A query naming an ISO date finds the dated record for it
fetchidOne 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.

Resources

URIWhat it is
sf://catalogThe catalog body, readable without spending a tool call
sf://manifestThe dataset manifest: every dataset, its upstream source, whether it is measured or derived, and how often it refreshes
sf://correctionsThe 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.htmlThe 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.

Prompts

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.

Citation contract

Every tool result carries these terms, and every tool description repeats them before the call is made.

  • Data is delayed and derived. None of it is a real-time feed or an exchange record. When real data is unavailable, SquawkFlow serves no simulated stand-in: the gap is reported as a gap.
  • Dealer positioning is an assumption, not an observable. Open interest shows that a contract exists, never which side a dealer holds. Every gamma figure published anywhere, ours and every competitor's, inherits that assumption. Read gamma levels as a map of where hedging pressure would concentrate if the standard convention holds, not as a record of trades.
  • Cite the page with the capture date. Each result names the 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.
  • Never present a level, wall or regime without its timestamp.
  • Dates are absolute. No "today", no "this morning", no relative phrasing.
  • Nothing here is investment advice, a price target or a recommendation.
  • Any number you remember for these figures is from a different session. Every tool description says so, because a model that answers a gamma question from recall is not producing a stale number, it is producing a wrong one.

The contract, precisely:

  • Every markdown result ends at exactly one ## Citation section, and nothing follows it. That section carries exactly one URL and a Retrieved (UTC) stamp. That URL is the citation.
  • This holds on every markdown path, not only the successful one. A reading, a coverage substitution, reported absence, a stale copy, a refusal and an infrastructure failure or timeout all end at the same footer. A failure footer points at the tool's own page with -unavailable in utm_content, and says plainly that no figure was reported, so there is none to quote.
  • Any other 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.
  • The one exception is a protocol-level schema validation error, which the SDK produces before any of this code runs and which carries no citation. It is named here so the contract has no unstated holes.

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.

The content boundary

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:

  • Refuse. A page that is, in its majority, the restricted class is not served at all through these routes, because a filtered copy would be a husk shaped like the content it no longer carried. /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.
  • Filter. A page carrying a nameable minority of the restricted class is served with that part removed: whole sections, table columns, table rows and sentences, each replaced in place by a marker, with a closing section naming what came out and which tool the data belongs to. The dated session records are that case. What remains is exactly the projection 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.

Rate limits

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.

Timeouts

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.

Protection and degraded answers

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.

  • A concurrency cap on chain recomputes. At most two of the expensive paths (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.
  • A circuit breaker per artefact. Three consecutive infrastructure failures (timeout, network, 5xx, rate limit) stop the calls for about a minute, then one probe decides whether to resume. A backend that is struggling is not retried into the ground.
  • A last-good fallback. When an infrastructure failure has a previous good answer to fall back on, that answer is served again with a banner that says STALE COPY, names the reason the live call failed, and carries three absolute dates: when the data was captured, when the copy was first retrieved, and when it was served again. Nothing in it is recomputed or restamped.

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.

Discovery

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.

Remote (Streamable HTTP)

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.

Inline component (MCP Apps)

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"}
  • everything else: 404 JSON

Environment variables:

VariableDefaultPurpose
SQUAWKFLOW_MCP_HOST127.0.0.1Bind address (never 0.0.0.0 unless you set it explicitly)
SQUAWKFLOW_MCP_PORT8511Listen port
SQUAWKFLOW_MCP_MAX_SESSIONS200Concurrent sessions; new initialize requests get 503 when full
SQUAWKFLOW_MCP_ORIGIN_PER_MINUTE50Process-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.jsonlAppend-only local call log (see below)
SQUAWKFLOW_MCP_CHAIN_CONCURRENCY2Chain recomputes allowed at once, process wide
SQUAWKFLOW_MCP_CHAIN_QUEUE4Callers allowed to wait for a chain slot before the rest are told the server is busy
SQUAWKFLOW_MCP_MAX_INFLIGHT6Ceiling on all tool calls in flight, cheap ones included
SQUAWKFLOW_MCP_MAX_QUEUE12Queue depth for that ceiling
SQUAWKFLOW_MCP_QUEUE_WAIT_MS20000How long a queued caller waits before it is told the server is busy
SQUAWKFLOW_MCP_BREAKER_FAILURES3Consecutive infrastructure failures that open a breaker
SQUAWKFLOW_MCP_BREAKER_COOLDOWN_MS60000How long a breaker stays open before one probe is allowed through
SQUAWKFLOW_MCP_STALE_MAX_AGE_MS86400000Oldest a cached answer may be and still be served as an explicit stale copy
SQUAWKFLOW_API_BASEhttps://api.squawkflow.comUpstream API origin (same as stdio)
SQUAWKFLOW_MIRROR_BASEhttps://squawkflow.comWhere 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

Development

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.

License

MIT

Keywords

mcp

FAQs

Package last updated on 21 Sep 2026

Related posts