Metadata-Version: 2.5
Name: candlefeed-mcp
Version: 0.1.1
Summary: MCP server for CandleFeed: download Binance USD-M order book days, rebuild the book, and query spreads and depth, plus candles, funding, open interest and liquidations.
Project-URL: Homepage, https://candlefeed.ai
Project-URL: Documentation, https://candlefeed.ai/docs/order-book
Author-email: CandleFeed <support@candlefeed.ai>
License: MIT
Keywords: claude,crypto,l2,market-data,mcp,model-context-protocol,order-book
Requires-Python: >=3.10
Requires-Dist: candlefeed[l2]<0.4,>=0.3.1
Requires-Dist: mcp<3,>=2.3
Requires-Dist: requests>=2.25
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: responses>=0.23; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# candlefeed-mcp

An MCP server that gives Claude Code, Claude Desktop, Cursor or any MCP client the CandleFeed order book files as tools: find a published day, download it with SHA-256 checks, rebuild the Binance USD-M book, and ask for the book, spread or depth at any moment. It also wraps four REST datasets (candles, funding, open interest, liquidations). It runs locally over stdio, and order book files are rebuilt on your machine, not on ours.

The data is historical. Order book days are published the morning after each UTC day ends, and every known gap is listed in the public gap log.

## Tools

| Tool | What it does | Key needed |
|---|---|---|
| `l2_coverage` | Published book and trade days per symbol, with unpublished days and the reason | no |
| `l2_gaps` | The public gap log: every stretch no capture node recorded, with times, size and reason | no |
| `l2_download_day` | Downloads one UTC day (`book` or `trades`) into the cache, size and SHA-256 checked; cached files are skipped | yes |
| `l2_book_at` | Top N bids and asks at a moment, best bid/ask, mid, spread in bps, and the snapshot it was anchored on | no (local) |
| `l2_spread_summary` | Spread statistics for a day or window, sampled every 100ms to 5min, plus the biggest 1-minute mid move | no (local) |
| `l2_depth_summary` | Resting size within each bps band of the mid at a moment, in the base asset and USDT, optionally averaged over a window | no (local) |
| `candles` | OHLCV, intervals 1m to 1d | yes |
| `funding_rates` | Funding settlements per exchange | yes |
| `open_interest` | Open interest in contracts and USD | yes (Builder) |
| `liquidations` | Bucketed or tick liquidations | yes (Builder) |

Depth outside the anchor snapshot's known price window is partial. Report completeness per band and exclude incomplete samples from full-depth statistics. `l2_depth_summary` flags each band and averages complete samples only.

The SHA-256 checks prove the files are consistent with what CandleFeed published for that day. They aren't a signature: hashes delivered by the same service as the files can't detect that service itself being compromised or malicious.

The rebuild follows the published rule (the same code as `candlefeed.l2book`): anchor on a snapshot that isn't `in_gap`, apply the event that contains its `lastUpdateId` whole, and report nothing between a chain break and the next snapshot. A moment with no trustworthy book comes back as an error that says why.

## What it costs to use

The 1st of every month is a free sample day on every plan, Free included, for every order book symbol. Sample downloads count against 10 GiB of new files per account per month; downloading the same file again that month is free. Every other day needs the Pro plan ($149/mo). A BTCUSDT book day is about 0.35 GB, so ask for days on purpose. REST tools follow the usual plan limits: Free gets BTC, ETH, SOL, XRP and DOGE on Binance for the last 30 days. Plans: https://candlefeed.ai/pricing?utm_source=mcp&utm_medium=readme

CandleFeed data, including samples, is licensed for internal use under Terms §5.3. Published charts, statistics, and research must not include Raw Data or Substantially Raw Derivatives. The raw files and row-level data aren't to be shared.

## Install

Needs Python 3.10+ (CPython) on Linux or macOS. Downloads rely on directory-relative, no-follow file operations to stay inside the cache, and refuse to run where those don't exist (Windows).

```bash
pip install candlefeed-mcp      # pulls in candlefeed[l2]>=0.3.1,<0.4
which candlefeed-mcp            # use this absolute path below if your client can't find the command
```

