Skip to main content
Glama
iwaqaruddin

binance-mcp

by iwaqaruddin

binance-mcp

CI License: MIT

A Model Context Protocol server that gives an LLM read-only access to Binance spot market data, for people who want a model to reason about real prices without giving it the ability to place an order.

Why this exists

A model can already be told to fetch api.binance.com/api/v3/klines itself. That works until it doesn't, and the failures are quiet ones.

Symbols have to be guessed. Binance has 3,680 spot pairs. The name is a concatenation with no separator, so a model writes BTC-USDT or btcusdt and gets back HTTP 400 {"code": -1100, "msg": "Illegal characters found in parameter 'symbol'"}. That is a lowercase rejection, not a missing pair, and the model cannot tell the difference. It retries with another guess. This server uppercases and strips separators before the call, and it exposes a search tool so a guess can be resolved instead of repeated.

Out-of-range limits are honoured silently. Ask /klines for 5000 candles and Binance returns HTTP 200 with 1000 rows. Nothing in the response says it was truncated. A model that asked for a year of hourly candles gets six weeks and reasons about it as if it were a year. This server clamps limits itself and states the cap in the tool schema, so the model knows what it asked for.

Rate limits are weighted, and the penalty is a ban. Binance budgets 6000 request weight per minute per IP, not 6000 requests. /exchangeInfo costs 20. /depth costs 5 for 100 levels and 25 for 101. Asking for 5000 levels costs 250, so twenty-four of those calls exhaust the minute. Cross the line and you get HTTP 429; ignore the 429 and you get HTTP 418 and a temporary IP ban. A model polling in a loop has no idea any of this is happening. This server reads the X-MBX-USED-WEIGHT-1M header on every response and stops before the budget is gone.

Every failure looks the same. A misspelled symbol, an exhausted rate limit and a Binance outage all reach the model as "the tool failed". They call for three different responses: fix the symbol, wait, and give up. This server returns a typed error with a code and a retriable flag.

The payloads are shaped for machines that already know the schema. Numbers arrive as strings, keys are camelCase, and the 24 hour ticker carries 21 fields. The full /exchangeInfo document is 16.7 MB, which is not something to put near a context window. This server renames, coerces and drops, and reduces the exchange listing to the four fields search needs.

Related MCP server: Bybit MCP Server

Quickstart

Python 3.11 or newer. No API key, because nothing here needs one.

git clone https://github.com/iwaqaruddin/binance-mcp.git
cd binance-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Confirm the checkout is sound:

ruff check . && mypy && pytest -q

The tests mock every HTTP call, so that command works offline.

Start the server by hand to see it boot. It speaks MCP over stdio, so it will look like it is hanging with no prompt. That is correct; press Ctrl-C to stop it.

binance-mcp

Then point a client at it. For Claude Desktop, add this to claude_desktop_config.json and restart the app. Use the absolute path to the executable inside the virtualenv; the config is not run through a shell, so ~ and $PATH lookups do not apply.

{
  "mcpServers": {
    "binance": {
      "command": "/absolute/path/to/binance-mcp/.venv/bin/binance-mcp",
      "env": {
        "BINANCE_MCP_MARKET_TTL_SECONDS": "2",
        "BINANCE_MCP_LOG_LEVEL": "INFO"
      }
    }
  }
}

Configuration comes from the process environment. .env.example documents every variable and its default; the server does not read a .env file itself, so set values in the env block above or export them before launching. All of them have working defaults, so the env block can be omitted entirely.

The tools

Tool

Arguments

Returns

get_ticker

symbol

Last price and the 24 hour window: open, high, low, price change and percent, weighted average, best bid and ask with sizes, volume in both assets, trade count, window start and end.

get_klines

symbol, interval (enum, default 1h), limit (default 100, capped at 500)

OHLCV candles, oldest first, as positional rows plus a columns list naming the nine fields.

get_order_book

symbol, depth (default 20, capped at 100)

A book snapshot: last_update_id, and bids and asks as price and quantity pairs.

get_symbol_info

symbol

Trading status, base and quote assets, precisions, permitted order types, and the tick size, lot step size and minimum notional as exact strings.

search_symbols

query, limit (default 10, capped at 25)

