> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whitebit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

> Connect Claude Code, Cursor, Claude Desktop, Codex, or OpenClaw to 115 WhiteBIT trading tools using the Model Context Protocol.

export const RegionBaseUrl = ({className = "", showBaseUrl = true, protocol = "https"}) => {
  const {useState, useEffect, useRef} = React;
  const EU_ENABLED = false;
  const [region, setRegionState] = useState(() => {
    if (!EU_ENABLED) return "com";
    if (typeof window !== 'undefined') {
      return localStorage.getItem("api-region-preference") || "com";
    }
    return "com";
  });
  const [mounted, setMounted] = useState(false);
  const observerRef = useRef(null);
  const isSyncingRef = useRef(false);
  const updateAllContentOnPage = targetRegion => {
    try {
      const domainFrom = targetRegion === "eu" ? "whitebit.com" : "whitebit.eu";
      const domainTo = targetRegion === "eu" ? "whitebit.eu" : "whitebit.com";
      const links = document.querySelectorAll('a');
      links.forEach(link => {
        let href = link.getAttribute('href');
        if (href && href.includes(domainFrom)) {
          link.setAttribute('href', href.replace(domainFrom, domainTo));
        }
      });
      const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
        acceptNode: node => {
          if (node.parentElement?.closest('.region-toggle-component')) {
            return NodeFilter.FILTER_REJECT;
          }
          return node.textContent.includes(domainFrom) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
        }
      });
      let currentNode;
      while (currentNode = walker.nextNode()) {
        currentNode.textContent = currentNode.textContent.replace(new RegExp(domainFrom, 'g'), domainTo);
      }
      console.log(`[RegionSync] Global content updated to ${domainTo}`);
    } catch (e) {
      console.error("[RegionSync] Error updating content:", e);
    }
  };
  const updateRegion = (newRegion, source) => {
    if (region === newRegion) return;
    console.log(`[RegionBaseUrl] Updating to "${newRegion}" (Source: ${source})`);
    if (source === 'observer') {
      isSyncingRef.current = true;
      setTimeout(() => isSyncingRef.current = false, 1000);
    }
    setRegionState(newRegion);
    localStorage.setItem("api-region-preference", newRegion);
    updateAllContentOnPage(newRegion);
    if (source === 'user-click') {
      window.dispatchEvent(new CustomEvent("regionChange", {
        detail: newRegion
      }));
      attemptToUpdateNativeDropdown(newRegion, 0);
      setTimeout(() => attemptToUpdateNativeDropdown(newRegion, 1), 500);
      setTimeout(() => attemptToUpdateNativeDropdown(newRegion, 2), 1500);
    }
  };
  const attemptToUpdateNativeDropdown = (targetRegion, attempt) => {
    if (isSyncingRef.current) return;
    try {
      const targetUrl = targetRegion === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
      const targetDesc = targetRegion === "eu" ? "EU Server" : "Production Server";
      const selects = document.querySelectorAll('select');
      for (const select of selects) {
        if (select.innerHTML.includes('whitebit.com') || select.innerHTML.includes('whitebit.eu')) {
          select.value = targetUrl;
          select.dispatchEvent(new Event('change', {
            bubbles: true
          }));
          return;
        }
      }
      const buttons = Array.from(document.querySelectorAll('button, [role="combobox"]'));
      const serverSelector = buttons.find(btn => {
        if (btn.closest('a') || btn.closest('[class*="card"]') || btn.closest('nav')) {
          return false;
        }
        const txt = btn.textContent || "";
        const isServerDropdown = (txt.includes('Production Server') || txt.includes('EU Server') || txt.includes('WhiteBIT Global Server') || txt.includes('WhiteBIT EU Server')) && !txt.includes('Run') && !txt.includes('Send') || btn.getAttribute('role') === 'combobox';
        return isServerDropdown;
      });
      if (serverSelector) {
        const currentText = serverSelector.textContent || "";
        if (currentText.includes(targetDesc)) return;
        serverSelector.click();
        setTimeout(() => {
          const options = document.querySelectorAll('[role="option"], li, button');
          for (const opt of options) {
            const optText = opt.textContent || "";
            if (optText.includes(targetDesc) || optText.includes(targetUrl)) {
              opt.click();
              return;
            }
          }
        }, 100);
      }
    } catch (e) {
      console.error("[Sync] Error:", e);
    }
  };
  useEffect(() => {
    if (!EU_ENABLED) return;
    setMounted(true);
    updateAllContentOnPage(region);
    const handleStorageChange = e => {
      if (e.key === "api-region-preference" && e.newValue) {
        updateRegion(e.newValue, 'storage');
      }
    };
    const handleRegionChange = e => {
      if (e.detail !== region) {
        updateRegion(e.detail, 'event');
      }
    };
    window.addEventListener("storage", handleStorageChange);
    window.addEventListener("regionChange", handleRegionChange);
    observerRef.current = new MutationObserver(mutations => {
      if (isSyncingRef.current) return;
      updateAllContentOnPage(region);
      for (const mutation of mutations) {
        if (mutation.type !== 'childList' && mutation.type !== 'characterData') continue;
        const target = mutation.target;
        const el = target.nodeType === Node.TEXT_NODE ? target.parentElement : target;
        if (el && (el.getAttribute('role') === 'option' || el.closest('[role="listbox"]'))) continue;
        const text = target.textContent || "";
        if (text.includes('WhiteBIT EU Server') || text.includes('https://whitebit.eu') && text.includes('Server')) {
          if (el && el.tagName !== 'A' && !el.closest('.region-toggle-component')) {
            if (region !== 'eu') updateRegion('eu', 'observer');
          }
        } else if (text.includes('WhiteBIT Global Server') || text.includes('https://whitebit.com') && text.includes('Server')) {
          if (el && el.tagName !== 'A' && !el.closest('.region-toggle-component')) {
            if (region !== 'com') updateRegion('com', 'observer');
          }
        }
      }
    });
    observerRef.current.observe(document.body, {
      childList: true,
      subtree: true,
      characterData: true
    });
    if (typeof window !== 'undefined') {
      const current = localStorage.getItem("api-region-preference");
      if (current) attemptToUpdateNativeDropdown(current, 'init');
    }
    return () => {
      window.removeEventListener("storage", handleStorageChange);
      window.removeEventListener("regionChange", handleRegionChange);
      if (observerRef.current) observerRef.current.disconnect();
    };
  }, [region]);
  const apiBaseUrl = protocol === "wss" ? region === "eu" ? "wss://api.whitebit.eu/ws" : "wss://wss.whitebit.com/ws" : region === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
  const baseUrlLabel = protocol === "wss" ? "WebSocket URL" : "Base URL";
  if (!EU_ENABLED) {
    if (!showBaseUrl) return null;
    return <div className={`flex items-center gap-2 flex-wrap my-4 region-toggle-component ${className}`}>
                <span className="text-sm text-gray-500 dark:text-gray-400 font-mono">
                    {baseUrlLabel}
                </span>
                <span className="text-sm text-gray-400">:</span>
                {protocol === "wss" ? <code className="text-sm font-mono text-primary dark:text-primary-light">
                        {apiBaseUrl}
                    </code> : <a href={apiBaseUrl} target="_blank" rel="noopener noreferrer" className="text-sm font-mono text-primary dark:text-primary-light hover:underline">
                        {apiBaseUrl}
                    </a>}
            </div>;
  }
  if (!mounted) return null;
  return <div className={`flex items-center gap-2 flex-wrap my-4 region-toggle-component ${className}`}>
            <span className="text-sm text-gray-500 dark:text-gray-400 font-mono">
                {baseUrlLabel}
            </span>
            <span className="text-sm text-gray-400">(</span>
            <div className="inline-flex bg-gray-100 dark:bg-gray-800 rounded-lg p-0.5 border border-gray-200 dark:border-gray-700">
                <button onClick={() => updateRegion("com", "user-click")} className={`px-2 py-0.5 text-xs font-medium rounded-md transition-all ${region === "com" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`}>
                    .com
                </button>
                <button onClick={() => updateRegion("eu", "user-click")} className={`px-2 py-0.5 text-xs font-medium rounded-md transition-all ${region === "eu" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`}>
                    .eu
                </button>
            </div>
            <span className="text-sm text-gray-400">)</span>
            {showBaseUrl && <>
                    <span className="text-sm text-gray-400">:</span>
                    {protocol === "wss" ? <code className="text-sm font-mono text-primary dark:text-primary-light">
                            {apiBaseUrl}
                        </code> : <a href={apiBaseUrl} target="_blank" rel="noopener noreferrer" className="text-sm font-mono text-primary dark:text-primary-light hover:underline">
                            {apiBaseUrl}
                        </a>}
                </>}
        </div>;
};

