Skip to main content
Glama
bistin

max-mcp

by bistin
README.md
# max-mcp

[![test](https://github.com/bistin/max-mcp-server/actions/workflows/test.yml/badge.svg)](https://github.com/bistin/max-mcp-server/actions/workflows/test.yml)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](./LICENSE)

An [MCP](https://modelcontextprotocol.io) server for the
[MAX Exchange](https://max.maicoin.com/) (MaiCoin) V3 REST API.

> **Disclaimer.** Unofficial and community-maintained. Not affiliated with,
> endorsed by, or supported by MaiCoin / MAX Exchange. Trading tools move real
> funds — use at your own risk.

It exposes MAX endpoints as MCP tools so an agent (Claude Code, Claude Desktop,
etc.) can query market data, read your account, and — when explicitly enabled —
place and cancel orders. The request signing follows the MAX V3 auth scheme:
HMAC-SHA256 over a base64 JSON payload, with the nonce echoed in the query/body
and `path` matched exactly (see [`max_mcp/client.py`](max_mcp/client.py)).

## Tools

| Group | Needs | Tools |
|-------|-------|-------|
| Public market data | — | `get_markets`, `get_currencies`, `get_server_timestamp`, `calibrate_time`, `get_ticker`, `get_tickers`, `get_order_book`, `get_klines`, `get_public_trades`, `get_index_prices`, `get_historical_index_prices`, `get_borrowing_interest_rates`, `get_borrowing_limits` |
| Account / history (read) | API key+secret | `get_user_info`, `get_accounts`, `get_order`, `get_open_orders`, `get_closed_orders`, `get_order_history`, `get_order_trades`, `get_my_trades`, `get_deposits`, `get_deposit`, `get_deposit_address`, `get_withdrawals`, `get_withdrawal`, `get_withdraw_addresses`, `get_rewards`, `get_internal_transfers`, `get_converts`, `get_convert` |
| M-Wallet (read) | API key+secret | `get_ad_ratio`, `get_loans`, `get_interests`, `get_liquidations` |
| Trading + m-wallet (**write**) | API key+secret **and** `MAX_ENABLE_TRADING` | `create_order`, `cancel_order`, `cancel_all_orders`, `create_loan`, `repay_loan`, `m_transfer` |

Write tools are **not registered at all** unless `MAX_ENABLE_TRADING` is set, so
an agent can't even see them in the default read-only configuration.

## Configuration (environment variables)

| Variable | Required for | Notes |
|----------|--------------|-------|
| `MAX_API_KEY` | private endpoints | your MAX API access key |
| `MAX_API_SECRET` | private endpoints | your MAX API secret key |
| `MAX_ENABLE_TRADING` | write tools | set to `1`/`true`/`yes` to register order/loan/transfer tools |
| `MAX_API_BASE_URL` | — | override base URL (default `https://max-api.maicoin.com`) |

Public market-data tools work with no credentials.

## Install

The package installs a `max-mcp` console script. The tricky part is **where that
binary lives**: an MCP client (Claude Code / Desktop) launches a fresh process
that does *not* inherit your shell's `PATH` or any activated virtualenv. So the
reliable pattern is to either let `uv` resolve the environment for you, or point
the client at an **absolute path** to the binary. Pick one of the options below.

### Option A — uv (recommended, no install step)

[uv](https://docs.astral.sh/uv/) can run the server straight from GitHub and
manage the environment itself, so there's no `PATH` to worry about:

```bash
# one-off smoke test
uvx --from "git+https://github.com/bistin/max-mcp-server.git" max-mcp
```

`uvx` is the command the MCP client will run too (see config sections below),
which sidesteps the whole "is it on my PATH" problem.

For local development with uv:

```bash
git clone https://github.com/bistin/max-mcp-server.git
cd max-mcp-server
uv sync                 # creates .venv and installs the project + deps
uv run max-mcp          # runs the stdio server inside the managed env
```

### Option B — venv + pip

Standard library virtualenv, no extra tooling:

```bash
git clone https://github.com/bistin/max-mcp-server.git
cd max-mcp-server
python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/max-mcp       # run via the venv's binary, not a bare `max-mcp`
```

The venv's binary lives at `/abs/path/to/max-mcp-server/.venv/bin/max-mcp`
(`...\.venv\Scripts\max-mcp.exe` on Windows). Note that path — you'll give it to
the MCP client below. Activating the venv (`source .venv/bin/activate`) only
affects *your* shell, not the client process, so don't rely on a bare `max-mcp`
working there.

> **Tip:** run `command -v max-mcp` (or `.venv/bin/python -c "import shutil,sys;
> print(shutil.which('max-mcp'))"`) to print the absolute path to paste into the
> configs below.

## Register with Claude Code

With uv (Option A) — no install step, uv resolves the env each launch:

```bash
claude mcp add max \
  -e MAX_API_KEY=your_key \
  -e MAX_API_SECRET=your_secret \
  -- uvx --from "git+https://github.com/bistin/max-mcp-server.git" max-mcp
```

With a venv (Option B) — point at the venv's absolute binary path:

```bash
claude mcp add max \
  -e MAX_API_KEY=your_key \
  -e MAX_API_SECRET=your_secret \
  -- /abs/path/to/max-mcp-server/.venv/bin/max-mcp
```

Add `-e MAX_ENABLE_TRADING=1` only if you want the agent to place/cancel orders.

## Claude Desktop config

Claude Desktop launches the command itself, so the same rule applies: use `uvx`,
or give an absolute path. Pick the block that matches your install.

uv (Option A):

```json
{
  "mcpServers": {
    "max": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/bistin/max-mcp-server.git",
        "max-mcp"
      ],
      "env": {
        "MAX_API_KEY": "your_key",
        "MAX_API_SECRET": "your_secret"
      }
    }
  }
}
```

venv (Option B) — use the absolute path to the venv binary
(`...\.venv\Scripts\max-mcp.exe` on Windows):

```json
{
  "mcpServers": {
    "max": {
      "command": "/abs/path/to/max-mcp-server/.venv/bin/max-mcp",
      "env": {
        "MAX_API_KEY": "your_key",
        "MAX_API_SECRET": "your_secret"
      }
    }
  }
}
```

## Tests

With uv:

```bash
uv run --extra test pytest
```

With a venv:

```bash
.venv/bin/pip install -e ".[test]"
.venv/bin/python -m pytest
```

All HTTP is mocked with `respx`, so the suite never touches the live API or your
account — it's safe to run with real credentials in the environment. The 44
tests cover:

- **Signing/headers** — nonce in both query and payload, `path` match, HMAC
  signature, monotonic nonce under same-millisecond concurrency.
- **Request placement** — GET params in the query string vs POST/DELETE params
  in the JSON body; `None`-valued optionals dropped, never sent as `null`.
- **Tool gating** — write tools hidden unless `MAX_ENABLE_TRADING`; flag parsing
  of `1`/`true`/`yes` (case-insensitive).
- **Edge cases** — `markets[]` array params repeated correctly, the
  `cancel_all_orders` safety scope, `calibrate_time` propagating upstream errors
  instead of crashing, error/​network-failure mapping to a structured dict, and
  the console entry point.

## Notes

- All prices/volumes/fees/balances are returned as **strings** — keep them as
  decimal strings, never parse to float.
- K-lines return bare numeric arrays `[ts, o, h, l, c, v]` with unix-second
  timestamps.
- MAX API errors are returned as `{"_http_status": <int>, "success": false,
  "error": {"code", "message"}}` so the agent can read the code/message.

## License

[Apache-2.0](./LICENSE) © bistin