Ranked symbol matches with base asset, quote asset, status and a score, plus the number of symbols searched.

symbol is case-insensitive and separators are stripped, so btc/usdt and BTCUSDT are the same request. interval is one of 1s, 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M.

Every failed call returns the same shape instead of a message:

{
  "error": "rate_limited",
  "message": "Local request-weight budget is exhausted for this minute.",
  "retriable": true,
  "retry_after_seconds": 41.2,
  "used_weight": 4802,
  "weight_limit": 6000,
  "remaining_weight": 1198
}

error is one of invalid_symbol, invalid_argument, rate_limited, upstream_unavailable or internal_error.

Design decisions

Read-only by design

There is no order placement, no account data and no signing code. The rejected alternative was one server with a BINANCE_API_KEY and a --enable-trading flag, which is how most exchange integrations are built.

The trade-off is that this repo cannot grow into a trading agent. That is the point. A server that can trade has to be trusted with prompt injection as a threat model: any text the model reads becomes a possible instruction to sell. A server with no key and no write path fails that attack by construction, and the security argument is a one-line audit rather than a policy document. Anyone who wants execution should build a separate server with its own approval path, and should have to think about it separately.

A TTL cache, not no cache and not Redis

Responses are held in a fixed-size in-process dictionary: two seconds for market data, sixty for exchange metadata.

No cache at all was the first alternative. It loses because models re-ask. A model comparing four pairs will often fetch the same ticker twice in one turn, and each repeat spends weight on data that has not changed. The second alternative was Redis, which buys a cache shared across processes and survives restarts. It loses on operational cost: it turns a pip install into a service dependency, and a market-data cache with a two-second lifetime has almost nothing to share. The trade-off accepted is that every process keeps its own copy and a restart starts cold. At this TTL, a cold start costs one request.

The market TTL is 2s rather than something longer because that is roughly the window in which a stale price is still the same price for reasoning purposes. It is configurable, and raising it is the correct move if several clients poll one symbol.

Reading the weight header, not retrying on failure

Every response carries the weight spent in the current minute. The server tracks that number, and once the configured share of the budget is spent it waits for the window to roll over. If the wait is longer than MAX_BACKOFF_SECONDS it fails the call instead and reports how much budget is left.

The alternative was retry with exponential backoff on 429, which is the default answer for most HTTP clients. It is the wrong answer here. Binance escalates repeated 429s from one IP into an HTTP 418 ban, so a retry loop converts a recoverable throttle into an outage that outlives the process. This server never retries a 429. It treats one as evidence the budget is gone and blocks the next call locally.

Two trade-offs come with this. A tool call can block for up to five seconds before returning, which is visible latency inside a client's timeout. And the local estimate can drift, because other processes on the same IP spend from the same budget without this server seeing it. The header corrects the count on every response, so drift lasts one request. Setting BINANCE_MCP_WEIGHT_LIMIT below 6000 is the fix when the IP is genuinely shared.

No indicator calculation

No moving averages, no RSI, no spread, not even a mid price from the order book this server just fetched.

The alternative is a get_rsi tool, and it is the single most requested feature of servers like this. It loses on two counts. Every indicator has parameters and conventions — Wilder smoothing or simple, close or typical price, how to seed the first value — and a tool that picks silently produces numbers the caller cannot reconcile with their charting platform. Worse, it moves the analysis inside an opaque function: the model reports RSI 71 and neither it nor the user can see which candles produced it. Serving the candles keeps the arithmetic in the transcript where it can be checked.

The trade-off is real and it is token cost. A model computing a 200-period moving average has to read 200 candles to do it. That is the price of an auditable number, and it is why the next decision exists.

Candles as rows, not objects

get_klines returns [[1700000000000, 67000.1, ...], ...] with a columns list, rather than a list of objects with nine named keys each.

The alternative is self-describing objects, which are easier to read in a log and need no lookup. It loses on arithmetic: 500 candles as objects spends roughly a third of its tokens repeating the same nine keys, and context is the scarce resource in a tool that exists to feed a model. The trade-off is that a row is meaningless without its header, so the header ships in the same response and the output schema pins the type of every position.

This is the one place where the wire format is optimised for the consumer rather than for readability, and it is contained to a single tool.

Floats for prices, strings for tick sizes