Get a free key at https://candlefeed.ai/signup?utm_source=mcp&utm_medium=readme&utm_campaign=mcp (Free plan, no card, about a minute). The server reads it from `CANDLEFEED_API_KEY` in its own environment and never takes it as a tool argument. Tool results and errors, including the SDK's argument errors and any text the API echoes back, are scrubbed of that exact key and of download-link signatures before they're returned.

## Claude Code

```bash
claude mcp add candlefeed --env CANDLEFEED_API_KEY=cf_live_... -- candlefeed-mcp
```

Or in `.mcp.json` at the project root:

```json
{
  "mcpServers": {
    "candlefeed": {
      "command": "candlefeed-mcp",
      "env": { "CANDLEFEED_API_KEY": "cf_live_..." }
    }
  }
}
```

A full BTC day download can take a minute or more. If a call times out, raise `MCP_TOOL_TIMEOUT` (milliseconds) before starting `claude`, and run the download again: files that finished are kept and skipped.

## Claude Desktop

Settings, Developer, Edit Config, then add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "candlefeed": {
      "command": "/absolute/path/to/candlefeed-mcp",
      "env": { "CANDLEFEED_API_KEY": "cf_live_..." }
    }
  }
}
```

Restart Claude Desktop. It doesn't inherit your shell's PATH, so use the absolute path from `which candlefeed-mcp`.

## Cursor

`~/.cursor/mcp.json` (every project) or `.cursor/mcp.json` (one project):

```json
{
  "mcpServers": {
    "candlefeed": {
      "command": "candlefeed-mcp",
      "env": { "CANDLEFEED_API_KEY": "cf_live_..." }
    }
  }
}
```

## Settings

| Variable | Default | Meaning |
|---|---|---|
| `CANDLEFEED_API_KEY` | none | Your key. Needed for downloads and REST tools |
| `CANDLEFEED_CACHE_DIR` | `~/.cache/candlefeed-mcp` | Where files go. On Linux and macOS (CPython), downloads write only inside it: each folder is opened without following symlinks and every write is relative to that folder's handle, temporary files are created exclusively, and file names and dates must be the ones requested. On a Python without those operations (Windows) downloads refuse to run. Before rebuilding a day the tools check that its folder contains no symlinks; that's a check at load time, not a guarantee against another process changing the cache while the server runs |
| `CANDLEFEED_BASE_URL` | `https://candlefeed.ai/api/v1` | API base; must be https |
| `CANDLEFEED_L2_STORAGE_HOST` | `candlefeed-l2-canonical.sgp1.digitaloceanspaces.com` | The only host files are downloaded from (https, port 443) |
| `CANDLEFEED_MCP_MAX_DAY_BYTES` | 2 GiB | Most new bytes one `l2_download_day` call will fetch |
| `CANDLEFEED_CACHE_MAX_BYTES` | 20 GiB | Cache quota; a download that wouldn't fit is refused before it starts |
| `CANDLEFEED_MCP_DOWNLOAD_DEADLINE` | 1800 | Seconds for one download. Checked before every request, after every read and before a file is committed; a file that isn't complete and verified in time is never kept. It's checked between reads, so a server that stalls inside one read (slow headers, say) is bounded by the socket read timeout, not a hard wall clock. Only that tool call waits on a download; other tools keep running |
| `CANDLEFEED_MCP_ROW_CACHE_BYTES` | 2 GiB | Decoded diff rows kept in memory for the loaded day. It limits that cache only, not the server's total memory; the event index, snapshots and read buffers come on top |

Downloaded days sit under `<cache>/book/binance/<SYMBOL>/<YYYY-MM-DD>/`, the same layout `CandleFeed().download_l2` writes, so you can open them with `candlefeed.l2book.L2Book.load(<cache>, "BTCUSDT", "2026-09-01")` in your own code.

## Try it

> Download the free BTCUSDT order book sample for 2026-09-01, find the biggest 1-minute move of the day, and show me the spread and the depth within 10 and 50 bps for ten minutes either side of it.

The agent calls `l2_download_day`, `l2_spread_summary` (which reports the biggest 1-minute move) and `l2_depth_summary` with `window_minutes=10`. `examples/l2_agent_demo.py` in the repo does the same in plain Python, with a chart.

## Development

```bash
pip install -e "./clients/python[l2,dev]" -e "./integrations/mcp[dev]"
pytest integrations/mcp/tests
```

Tests mock every HTTP call and build synthetic order book days with a known true book.