<RegionBaseUrl />

The WhiteBIT MCP server exposes the full WhiteBIT API as AI tools over the Model Context Protocol. Connect a supported AI client and interact with markets, trading, balances, wallets, and sub-accounts using natural language.

* **115 tools** across market data, spot trading, collateral trading, wallet, lending, sub-accounts, mining pool, and currency conversion
* Runs locally — API keys never leave the machine
* Credentials are passed per tool call and are not stored or logged by the server

<Note>
  The MCP server documented on this page enables **trading** via AI clients. For documentation search via MCP (searching the WhiteBIT API docs in Cursor or VS Code), see [Docs search via MCP](/guides/ai-ide-setup).
</Note>

## Prerequisites

* A WhiteBIT account with an API key (for trading tools) — see [API key generation](/api-reference/authentication)
* A supported AI client: Claude Code, Cursor, Claude Desktop, Codex, or OpenClaw
* [uv](https://docs.astral.sh/uv/getting-started/installation/) **or** [Docker](https://docs.docker.com/get-docker/)

## Quick start

<Tabs>
  <Tab title="Auto-setup (recommended)">
    One command configures the MCP server for every supported AI client at once. No Docker, no manual config editing.

    <Steps>
      <Step title="Install uv">
        `uv` is a Python package manager that provides the `uvx` command used to run the server.

        <Tabs>
          <Tab title="macOS / Linux">
            ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
            curl -LsSf https://astral.sh/uv/install.sh | sh
            source $HOME/.local/bin/env
            ```
          </Tab>

          <Tab title="Windows">
            ```powershell theme={"theme":{"light":"github-light","dark":"github-dark"}}
            powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
            ```
          </Tab>
        </Tabs>
      </Step>

      <Step title="Run the setup command">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        uvx --from whitebit-mcp whitebit-mcp-setup --public=YOUR_API_KEY
        ```

        <Warning>
          Keep `--from whitebit-mcp`. `whitebit-mcp-setup` is a command inside the `whitebit-mcp` package, not a package of its own — running `uvx whitebit-mcp-setup` downloads and executes an unrelated third-party package that happens to hold that name on PyPI, and passes the API credentials to it.
        </Warning>

        Omitting `--secret` makes the script prompt for the secret key without echoing it, which keeps it out of the shell history. To run non-interactively, add `--secret=YOUR_SECRET_KEY`.

        The script writes the server configuration and credentials into the config of each AI tool it finds:

        | AI tool | Config file |
        | - | - |
        | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) / `%APPDATA%\Claude\…` (Windows) |
        | Claude Code | `~/.claude/mcp.json` |
        | Cursor | `~/.cursor/mcp.json` |
        | OpenAI Codex | `~/.codex/config.toml` |
        | OpenClaw | `~/.openclaw/openclaw.json` |

        Existing configs are updated in place — other configured MCP servers are not affected.
      </Step>

      <Step title="Start the MCP server">
        The MCP server is started automatically by the AI client — there is no separate server process to run manually. Under the hood, the setup command registered the following as the server entry in each config:

        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        uvx whitebit-mcp
        ```

        The AI client executes this command on startup and keeps the server running in the background as long as the client is open.
      </Step>

      <Step title="Restart the AI client">
        Restart the AI client so it picks up the new config and launches the server.

        * **Claude Desktop** — quit and reopen the app
        * **Claude Code** — run `claude mcp list` to reload, or start a new session
        * **Cursor** — open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`) and run **MCP: Reload Servers**
        * **Codex** — start a new Codex session
        * **OpenClaw** — restart the agent
      </Step>

      <Step title="Verify the connection">
        Ask the AI client something like:

        ```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
        What is the current BTC_USDT price?
        ```

        If the WhiteBIT MCP server is connected, the AI client calls the tool and returns live data.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Docker">
    <Steps>
      <Step title="Clone the repository">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        git clone https://github.com/whitebit-exchange/whitebit-mcp
        cd whitebit-mcp
        ```
      </Step>

      <Step title="Start the server">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        docker compose up -d
        ```

        The MCP endpoint is available at `http://localhost:8080/mcp`.
      </Step>

      <Step title="Verify the server is running">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        docker compose ps
        ```

        The `whitebit-mcp` container status shows `Up`.
      </Step>
    </Steps>

    Stop the server at any time:

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    docker compose down
    ```

    Then follow the [Connect an AI client](#connect-an-ai-client) section below to register the server in the AI tool.
  </Tab>

  <Tab title="Without Docker">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    pip install mcp whitebit-python-sdk
    mcp run server.py
    ```

    The server listens on `http://0.0.0.0:8000`. Use port `8000` in place of `8080` in all client configurations below.

    Then follow the [Connect an AI client](#connect-an-ai-client) section below to register the server in the AI tool.
  </Tab>
</Tabs>

## Connect an AI client

Skip this section if the auto-setup tab above was used — the AI clients are already configured.

<Tabs>
  <Tab title="Claude Code">
    **Global registration (all projects):**

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    claude mcp add --transport http --scope user whitebit http://localhost:8080/mcp
    ```

    Verify the connection:

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    claude mcp list
    # whitebit: http://localhost:8080/mcp (http)
    ```

    **Project-scoped registration:**

    The repository includes a `.mcp.json` file at the project root. Running `claude` from inside the cloned `whitebit-mcp` directory registers the server automatically at project scope.

    Use `/mcp` inside a Claude Code session to see live server status and available tools.
  </Tab>

  <Tab title="Cursor">
    Create or edit `~/.cursor/mcp.json`:

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "mcpServers": {
        "whitebit": {
          "url": "http://localhost:8080/mcp"
        }
      }
    }
    ```

    Open the Command Palette (`Cmd+Shift+P` on macOS, `Ctrl+Shift+P` on Windows/Linux) and run **MCP: Reload Servers**.
  </Tab>

  <Tab title="Claude Desktop">
    Edit the Claude Desktop configuration file:

    * **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
    * **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "mcpServers": {
        "whitebit": {
          "type": "http",
          "url": "http://localhost:8080/mcp"
        }
      }
    }
    ```

    Restart Claude Desktop.
  </Tab>

  <Tab title="Codex">
    Add to `~/.codex/config.toml`:

    ```toml theme={"theme":{"light":"github-light","dark":"github-dark"}}
    [mcp_servers.whitebit]
    url = "http://localhost:8080/mcp"
    ```

    Start a Codex session — the server connects automatically.
  </Tab>

  <Tab title="OpenClaw">
    Add to the OpenClaw agent configuration:

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "mcp_servers": {
        "whitebit": {
          "url": "http://localhost:8080/mcp"
        }
      }
    }
    ```
  </Tab>
</Tabs>

## API keys

Generate API keys at [whitebit.com](https://whitebit.com) → **Profile** → **API Keys**. Use the minimum permissions required for the intended use case — read-only keys are sufficient for balance and order queries.

<Warning>
  Never paste API keys into the AI conversation. Credentials are supplied at connection time — the setup command writes the keys into the AI client configuration, and the client passes the credentials to the server on every connection. The server uses keys only to sign outgoing WhiteBIT API requests — keys are not stored, logged, or cached.
</Warning>

**Credential handling:** credentials are read from the transport, never from tool parameters.

| Transport | Where credentials come from |
| - | - |
| stdio / `uvx` | `WHITEBIT_API_KEY` and `WHITEBIT_SECRET_KEY` environment variables |
| HTTP / Docker | `X-WB-Api-Key` and `X-WB-Secret-Key` request headers |

**Public market data tools** (market info, tickers, order book) query public endpoints and need no credentials. On the HTTP transport the `/mcp` endpoint still answers `401` to a connection that carries no credentials at all, so the client must be configured with keys even for public data.

## Example prompts

### Market data — no API key required

These tools query public endpoints and return data without credentials.

* "What is the current BTC\_USDT price?"
* "Show the order book for ETH\_USDT"
* "List all available markets"
* "What are the deposit fees for USDT?"
* "What is the funding rate history for BTC\_USDT?"
* "Is WhiteBIT in maintenance mode?"

### Spot trading — API key required

* "Show the spot balance"
* "List open orders on BTC\_USDT"
* "Place a limit buy order for 0.001 BTC at 90000 on BTC\_USDT"
* "Cancel all open orders on ETH\_USDT"
* "Show order history for the last 30 days"

### Wallet — API key required

* "What is the deposit address for USDT on TRC20?"
* "Show recent deposits and withdrawals"
* "Transfer 100 USDT from the main account to spot trading"
* "Create a WhiteBIT code for 50 USDT"

### Collateral trading — API key required

* "List open collateral positions"
* "Place a collateral limit buy for 0.01 BTC on BTC\_USDT"
* "What is the current leverage?"
* "Close the BTC\_USDT position"

### Sub-accounts — API key required

* "List all sub-accounts"
* "Show the balance of sub-account ID 123"
* "Transfer 200 USDT to sub-account ID 456"

## Configuration reference

| Variable | Default | Description |
| - | - | - |
| `WHITEBIT_BASE_URL` | `https://whitebit.com` | API base URL — override for custom deployments |

Set in `docker-compose.yml` or as a shell variable:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
WHITEBIT_BASE_URL=https://whitebit.com docker compose up
```

## Safe account practices

Before using the MCP server with a live account:

* Create a dedicated sub-account with a separate API key for AI tool access.
* Fund the sub-account with only the amount allocated for AI-assisted trading.
* Generate API keys with the minimum required permissions — use read-only keys for monitoring.
* Use IP whitelisting on the API key to restrict access to the machine running the server.
* Test with a small amount before executing larger trades.

## What's next

* [CLI](/guides/cli) — Trade and query account data directly from the terminal without an AI client
* [AI tools FAQ](/guides/ai-faq) — Answers to common questions about API key safety, supported clients, and account practices
* [AI tools overview](/guides/use-with-ai) — All WhiteBIT AI tools in one place
* [Authentication](/api-reference/authentication) — How to generate and manage WhiteBIT API keys
* [API Reference Overview](/api-reference/overview) — Full REST API endpoint listing


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.