{"_id":"squawkflow-mcp","_rev":"2-9637355bdccf13caa80da48b8e3559d7","name":"squawkflow-mcp","dist-tags":{"latest":"0.3.0"},"versions":{"0.2.1":{"name":"squawkflow-mcp","version":"0.2.1","keywords":["mcp","model-context-protocol","gamma-exposure","gex","options","market-data","spx","vix","max-pain","open-interest","13f","sec-edgar","congress","cftc"],"license":"MIT","_id":"squawkflow-mcp@0.2.1","maintainers":[{"name":"dhawal.chheda","email":"dhawal.chheda@gmail.com"}],"homepage":"https://squawkflow.com","bugs":{"url":"https://github.com/dhawalc/squawkflow/issues"},"bin":{"squawkflow-mcp":"dist/index.js","squawkflow-mcp-http":"dist/serve-http.js"},"dist":{"shasum":"2fa617c0a6fc32a52a672c130b0e772222722ffb","tarball":"https://registry.npmjs.org/squawkflow-mcp/-/squawkflow-mcp-0.2.1.tgz","fileCount":30,"integrity":"sha512-DUxl+as44DRiVJYfphphpVyiTb/6YdYwOJkcT0YbOU66hiESBqFhyz+nc0bVlZCEBcW8duAELJWC9p2jY27AZA==","signatures":[{"sig":"MEUCID2teKtKUF4shrXHSrdkZIwzrkkKwnCragsp8WXmOhT2AiEAuoVn1jZBPw9gwUxbJTYm4sf8kzgj8tWxj8+zB10fQ80=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":346865},"main":"dist/index.js","type":"module","engines":{"node":">=18"},"gitHead":"3634780934f373b027a8add5ec14806ef594c32e","mcpName":"com.squawkflow.mcp/market-structure","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc","start":"node dist/index.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"dhawal.chheda","email":"dhawal.chheda@gmail.com"},"repository":{"url":"git+https://github.com/dhawalc/squawkflow.git","type":"git","directory":"mcp/squawkflow-mcp"},"_npmVersion":"11.19.1","description":"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, dated session records, 13F filing receipts, congressional disc","directories":{},"_nodeVersion":"26.8.2","dependencies":{"zod":"^4.5.4","@modelcontextprotocol/sdk":"^1.30.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/squawkflow-mcp_0.2.1_1789885729090_0.023134501575029054","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"squawkflow-mcp@0.3.0","bin":{"squawkflow-mcp":"dist/index.js","squawkflow-mcp-http":"dist/serve-http.js"},"bugs":{"url":"https://github.com/dhawalc/squawkflow/issues"},"dist":{"shasum":"4404f4c785113d684d13d9d756a9083a6615eb7d","tarball":"https://registry.npmjs.org/squawkflow-mcp/-/squawkflow-mcp-0.3.0.tgz","fileCount":31,"integrity":"sha512-aGxvc30meDdiT9GeR6EIfIntSV/OlF++/249FpRSvAgxMLRaauugf0pNYNzzhGMBoWLXykh7BtPY8u4Vyd7nSg==","signatures":[{"sig":"MEQCIAOR0fqINejo4Zu4gelHVsVdh3JkXHhiFk4AkYwpB0TIAiBNxKg9lW6LFT7Hjk+qrcP2aH9gbGVpxR9TXxGb5ZPXdg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFySaIGMS0qBH1hanTxFtNdmZ9L+R4F9kMMHEIBFZuRoAiBffhGrH7b7lySz8teZBdf6FUtsJTaLPq3cfYTky1fB6A=="}],"unpackedSize":375426},"main":"dist/index.js","name":"squawkflow-mcp","type":"module","engines":{"node":">=18"},"gitHead":"103f1f4ff33d79c635e275500ab7506e8e553ecc","license":"MIT","mcpName":"com.squawkflow.mcp/market-structure","scripts":{"test":"npm run build && node --test test/*.test.mjs","build":"tsc","start":"node dist/index.js","prepublishOnly":"npm run build"},"version":"0.3.0","_npmUser":{"name":"dhawal.chheda","email":"dhawal.chheda@gmail.com"},"homepage":"https://squawkflow.com","keywords":["mcp","model-context-protocol","gamma-exposure","gex","options","market-data","spx","vix","max-pain","open-interest","13f","sec-edgar","congress","cftc","market-calendar"],"repository":{"url":"git+https://github.com/dhawalc/squawkflow.git","type":"git","directory":"mcp/squawkflow-mcp"},"_npmVersion":"11.19.1","description":"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","directories":{},"maintainers":[{"name":"dhawal.chheda","email":"dhawal.chheda@gmail.com"}],"_nodeVersion":"26.8.2","dependencies":{"zod":"^4.5.4","@modelcontextprotocol/sdk":"^1.30.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/squawkflow-mcp_0.3.0_1789967056810_0.9376546036400957"}}},"time":{"created":"2026-09-20T06:28:48.903Z","modified":"2026-09-21T05:04:17.105Z","0.2.1":"2026-09-20T06:28:49.282Z","0.3.0":"2026-09-21T05:04:16.917Z"},"bugs":{"url":"https://github.com/dhawalc/squawkflow/issues"},"license":"MIT","homepage":"https://squawkflow.com","keywords":["mcp","model-context-protocol","gamma-exposure","gex","options","market-data","spx","vix","max-pain","open-interest","13f","sec-edgar","congress","cftc","market-calendar"],"repository":{"url":"git+https://github.com/dhawalc/squawkflow.git","type":"git","directory":"mcp/squawkflow-mcp"},"description":"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","maintainers":[{"name":"dhawal.chheda","email":"dhawal.chheda@gmail.com"}],"readme":"# squawkflow-mcp\n\nRead-only [MCP](https://modelcontextprotocol.io) server over SquawkFlow's free\npublic market-structure data: dealer gamma levels, the strike by expiration\ngamma grid, the sector gamma matrix, overnight open interest change, max pain,\nthe CBOE VIX futures settlement curve, dated session records, 13F filing\nreceipts, congressional disclosures, CFTC futures positioning, and the Lab\npaper-trading record. Plus `search` and `fetch` over every page SquawkFlow\npublishes for machine reading.\n\nNo account, no API key, no configuration. Nothing here places an order,\nSquawkFlow has no order execution, and every tool is a plain GET against\n`https://api.squawkflow.com/api/public`.\n\n## Hosted endpoint (no install)\n\nThe server also runs hosted, speaking MCP streamable HTTP:\n\n```\nhttps://mcp.squawkflow.com/mcp\n```\n\nClients 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.\n\nThat endpoint is loopback on the same machine as the SquawkFlow backend and the\nCloudflare tunnel, not a CDN edge, which is why the protection described under\n[Protection and degraded answers](#protection-and-degraded-answers) exists.\n\n## Install\n\nPublished on npm as [`squawkflow-mcp`](https://www.npmjs.com/package/squawkflow-mcp). One command, no checkout:\n\n```bash\nnpx -y squawkflow-mcp\n```\n\nOr from a checkout for development: `cd mcp/squawkflow-mcp && npm install && npm run build`.\n\n### Claude Code\n\n```bash\nclaude mcp add squawkflow -- node /absolute/path/to/mcp/squawkflow-mcp/dist/index.js\n```\n\n### Claude Desktop\n\nAdd this to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"squawkflow\": {\n      \"command\": \"node\",\n      \"args\": [\"/absolute/path/to/mcp/squawkflow-mcp/dist/index.js\"]\n    }\n  }\n}\n```\n\n### ChatGPT, Cursor, Windsurf, and other MCP clients\n\nAny client that speaks MCP over stdio takes the same two fields: the command\n`node` and one argument, the absolute path to `dist/index.js`. Clients that\naccept a single command string want\n`node /absolute/path/to/mcp/squawkflow-mcp/dist/index.js`.\n\nOnce the package is on npm the command becomes `npx` with args\n`[\"-y\", \"squawkflow-mcp\"]` everywhere above, with no other change.\n\n## Tools\n\nFourteen data tools plus the catalog. Every one declares\n`readOnlyHint`, `destructiveHint: false`, `idempotentHint` and `openWorldHint`,\nand an `outputSchema` for the structured content it returns.\n\n| Tool | Arguments | Returns |\n| --- | --- | --- |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `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 |\n| `fetch` | `id` | One published page verbatim from its markdown mirror, with a non-empty canonical URL and the vintage the page states for itself |\n\n`search` and `fetch` read the site's published files rather than the API, so a\nwide reading pass costs the market data endpoints nothing. They return a JSON\nobject in both `structuredContent` and `content[0].text`, with a non-empty\n`url` on every result, because that is what makes a ChatGPT citation point back\nat the page.\n\n## Resources\n\n| URI | What it is |\n| --- | --- |\n| `sf://catalog` | The catalog body, readable without spending a tool call |\n| `sf://manifest` | The dataset manifest: every dataset, its upstream source, whether it is measured or derived, and how often it refreshes |\n| `sf://corrections` | The dated ledger of claims SquawkFlow published and later withdrew or corrected |\n| `sf://methodology/{page}` | One methodology or explainer page, for example `sf://methodology/how-to-calculate-gamma-exposure` |\n| `sf://session/{date}` | One dated session record page, for example `sf://session/2026-09-17` |\n| `sf://page/{+path}` | Any published page, for example `sf://page/gex` or `sf://page/glossary/zero-gamma` |\n| `ui://squawkflow/panel.html` | The inline MCP Apps panel |\n\nThe three templates complete their parameter from what is actually published.\n\nEvery route that returns a document body, `fetch` and all six resources alike,\npasses through the same content policy as the dedicated tools. See\n[The content boundary](#the-content-boundary).\n\n## Prompts\n\nEight registered prompts, which appear as slash commands in Claude Code, VS\nCode, Cursor, Goose and Zed. Each one names the tools to call and the order to\ncall them in, states the limits in the words the tools use, and ends at one\ndated squawkflow.com link.\n\n`morning-brief`, `gamma-context`, `whats-new-overnight`, `filing-check`,\n`disclosure-check`, `session-record`, `expiration-week`, and\n`what-squawkflow-wont-say`.\n\nEvery prompt argument is a single token. Claude Code splits prompt arguments on\nwhitespace, so an ISO date or a slug works and free text does not.\n\nResults come back as markdown rather than JSON. SquawkFlow publishes markdown\nmirrors of these same pages for the same reason: a model re-emitting a labelled\nprose block keeps the label attached to the number, while a model re-emitting a\nJSON blob tends to keep the number and drop the timestamp, and the timestamp is\nwhat stops a stale level being quoted as a current one.\n\n## Citation contract\n\nEvery tool result carries these terms, and every tool description repeats them\nbefore the call is made.\n\n- **Data is delayed and derived.** None of it is a real-time feed or an exchange\n  record. When real data is unavailable, SquawkFlow serves no simulated\n  stand-in: the gap is reported as a gap.\n- **Dealer positioning is an assumption, not an observable.** Open interest\n  shows that a contract exists, never which side a dealer holds. Every gamma\n  figure published anywhere, ours and every competitor's, inherits that\n  assumption. Read gamma levels as a map of where hedging pressure would\n  concentrate if the standard convention holds, not as a record of trades.\n- **Cite the page with the capture date.** Each result names the\n  `squawkflow.com` page it comes from and the UTC time the data was captured.\n  These figures are recomputed every session, so a level repeated without its\n  date is not stale, it is wrong.\n- **Never present a level, wall or regime without its timestamp.**\n- **Dates are absolute.** No \"today\", no \"this morning\", no relative phrasing.\n- **Nothing here is investment advice**, a price target or a recommendation.\n- **Any number you remember for these figures is from a different session.**\n  Every tool description says so, because a model that answers a gamma question\n  from recall is not producing a stale number, it is producing a wrong one.\n\nThe contract, precisely:\n\n1. **Every markdown result ends at exactly one `## Citation` section, and\n   nothing follows it.** That section carries exactly one URL and a\n   `Retrieved (UTC)` stamp. That URL is the citation.\n2. **This holds on every markdown path, not only the successful one.** A\n   reading, a coverage substitution, reported absence, a stale copy, a refusal\n   and an infrastructure failure or timeout all end at the same footer. A\n   failure footer points at the tool's own page with `-unavailable` in\n   `utm_content`, and says plainly that no figure was reported, so there is\n   none to quote.\n3. **Any other `squawkflow.com` link in a result is a pointer, not the\n   citation**, and carries the same stamp. That includes the methodology\n   pointer on a coverage substitution and the links inside a published\n   document body, which are rewritten to the stamped form as the document\n   passes the boundary. A whole-response scan finds no untagged link.\n4. **`search` and `fetch` answer a different contract**, because they answer\n   somebody else's: they return JSON, and the citation is the `url` field, one\n   per search result and one on a fetched document, never empty. Their dates\n   are the payload's `retrievedAt` and the document's own published stamp in\n   `metadata`.\n5. **The one exception** is a protocol-level schema validation error, which\n   the SDK produces before any of this code runs and which carries no\n   citation. It is named here so the contract has no unstated holes.\n\nLinks are stamped `utm_source=mcp`, the transport in `utm_medium` (`stdio` or\n`http`), and the artefact in `utm_content` (`gex-levels`, `gamma-grid`,\n`max-pain`, `vix-curve`, `lab-receipts`, `catalog`, a `-methodology` variant on\na coverage note, or a `-unavailable` variant on a failed call). A number is\nnever removed from a response to make that link the only way to get it.\n\n## The content boundary\n\nTwo of these tools carry a positive allowlist. `get_lab_record` returns dated\nsimulated-record receipts and counts of them and nothing about how any record\nturned out. `get_session_record` returns the levels as they were published and\nthe verdict on each, and no session price extreme or distance computed from\none.\n\nThose allowlists are worth nothing if another route returns the same pages' raw\nmarkdown, so `fetch` and every `sf://` resource go through one content policy,\nwritten once in `src/policy.ts`, with two dispositions:\n\n- **Refuse.** A page that is, in its majority, the restricted class is not\n  served at all through these routes, because a filtered copy would be a husk\n  shaped like the content it no longer carried. `/lab` is that case: the route\n  refuses and names `get_lab_record` as the contract. The page stays published\n  on the web for people to read, and search still returns it as a link.\n- **Filter.** A page carrying a nameable minority of the restricted class is\n  served with that part removed: whole sections, table columns, table rows and\n  sentences, each replaced in place by a marker, with a closing section naming\n  what came out and which tool the data belongs to. The dated session records\n  are that case. What remains is exactly the projection `get_session_record`\n  already returns: the published levels, their verdicts and the capture\n  coverage.\n\nEvery other page passes through a general claim filter that drops sentences and\ntable cells carrying performance statistics or advice, the same rule applied to\nupstream API prose. `/corrections` is exempt, and deliberately so: it is the\nledger of claims that were published and later withdrawn, and a filter that\nremoved the withdrawn claim from its own withdrawal notice would hide the\ncorrection.\n\nRemoval is never silent. A filtered document says what was removed, where, and\nwhich tool is the contract for it, so a model reading one knows it is reading a\nfiltered copy rather than the whole published page.\n\nTwo claims are deliberately absent from every response. Wall hold rates are not\nreturned: SquawkFlow withdrew that statistic on 2026-08-31 because it was\nmeasured against a third-party wall definition and was dominated by distance,\nand measured near the money it was 49.4% rather than the headline figure. Do not\ncite a hold rate for these levels from any previously retrieved copy. The\n`/dark-pool-flow` block-print tape is not exposed at all: those prints are fully\nmodeled with no real-print path, so presenting them through a data tool would be\na misuse the page's own disclosure exists to prevent.\n\n## Rate limits\n\nThe public endpoints allow **60 requests per minute per IP address**. This\nserver spaces its own outbound calls one second apart, server wide, so a burst\nof concurrent tool calls queues instead of racing. A `429` still surfaces a\nplain message naming the limit and the retry delay, because the address may be\nshared with another client.\n\nPrefer reusing a result already in context over re-fetching it. Details:\n<https://squawkflow.com/docs/rate-limits>.\n\n## Timeouts\n\nRequests time out at 10 seconds, except the two tools that can trigger a\nrecompute from a raw options chain. `get_gex_levels` for SPY or QQQ and\n`get_gamma_heatmap` for any symbol allow 30 seconds, because a cold cache on\nthose paths measured 13 to 27 seconds against production on 2026-09-01, and a\n10 second cap there does not make the call fast, it makes it fail. SPX gamma\nlevels come from a daily archive rather than a live chain and stay on the 10\nsecond budget.\n\nA cold call warms a cache shared by every caller, so a request that does time\nout is usually fast on the retry. The timeout message says so.\n\n## Protection and degraded answers\n\nThe hosted endpoint shares one machine with the backend that answers\nsquawkflow.com, so an agent that loops on a cold chain recompute is spending the\nsite's own capacity. Three mechanisms sit between a tool call and that machine.\n\n- **A concurrency cap on chain recomputes.** At most two of the expensive paths\n  (`get_gamma_heatmap` for any symbol, `get_gex_levels` for SPY or QQQ) run at\n  once, with a short queue behind them. Past the queue a caller is told the\n  server is busy and given a retry delay, rather than being parked until its\n  client gives up.\n- **A circuit breaker per artefact.** Three consecutive infrastructure failures\n  (timeout, network, 5xx, rate limit) stop the calls for about a minute, then one\n  probe decides whether to resume. A backend that is struggling is not retried\n  into the ground.\n- **A last-good fallback.** When an infrastructure failure has a previous good\n  answer to fall back on, that answer is served again with a banner that says\n  STALE COPY, names the reason the live call failed, and carries three absolute\n  dates: when the data was captured, when the copy was first retrieved, and when\n  it was served again. Nothing in it is recomputed or restamped.\n\n**Absence is not failure.** When the API answers that it has no snapshot right\nnow, that is the answer: the gap is reported as a gap, no cached copy stands in\nfor it, and it never counts toward the breaker. The reverse also holds, an\noutage is never quietly reported as missing data.\n\n**House rule:** a number is never removed from a tool response to force a click.\nThe dated link is an addition to a complete answer, never a substitute for part\nof one.\n\n## Discovery\n\nA plain `GET` on the endpoint answers with a description of the server rather\nthan a protocol error: what it is, the tool list with one line each, the\nprompts, the `sf://` resources, what it deliberately does not publish, and the\nconnect line.\n\n```\ncurl https://mcp.squawkflow.com/\n```\n\n`GET /` and a sessionless `GET /mcp` both serve it, as `text/plain` by default\nand as HTML to a browser. `POST` is unchanged, a `GET` that asks for\n`text/event-stream` still opens the Streamable HTTP channel and still gets the\nsession error when it has no session, and `/.well-known/mcp-registry-auth` is\nuntouched.\n\nThis exists because the bare failure a plain `GET` used to return was read by a\nresearching agent as evidence that this server's capabilities could not be\nverified. The error was correct and the conclusion was wrong.\n\n## Remote (Streamable HTTP)\n\nA second entry point serves MCP over [Streamable HTTP](https://modelcontextprotocol.io).\nIt binds to loopback; the hosted deployment reaches the public internet through\nthe Cloudflare tunnel at `https://mcp.squawkflow.com/mcp`, not by binding a\npublic interface.\n\n## Inline component (MCP Apps)\n\nHosts that support [MCP Apps](https://modelcontextprotocol.io) (for example\nChatGPT and Claude Desktop) can render tool results in an inline panel instead\nof a wall of markdown. SquawkFlow has not been approved or listed by any host;\nthis describes what the server exposes, not a guarantee that a given client\nwill show it.\n\nThe panel (`ui://squawkflow/panel.html`) shows only figures that already appear\nin the text tool result: metrics, an optional table, and the same honesty notes.\nIt never adds numbers the text does not carry.\n\nClick-out links use `https://squawkflow.com<page>?utm_source=<venue>&utm_medium=mcp-app`\nwhere `venue` is `chatgpt-app`, `claude-app`, or `mcp-app` from the host name.\nThis is separate from the `?utm_source=mcp&utm_medium=...` citation URLs in the\ntext result.\n\nThere is no hosted MCP Apps endpoint on squawkflow.com. The panel ships inside\nthis package and is served by your local stdio or Streamable HTTP install.\n\n```bash\nnpm run build\nnpx squawkflow-mcp-http\n# or: node dist/serve-http.js\n```\n\nBy default the server binds to `127.0.0.1:8511`. Routes:\n\n- `POST|GET|DELETE /mcp`: stateful MCP sessions (one server plus one transport\n  per session; `enableJsonResponse: true` so POST answers with JSON rather than\n  SSE, which keeps local tests simple)\n- `GET /healthz`: `{\"ok\": true, \"version\", \"sessions\": <live count>, \"inFlight\", \"chainInFlight\", \"openBreakers\", \"cachedArtifacts\"}`\n- everything else: `404` JSON\n\nEnvironment variables:\n\n| Variable | Default | Purpose |\n| --- | --- | --- |\n| `SQUAWKFLOW_MCP_HOST` | `127.0.0.1` | Bind address (never `0.0.0.0` unless you set it explicitly) |\n| `SQUAWKFLOW_MCP_PORT` | `8511` | Listen port |\n| `SQUAWKFLOW_MCP_MAX_SESSIONS` | `200` | Concurrent sessions; new initialize requests get `503` when full |\n| `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 |\n| `SQUAWKFLOW_MCP_SESSION_LOG` | `<package>/data/mcp_sessions.jsonl` | Append-only local call log (see below) |\n| `SQUAWKFLOW_MCP_CHAIN_CONCURRENCY` | `2` | Chain recomputes allowed at once, process wide |\n| `SQUAWKFLOW_MCP_CHAIN_QUEUE` | `4` | Callers allowed to wait for a chain slot before the rest are told the server is busy |\n| `SQUAWKFLOW_MCP_MAX_INFLIGHT` | `6` | Ceiling on all tool calls in flight, cheap ones included |\n| `SQUAWKFLOW_MCP_MAX_QUEUE` | `12` | Queue depth for that ceiling |\n| `SQUAWKFLOW_MCP_QUEUE_WAIT_MS` | `20000` | How long a queued caller waits before it is told the server is busy |\n| `SQUAWKFLOW_MCP_BREAKER_FAILURES` | `3` | Consecutive infrastructure failures that open a breaker |\n| `SQUAWKFLOW_MCP_BREAKER_COOLDOWN_MS` | `60000` | How long a breaker stays open before one probe is allowed through |\n| `SQUAWKFLOW_MCP_STALE_MAX_AGE_MS` | `86400000` | Oldest a cached answer may be and still be served as an explicit stale copy |\n| `SQUAWKFLOW_API_BASE` | `https://api.squawkflow.com` | Upstream API origin (same as stdio) |\n| `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 |\n\n**Throttle budgets.** Each MCP session spaces its own outbound calls **one second\napart** (1000 ms). On top of that, the process enforces a **50 requests per\nminute** sliding window on all sessions combined, because every remote install\nshares this server's egress address against SquawkFlow's **60 requests per minute\nper IP** public cap.\n\n**Call log.** The server appends JSON lines to `SQUAWKFLOW_MCP_SESSION_LOG`, a\nlocal file that is never uploaded and that nothing in this package reads back.\nTwo line shapes:\n\n- `{\"ts_utc\", \"event\": \"session\", \"session_id\", \"transport\", \"client_name\", \"client_version\"}`\n  once per HTTP session, at `onsessioninitialized` when `getClientVersion()` is\n  already populated, otherwise on the first `tools/list` or `tools/call`.\n- `{\"ts_utc\", \"event\": \"tool_call\", \"tool\", \"transport\", \"outcome\", \"symbol\"?, \"reason\"?}`\n  once per tool call, on stdio and HTTP alike. `outcome` is `ok`, `stale` or\n  `error`, so a failure is recorded as a failure rather than as silence.\n  `symbol` is a coarse enum only: `SPX`, `SPY`, `QQQ`, or the literal\n  `unsupported` when the caller asked for something outside the coverage list.\n\nThe tool-call line carries no session or client identifier, and neither line\ncarries an IP address, a header, a raw argument or any part of a response. The\nsymbol a caller typed for an uncovered ticker is never written down. A log write\nfailure is reported once on stderr and never fails the request.\n\nIdle sessions with no request for **30 minutes** are closed by a background sweep.\nThe sweep timer is `unref()`ed so it does not keep the process alive on its own.\n\n```bash\nnpm test\n```\n\n## Development\n\n```bash\nnpm run build                 # tsc to dist/\nnode scripts/smoke.mjs        # initialize + tools/list + one tools/call over real stdio\nnode scripts/smoke.mjs get_lab_record '{\"limit\":3}'\n```\n\n`SQUAWKFLOW_API_BASE` overrides the API origin for local testing. Anything other\nthan the public production API is unsupported.\n\n## Links\n\n- Catalog of everything here: call `list_squawkflow_tools`\n- Gamma levels: <https://squawkflow.com/gex>\n- Gamma by strike and expiration: <https://squawkflow.com/gex-heatmap>\n- Max pain: <https://squawkflow.com/spx-max-pain>\n- VIX term structure: <https://squawkflow.com/vix-term-structure>\n- The Lab: <https://squawkflow.com/lab>\n- API docs: <https://squawkflow.com/docs>\n\n## License\n\nMIT\n","readmeFilename":"README.md"}