hfmarketdata-mcp
The official MCP server for HF Market Data — 1-minute to daily bars for stocks, ETFs, futures, crypto, indices and FX since 2007, individual futures contracts, custom continuous series, term structures, options chains with Greeks and point-in-time SEC fundamentals, straight into Claude Code, Cursor, Codex or any MCP client.
Quickstart (60 seconds)
# 1. Install (pick one)
npm i -g hfmarketdata-mcp # from npm (once published)
npm i -g https://www.hfmarketdata.io/downloads/hfmarketdata-mcp-1.0.0.tgz # direct tarball, works today
# 2. Check the connection (keyless works out of the box: 30 requests/hour)
hfmarketdata-mcp --selftest
# OK https://www.hfmarketdata.io (312 ms, keyless) — datasets: stock, etf, futures, futures_contracts, crypto, index, fx, options
# 3. Add it to Claude Code
claude mcp add hfmarketdata -e HFMD_API_KEY=hfmd_live_… -- npx -y hfmarketdata-mcpThen just ask:
"Plot me the CL term structure" · "Compare ESZ25 and ESH26 over 30 days" · "Screen US stocks with PE<15 and FCF yield>6% and show their last 3 quarters"
The platform is entirely free. Keyless mode has low hourly limits (30 requests / hour); a free API key (free account at https://www.hfmarketdata.io/signup) gives 120 requests / minute, and higher limits are granted on request by e-mail to contact@spboucher.ai (also free). Set the key as HFMD_API_KEY; the server never logs it. Details: https://www.hfmarketdata.io/limits.
Tools
| Tool | What it does | Backed by |
|---|---|---|
search_symbols |
Find tickers (stock/etf/crypto/index/fx), futures roots or optionable underlyings | /v1/{asset}/tickers, /v1/futures/roots, /v1/options/tickers |
get_bars |
OHLCV bars 1min…1day for one or many symbols; vendor continuous futures; individual contracts (ESZ25) |
/v1/bars/{asset}/{ticker}, /v1/bars/{asset}?tickers=, /v1/futures/contract/{symbol}/bars |
get_futures_contracts |
Every expiration of a root with expiry / FND / status / volume / OI | /v1/futures/{root}/contracts |
get_futures_chain |
Listed contracts as of a date, front month first | /v1/futures/{root}/chain |
get_continuous |
Server-built continuous series: roll=volume|open_interest|calendar, adjust=none|back_adjusted|ratio, depth |
/v1/futures/{root}/continuous |
get_term_structure |
The curve on a date (prices, spreads, OI) — contango / backwardation | /v1/futures/{root}/term-structure |
get_options_chain |
EOD chain with IV and Greeks, filters by expiry / strikes / side; list_expirations |
/v1/options/chain/{t}, /v1/options/expirations/{t} |
get_coverage |
What exists for a contract or a ticker's fundamentals, gaps included | /v1/futures/contract/{s}/coverage, /v1/fundamentals/{t}/coverage |
get_financial_statements |
Income / balance / cash-flow, annual or quarterly, point-in-time; single XBRL fact history with concept |
/v1/fundamentals/{t}/statements, /v1/fundamentals/{t}/facts/{concept} |
get_ratios |
Latest P/E, P/B, EV/EBITDA, FCF yield, ROE, margins, leverage… | /v1/fundamentals/{t}/ratios |
get_ratios_daily |
Daily ratio history recomputed with the fundamentals known that day | /v1/fundamentals/{t}/ratios/daily |
screen_fundamentals |
filters="pe<15,roe>0.15,fcf_yield>0.06" + sort="fcf_yield:desc" (+ as_of) |
/v1/fundamentals/screener |
get_filings |
10-K / 10-Q / 8-K / 4 / 13F… with URLs | /v1/fundamentals/{t}/filings |
subscribe_filings |
Opens wss://…/v1/stream, subscribes to filings, returns up to N messages within a bounded time window |
websocket |
Resources: hfmarketdata://status (live dataset inventory) · hfmarketdata://limits (rate limits + your headroom).
Prompts: term-structure-analysis(root, as_of?, compare_to?) · compare-contracts(symbol_a, symbol_b, days?) · fundamentals-snapshot(ticker, quarters?).
Behaviour every tool shares
HFMD_BASE_URL(defaulthttps://www.hfmarketdata.io) andHFMD_API_KEY(optional) are the only configuration.format=jsonis always requested; both the v1 envelope{count,data}and the v2 envelope{data,meta}are understood.- Outputs are compact tables
{"columns":[…],"rows":[[…]]}. The defaultlimitis 500 rows (max 5 000); above 200 rows the tool returns the count, the first/last 15 rows, per-column numeric ranges and a hint to narrow the request — so a careless "all 1-minute bars since 2010" cannot flood the context. - Rate-limit headroom (
X-RateLimit-Remaining-*, reset) is appended to every result; a 429 comes back as a clear message withRetry-Afterand a reminder that a free API key lifts the low keyless limits. - Errors are uniform:
HF Market Data error <status> <CODE>: <message> [Docs: …], whether the API answered with the v2{"error":{code,message,docs}}envelope or the legacy{"detail"}. subscribe_filingsis bounded by design (max_messages≤ 100,timeout_seconds≤ 120) and closes the socket when it returns; it needs Node ≥ 22 (globalWebSocket) or Node 20 with--experimental-websocket.- Read-only: no tool ever writes anything anywhere.
Configuration snippets
Claude Code
claude mcp add hfmarketdata -e HFMD_API_KEY=hfmd_live_… -- npx -y hfmarketdata-mcp
# tarball install instead of npm:
npm i -g https://www.hfmarketdata.io/downloads/hfmarketdata-mcp-1.0.0.tgz && claude mcp add hfmarketdata -e HFMD_API_KEY=hfmd_live_… -- hfmarketdata-mcpOr in .mcp.json at the root of a project (shared with your team):
{
"mcpServers": {
"hfmarketdata": {
"command": "npx",
"args": ["-y", "hfmarketdata-mcp"],
"env": { "HFMD_API_KEY": "hfmd_live_…" }
}
}
} Cursor — .cursor/mcp.json (project) or ~/.cursor/mcp.json (global)
{
"mcpServers": {
"hfmarketdata": {
"command": "npx",
"args": ["-y", "hfmarketdata-mcp"],
"env": { "HFMD_API_KEY": "hfmd_live_…" }
}
}
}One-click (best effort): cursor://anysphere.cursor-deeplink/mcp/install?name=hfmarketdata&config=<base64 of the JSON object above> — generated for you on https://www.hfmarketdata.io/integrations/cursor.
Codex — ~/.codex/config.toml
[mcp_servers.hfmarketdata]
command = "npx"
args = ["-y", "hfmarketdata-mcp"]
env = { HFMD_API_KEY = "hfmd_live_…" } Claude Desktop — claude_desktop_config.json
{ "mcpServers": { "hfmarketdata": { "command": "npx", "args": ["-y", "hfmarketdata-mcp"], "env": { "HFMD_API_KEY": "hfmd_live_…" } } } }Keyless? Simply drop the env block.
Example session
> Compare ESZ25 and ESH26 over 30 days
⏺ get_coverage(kind="futures_contract", symbol="ESZ25") … get_bars(asset="futures_contract", symbol="ESZ25", start="2025-08-05") …
⏺ Over the last 30 sessions ESH26 traded at a 61.5 → 58.0 point premium to ESZ25 (calendar spread narrowing 3.5 pts as
financing expectations eased). Volume is still 96 % in ESZ25; the roll window usually opens ~8 sessions before the
December expiry (2025-12-19). One session (2025-09-01, Labor Day) is missing in both legs — not interpolated.Development
npm install
npm test # vitest — 45 tests, every tool mocked (fetch + websocket)
npm run build # tsc → dist/
node dist/index.js --selftest
node scripts/live-smoke.mjs # real stdio round-trip against www.hfmarketdata.io
npm run inspect # MCP Inspector UIRelease
npm version 1.0.x
npm run build && npm test
npm pack # → hfmarketdata-mcp-1.0.x.tgz (this is what /downloads serves)
npm publish --dry-run # inspect the file list
npm publish --access public # requires the maintainer's npm login (npm login / NPM_TOKEN)Until the package is on the npm registry, npx -y hfmarketdata-mcp resolves nothing: use the tarball URL above or npm i -g ./hfmarketdata-mcp-1.0.0.tgz.
Project layout
src/index.ts bin — stdio transport, --help/--version/--selftest
src/server.ts createServer(): 14 tools · 2 resources · 3 prompts
src/tools.ts tool catalogue (zod schemas + handlers), runTool()
src/client.ts fetch wrapper: auth, format=json, rate headers, uniform HfmdError
src/format.ts compact tables, head/tail summaries
src/stream.ts bounded websocket consumer
src/resources.ts hfmarketdata://status · hfmarketdata://limits
src/prompts.ts term-structure-analysis · compare-contracts · fundamentals-snapshot
test/ vitest (mocked fetch + fake WebSocket + in-memory MCP transport)
scripts/ live-smoke.mjs · record-demo.mdLicence
MIT © Simon-Pierre Boucher — data by FirstRate Data and SEC EDGAR, served by HF Market Data. Questions: contact@spboucher.ai.