{"slug": "mcp-server-for-hyperliquid-trading-perps", "title": "MCP server for Hyperliquid trading perps", "summary": "A developer released an MCP server that gives AI models direct, signed access to Hyperliquid perpetual futures trading, with built-in safety checks to prevent costly order failures. The tool supports testnet and mainnet trading, agent mode for production, and includes thirty tools for order execution, bracket orders, and leverage management.", "body_md": "An MCP server that gives your AI/LLM direct, signed access to Hyperliquid perps. Built on the official Python SDK, hardened for the one failure mode that actually costs money: a rejected order reported as success. Every write parses the exchange response before it claims anything. Every read tells you where the data came from.\n\nYou point Claude (or any MCP client) at it, hand it a key, and it trades.\n\n```\nuvx --from mcp-hyperliquid hyperliquid-mcp\n```\n\nThat's it. No `.env`\n\nfile, no config directory — everything comes from your MCP client's `env`\n\nblock:\n\n```\n{\n  \"mcpServers\": {\n    \"hyperliquid\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"mcp-hyperliquid\", \"hyperliquid-mcp\"],\n      \"env\": {\n        \"HYPERLIQUID_PRIVATE_KEY\": \"0x...\",\n        \"HYPERLIQUID_TESTNET\": \"true\"\n      }\n    }\n  }\n}\n```\n\nYes, `\"true\"`\n\n. Start on testnet. Flip it to `\"false\"`\n\nafter your strategy survives fake money.\n\nRunning from source instead:\n\n```\ngit clone https://github.com/Dakkshin/hyperliquid-mcp.git\ncd hyperliquid-mcp\nuv sync\nuv run python -m hyperliquid_mcp.server\n```\n\n(Same client config, but `command: \"uv\"`\n\n, `args: [\"--directory\", \"/path/to/hyperliquid-mcp\", \"run\", \"python\", \"-m\", \"hyperliquid_mcp.server\"]`\n\n.)\n\nHyperliquid doesn't know your wallet until it holds funds. One-time setup:\n\n**Mainnet:** connect at[https://app.hyperliquid.xyz](https://app.hyperliquid.xyz), deposit anything from Arbitrum. Deposit = registration.**Testnet:** connect at[https://app.hyperliquid-testnet.xyz](https://app.hyperliquid-testnet.xyz), hit the faucet.\n\nSkip this and every write comes back `User or API Wallet does not exist`\n\n. The server will hand you that exact message instead of a stack trace — but it can't deposit for you.\n\n**Just exploring / paper trading.** `HYPERLIQUID_TESTNET=true`\n\n, your wallet key, done. Testnet is a full parallel universe: separate ledger, separate agent approvals, and — this bites everyone — **separate asset indices**. BTC is index 0 on mainnet and 3 on testnet. Never hardcode an index; resolve through `hyperliquid_get_meta`\n\nevery time.\n\n**Running real money.** Use agent mode. Generate an API wallet in the Hyperliquid UI (More -> API), approve it from your main account, then:\n\n```\n\"env\": {\n  \"HYPERLIQUID_PRIVATE_KEY\": \"0xApiWalletKey...\",\n  \"HYPERLIQUID_ACCOUNT_ADDRESS\": \"0xYourMainAccount...\",\n  \"HYPERLIQUID_TESTNET\": \"false\"\n}\n```\n\nThe API wallet signs; your main key never touches the machine running the model. This is the only sane production configuration. Agent approvals are per-network — a mainnet-approved agent is a stranger on testnet.\n\n**Trading builder-dex perps (equities, gold, HIP-3 stuff).** Set `HYPERLIQUID_PERP_DEXS=\"xyz\"`\n\n(comma-separated for several). Unset means primary dex only — ~2s startup, covers every standard perp. `\"all\"`\n\nloads every discovered dex: hundreds of serial REST calls, ~90s, and your MCP client kills the connection at 30s unless you raise `MCP_TIMEOUT=180000`\n\n. You almost certainly don't want `\"all\"`\n\n.\n\nOptional: `HYPERLIQUID_VAULT_ADDRESS`\n\nif you trade a vault.\n\nThirty tools. The interesting ones:\n\n**Execution.** `place_order`\n\n, `place_bracket_order`\n\n, `modify_order`\n\n, `cancel_order`\n\n, `cancel_all_orders`\n\n, `update_leverage`\n\n. Market orders are emulated the way the SDK does it — aggressive IoC limit at mid ±5% — because Hyperliquid has no native market order and a resting buy at $0 would just sit there forever.\n\n**Brackets are real OCO.** Entry + TP + SL ship as one atomic `normalTpsl`\n\ngroup. TP and SL are children of the entry: one fills, the exchange kills the other; cancel the entry, both die. No stale stop left resting on the book at 3am. The stop-loss triggers as *market* with a slippage-bounded limit — a limit stop can gap straight through its price and never fill, which defeats the entire point of a stop.\n\n**The server refuses garbage before signing it.** Wrong-side stop on a long (SL above entry)? Rejected locally — it would trigger the instant it hit the book. `size: \"NaN\"`\n\n? Rejected — the SDK's wire encoder would happily sign it otherwise. `asset: 5.7`\n\n? Rejected, not truncated to 5 — silent truncation means ordering the wrong instrument.\n\n**And it never lies about outcomes.** A rejected cancel says `Cancel failed`\n\nwith the exchange's reason. A bracket with an invalid leg reports the atomic group rejection. `cancel_all_orders`\n\nreturns per-oid outcomes and an honest count — including the untriggered TP/SL stops, which the naive open-orders endpoint doesn't even list. If a write *times out*, the response says \"may still have executed, recheck\" — because a timeout after the request left the building is not a failure, and treating it as one is how you double-fill.\n\n**Market data with a freshness receipt.** Order books, trades, candles, funding, open interest. Hot reads are served from a live in-memory WebSocket mirror — O(1), no REST round-trip — and every response carries `source: \"websocket\"`\n\nor `\"rest\"`\n\nso you know exactly what you got. The SDK's socket doesn't auto-reconnect; when it dies, reads silently degrade to REST instead of serving you a stale book. Slower, never wrong.\n\n**Server-side quant, token-sized payloads.** The whole point of computing on the server: an 8B model shouldn't be doing float comparisons, and you shouldn't pay tokens for 1000 raw book levels.\n\n`get_microstructure`\n\n— OBI, Stoikov micro-price, spread in bps. ~20 tokens.`get_orderflow`\n\n— CVD, aggressor buy/sell volume, trade-flow imbalance off the live tape. Book says intent, tape says action; read both.`get_indicators`\n\n— RSI (Wilder's, pinned by tests against Cutler's), Bollinger (population std, also pinned), EMA 9/21/50/200, volume SMA. One call, one interval. Returns raw values plus*deterministic*flags —`rsi.zone`\n\n,`ema.is_ordered_up`\n\n,`bollinger.is_squeeze`\n\n. Mathematical facts only. It will never tell you`signal: \"BUY\"`\n\n; forming opinions is your model's job.`run_monte_carlo`\n\n— vectorized GBM, 10k+ paths, returns VaR, terminal percentiles, probability of profit. Samples terminal prices directly (one draw per path, O(iterations) whatever the horizon). Default drift is martingale — zero expected return — because 30 days of history is a vibe, not a drift estimate. Check`observations`\n\nin the output: the candle API caps around 5000 rows, so a 30-day lookback at 1m candles is quietly ~3.5 days of data. The field exists so you can catch it.`get_beta`\n\n— beta, correlation, R² against a benchmark you must name (no sneaky default). Timestamp-aligned log-return regression. No alpha, no flags — beta is a continuous sizing input, and a`beta > 1`\n\nboolean would invent a cliff that doesn't exist.\n\n**Not implemented:** `place_twap_order`\n\n/ `cancel_twap_order`\n\nare schema stubs that raise. They're declared so clients can see them coming; they don't work yet. ¯\\*(ツ)*/¯\n\nYou don't call these tools — your model does. Prompts that work:\n\n```\nShow me my Hyperliquid balance\n```\n\nReturns both ledgers — perp *and* spot. Hyperliquid keeps them separate, and a wallet holding only spot USDC reports a $0 perp balance. Learned that one the annoying way; the tool now surfaces `totalUsdcAcrossAccounts`\n\nso nobody else has to.\n\n```\nIs there buy or sell pressure on HYPE right now?\n```\n\nOne `get_microstructure`\n\ncall:\n\n```\n{\"asset\":\"HYPE\",\"source\":\"websocket\",\"depth\":5,\"OBI\":0.62,\"micro_price\":1.452,\"mid\":1.451,\"spread_bps\":2.1}\n```\n\nOBI 0.62 = bids carrying 62% of top-of-book volume. Micro-price above mid = pressure pointing up. Spread ~2bps = liquid.\n\n```\nLong 4.12 SOL, entry 218, target 219.50, stop 216.80\n```\n\nModel resolves SOL's index via `get_meta`\n\n, fires `place_bracket_order`\n\n, gets back three legs with statuses. If it had asked for a stop above entry, the order never leaves the machine.\n\n```\nIf I hold BTC for 7 days, what's my downside?\n```\n\n`run_monte_carlo`\n\n-> `VaR_5pct: 0.0836`\n\n= 8.4% loss at the 5th percentile. Ten thousand simulated paths, none of which cross the wire — only the summary does.\n\n** User or API Wallet does not exist** — wallet not registered on\n\n*this*network, or agent not approved on\n\n*this*network. Deposit/faucet first; approve agents per-network.\n\n** Order has invalid price. / Order has invalid size.** — tick and lot rules. Prices: max 5 significant figures and per-asset decimal limits. Sizes:\n\n`szDecimals`\n\nfrom `get_meta`\n\n. And `size × price ≥ $10`\n\n, always.**Connection times out at startup** — you set `HYPERLIQUID_PERP_DEXS=\"all\"`\n\n. Unset it, or raise `MCP_TIMEOUT`\n\n. This is a startup-speed problem, not a config error.\n\n**Every read says source: \"rest\"** — the WebSocket died (it doesn't reconnect) or hasn't warmed up yet. First read on a coin is always REST; the mirror takes over within a couple of seconds. Persistent REST means restart the server. Data stays correct either way — that's the fallback doing its job.\n\n**Debugging anything else:**\n\n```\nHYPERLIQUID_LOG_LEVEL=DEBUG uvx --from mcp-hyperliquid hyperliquid-mcp\n```\n\n- The key in your MCP config can sign orders. Treat that file like the key itself.\n- Agent mode for anything real. Main key stays offline.\n- Testnet first. The parallel universe is free.\n- Brackets over naked entries. The exchange-side OCO exists so a dead process can't orphan your stop.\n- The server never logs or echoes the private key. It also can't stop you from pasting it into a chat. Don't.\n\n```\nuv sync\nuv run pytest        # ~100 tests: golden-value math pins, response-parsing fixtures, liveness guards\nuv run black src/\nuv run mypy src/\n```\n\nEverything lives in `src/hyperliquid_mcp/server.py`\n\n— one class, two SDK clients, a dispatch table, and a threaded WebSocket state engine. The tests are the contract: Wilder RSI vs Cutler's, Bollinger `ddof=0`\n\nvs `ddof=1`\n\n, martingale drift, OCO status parsing — swap a formula and the suite fails loudly, which is the entire point.\n\nPRs welcome. Bring tests; the math ones especially — a wrong number that parses is worse than a crash.\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\nThis is software that lets a language model sign orders against a perpetuals exchange with real leverage. It is provided as-is, it has edge cases, and markets do not care about you. Trade only what you can lose entirely. Nobody here is responsible for your P&L.", "url": "https://wpnews.pro/news/mcp-server-for-hyperliquid-trading-perps", "canonical_source": "https://github.com/Dakkshin/hyperliquid-mcp", "published_at": "2026-07-11 07:47:03+00:00", "updated_at": "2026-07-11 08:04:53.518058+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools"], "entities": ["Hyperliquid", "Claude", "MCP", "Arbitrum"], "alternates": {"html": "https://wpnews.pro/news/mcp-server-for-hyperliquid-trading-perps", "markdown": "https://wpnews.pro/news/mcp-server-for-hyperliquid-trading-perps.md", "text": "https://wpnews.pro/news/mcp-server-for-hyperliquid-trading-perps.txt", "jsonld": "https://wpnews.pro/news/mcp-server-for-hyperliquid-trading-perps.jsonld"}}