Prices, volumes and percentages are converted to JSON numbers. Tick size, lot step size and minimum notional stay strings, exactly as Binance published them.

Converting everything to numbers was the alternative, and it is more consistent. It loses where exactness is load-bearing. A tick size of 0.01000000 describes a decimal grid, and a value that survives a float round-trip a few times stops landing on that grid; an order priced off a drifted tick size is rejected by the exchange or, worse, quietly rounded. Leaving everything as strings was the other alternative, and it loses because it makes the model parse before it can compare, which it does inconsistently.

The trade-off accepted: a quote volume above roughly 10^15 loses its last digits to float representation. No spot pair is close, and the failure is a rounding error in a number nobody trades on, rather than a rejected order.

When you should not use this

You are polling faster than once every couple of seconds. The cache returns identical data within its TTL, so a tighter loop gets the same answer while still paying MCP round-trip cost. If you need every tick, you need the websocket streams and this server does not have them.

You need sub-second freshness. A cached ticker can be two seconds old, an order book snapshot is stale the moment it is serialised, and the request itself takes tens of milliseconds. Do not build anything that acts on a price this returns. Market making, arbitrage, and stop placement all need a direct websocket feed and a clock you control.

You need private account data. Balances, open orders, fills, positions and funding history all require a signed request. There is no code path here that can produce one.

You want the model to execute. No tool here places, modifies or cancels an order, and adding one is out of scope rather than not yet done.

You need the full order book. Depth is capped at 100 levels per side, which is a slice of the book. Reconstructing a full book means the depth snapshot endpoint plus the diff stream, which is a different program.

You are running this in a multi-tenant service. The weight budget is tracked per process, and the exchange enforces it per IP. Several instances behind one egress IP will oversubscribe the budget until a 429 arrives. Lowering BINANCE_MCP_WEIGHT_LIMIT divides the budget but does not coordinate it.

Limitations

The cache does not deduplicate concurrent identical requests. Two tool calls for the same symbol arriving in the same event-loop tick both reach the network, and the second populates a cache entry the first already wrote.

There is no request coalescing or coordination across processes, for the reason in the section above.

Spot only. Futures, options and margin endpoints live on different hosts with different weight budgets and are not covered.

search_symbols matches ticker strings, not names. A query for bitcoin finds nothing; btc finds everything. Ranking prefers pairs quoted in the more liquid stablecoins, which is a judgement call about the current spot market rather than a property of the data.

Klines are the most recent N candles. There is no start or end time argument, so historical windows that do not end at the present cannot be requested.

The weight table for each endpoint is hardcoded. If Binance repriced an endpoint, the first request of a minute would be mis-estimated. The response header corrects it immediately, so the consequence is one optimistic request rather than a ban.

Roadmap

Single-flight deduplication of in-flight identical requests, which is the cheapest remaining weight saving.

A start_time and end_time pair on get_klines, so historical windows can be requested without walking backwards.

Optional --transport http so one server process can back several clients, which also makes a shared weight budget tractable.

Testnet documentation. BINANCE_MCP_BASE_URL already points at testnet.binance.vision if you set it, but nothing here verifies that the testnet symbol list behaves the same way.

Licence

MIT. See LICENSE.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    C
    quality
    D
    maintenance
    A server implementation that streams real-time Binance market data (spot and futures) via WebSockets, enabling applications to receive and process cryptocurrency market information through the Model Context Protocol.
    6
    48
    20
    MIT
  • A
    license
    B
    quality
    F
    maintenance
    A Model Context Protocol server that provides read-only access to Bybit's cryptocurrency exchange API, allowing users to query real-time cryptocurrency data using natural language.
    12
    19
    16
    MIT
  • A
    license
    -
    quality
    D
    maintenance
    A Model Context Protocol server that enables LLMs to interact with cryptocurrency exchanges through CCXT, allowing for tasks like fetching balances, market data, creating orders, and trading operations in a standardized way.
    8
    MIT

View all related MCP servers

Related MCP Connectors

  • Read-only MCP server for Robinhood Chain token discovery, research, and due diligence via GMGN.

  • A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…

  • MCP server for Gainium — manage trading bots, deals, and balances via AI assistants

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iwaqaruddin/binance-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server