hyperliquid-agent-gateway 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. hyperliquid_agent_gateway-0.1.1/.github/workflows/release.yml +26 -0
  2. hyperliquid_agent_gateway-0.1.1/.github/workflows/tests.yml +27 -0
  3. hyperliquid_agent_gateway-0.1.1/.gitignore +7 -0
  4. hyperliquid_agent_gateway-0.1.1/CHANGELOG.md +51 -0
  5. hyperliquid_agent_gateway-0.1.1/LICENSE +21 -0
  6. hyperliquid_agent_gateway-0.1.1/PKG-INFO +205 -0
  7. hyperliquid_agent_gateway-0.1.1/README.md +178 -0
  8. hyperliquid_agent_gateway-0.1.1/deploy/hyperliquid-recorder.service +16 -0
  9. hyperliquid_agent_gateway-0.1.1/deploy/hyperliquid-recorder.timer +15 -0
  10. hyperliquid_agent_gateway-0.1.1/hyperliquid_mcp/API_NOTES.md +92 -0
  11. hyperliquid_agent_gateway-0.1.1/hyperliquid_mcp/__init__.py +11 -0
  12. hyperliquid_agent_gateway-0.1.1/hyperliquid_mcp/evm.py +380 -0
  13. hyperliquid_agent_gateway-0.1.1/hyperliquid_mcp/info.py +350 -0
  14. hyperliquid_agent_gateway-0.1.1/hyperliquid_mcp/server.py +1230 -0
  15. hyperliquid_agent_gateway-0.1.1/pyproject.toml +43 -0
  16. hyperliquid_agent_gateway-0.1.1/scripts/recorder.py +125 -0
  17. hyperliquid_agent_gateway-0.1.1/scripts/smoke_api_offline.py +191 -0
  18. hyperliquid_agent_gateway-0.1.1/scripts/smoke_server_offline.py +231 -0
  19. hyperliquid_agent_gateway-0.1.1/scripts/smoke_v011_live.py +87 -0
  20. hyperliquid_agent_gateway-0.1.1/tests/conftest.py +95 -0
  21. hyperliquid_agent_gateway-0.1.1/tests/fixtures/allMids.json +954 -0
  22. hyperliquid_agent_gateway-0.1.1/tests/fixtures/candleSnapshot.json +590 -0
  23. hyperliquid_agent_gateway-0.1.1/tests/fixtures/clearinghouseState.json +1032 -0
  24. hyperliquid_agent_gateway-0.1.1/tests/fixtures/fundingHistory.json +1010 -0
  25. hyperliquid_agent_gateway-0.1.1/tests/fixtures/getLogs.json +182 -0
  26. hyperliquid_agent_gateway-0.1.1/tests/fixtures/l2Book.json +210 -0
  27. hyperliquid_agent_gateway-0.1.1/tests/fixtures/meta.json +1585 -0
  28. hyperliquid_agent_gateway-0.1.1/tests/fixtures/metaAndAssetCtxs.json +4916 -0
  29. hyperliquid_agent_gateway-0.1.1/tests/fixtures/recentTrades.json +132 -0
  30. hyperliquid_agent_gateway-0.1.1/tests/fixtures/spotClearinghouseState.json +25 -0
  31. hyperliquid_agent_gateway-0.1.1/tests/fixtures/spotMeta.json +8947 -0
  32. hyperliquid_agent_gateway-0.1.1/tests/fixtures/spotMetaAndAssetCtxs.json +16141 -0
  33. hyperliquid_agent_gateway-0.1.1/tests/fixtures/userFills.json +9017 -0
  34. hyperliquid_agent_gateway-0.1.1/tests/fixtures/userFunding.json +6002 -0
  35. hyperliquid_agent_gateway-0.1.1/tests/test_evm.py +336 -0
  36. hyperliquid_agent_gateway-0.1.1/tests/test_info.py +269 -0
  37. hyperliquid_agent_gateway-0.1.1/tests/test_live_regressions.py +124 -0
  38. hyperliquid_agent_gateway-0.1.1/tests/test_online.py +92 -0
  39. hyperliquid_agent_gateway-0.1.1/tests/test_server.py +752 -0
  40. hyperliquid_agent_gateway-0.1.1/uv.lock +2303 -0
@@ -0,0 +1,26 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write
12
+ contents: write
13
+ steps:
14
+ - uses: actions/checkout@v5
15
+ - uses: actions/setup-python@v6
16
+ with:
17
+ python-version: "3.13"
18
+ - run: pip install build twine
19
+ - run: python -m build
20
+ - run: python -m twine check dist/*
21
+ - uses: pypa/gh-action-pypi-publish@release/v1
22
+ - name: GitHub Release
23
+ uses: softprops/action-gh-release@v2
24
+ with:
25
+ files: dist/*
26
+ generate_release_notes: true
@@ -0,0 +1,27 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ offline:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python: ["3.11", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v5
16
+ - uses: actions/setup-python@v6
17
+ with:
18
+ python-version: ${{ matrix.python }}
19
+ - run: pip install -e ".[dev]"
20
+ - name: Run tests
21
+ run: |
22
+ pytest tests/ -q --cov=hyperliquid_mcp --tb=short > pytest_output.txt 2>&1 || FAILED=1
23
+ grep -E "^(FAILED|ERROR)|Error|error" pytest_output.txt | head -25 | while IFS= read -r line; do
24
+ echo "::error::${line:0:250}"
25
+ done
26
+ tail -40 pytest_output.txt
27
+ exit ${FAILED:-0}
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.log
5
+ .coverage
6
+ .pytest_cache/
7
+ tests/fixtures/live_*
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1 (2026-09-06)
4
+
5
+ Fix release from the independent v0.1.0 review.
6
+
7
+ - `liquidation_risk` (blocker): the LIVE `clearinghouseState` response
8
+ carries no `markPx` in positions, so `mark_px` and
9
+ `liq_distance_pct` were null in every branch live. Mark is now
10
+ resolved per coin from `metaAndAssetCtxs` (60s cache) with an
11
+ `allMids` fallback; each row reports `mark_px_source`
12
+ ('position' | 'metaAndAssetCtxs' | 'allMids' | null) and distances
13
+ stay null (honest) when no source knows the coin. `trader_activity`
14
+ open-position rows get the same resolver. Note documents that
15
+ `withdrawable` may be omitted by the venue while positions are open.
16
+ - `tests/fixtures/clearinghouseState.json` re-recorded from the live
17
+ API (no `markPx` in positions, no `withdrawable` in marginSummary)
18
+ so the offline suite exercises the live shape; regression tests
19
+ cover mark from cache / allMids fallback / neither, liqPx present
20
+ and null, cross and isolated.
21
+ - `wallet_balance` / `token_transfers`: per-token decimals via a
22
+ static map of canonical HyperEVM tokens (6 for USDC/USDT-style,
23
+ 18 for PURR/HYPE) with honest `decimals_source`
24
+ ('static_map' | 'assumed_18') on every row; unknown tokens assume
25
+ 18 and say so.
26
+ - `funding_history` / `userFunding` README note: the venue returns
27
+ only non-zero funding events, so a live `funding_drag` of null for a
28
+ fresh address is expected, not a bug.
29
+ - Version bumped to 0.1.1 (pyproject == package == server).
30
+
31
+ ## 0.1.0 (2026-09-06)
32
+
33
+ Initial release.
34
+
35
+ - Read-only, keyless MCP gateway to Hyperliquid public data.
36
+ - `/info` client (`hyperliquid_mcp/info.py`): weight-based rate bucket
37
+ (1200 weight/min), per-type TTL caches, retries with backoff on
38
+ 429/5xx, `User-Agent: hyperliquid-agent-gateway/0.1`.
39
+ - HyperEVM JSON-RPC client (`hyperliquid_mcp/evm.py`): separate 100
40
+ req/min budget, `RpcError` taxonomy, adaptive `eth_getLogs` window
41
+ (30-block start, halve on too-big, double on quiet, cap 60).
42
+ - 12 MCP tools (`hyperliquid_mcp/server.py`): market_overview,
43
+ spot_overview, quote, order_book, candles, trades, funding_history,
44
+ liquidation_risk, trader_activity, funding_carry_screener,
45
+ token_transfers, wallet_balance - all annotated read-only with honest
46
+ degradation (error dicts, never tracebacks) and freshness fields.
47
+ - Offline test suite on schema-realistic fixtures (well over a hundred
48
+ checks; run `pytest -q` for the exact count); live online checks are
49
+ opt-in via `-m online`.
50
+ - `scripts/recorder.py` live-fixture recorder + systemd units
51
+ (6h schedule, disabled by default).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hyperliquid-agent-gateway contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.5
2
+ Name: hyperliquid-agent-gateway
3
+ Version: 0.1.1
4
+ Summary: MCP gateway for AI agents to Hyperliquid public data: perp/spot market overviews, quotes, order books, candles, trades, funding, account risk and HyperEVM transfers. Read-only, keyless.
5
+ Project-URL: Homepage, https://github.com/alekskram/hyperliquid-agent-gateway
6
+ Project-URL: Repository, https://github.com/alekskram/hyperliquid-agent-gateway
7
+ Project-URL: Issues, https://github.com/alekskram/hyperliquid-agent-gateway/issues
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai-agents,defi,funding,hyper evm,hyperliquid,mcp,perps
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Office/Business :: Financial :: Investment
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: fastmcp>=3.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: hatchling; extra == 'dev'
23
+ Requires-Dist: pip-audit; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
25
+ Requires-Dist: pytest>=8; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # hyperliquid-agent-gateway
29
+
30
+ [![CI](https://github.com/alekskram/hyperliquid-agent-gateway/actions/workflows/tests.yml/badge.svg)](https://github.com/alekskram/hyperliquid-agent-gateway/actions/workflows/tests.yml)
31
+ [![PyPI](https://img.shields.io/pypi/v/hyperliquid-agent-gateway.svg)](https://pypi.org/project/hyperliquid-agent-gateway/)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
33
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](pyproject.toml)
34
+
35
+ An MCP (Model Context Protocol) server that gives AI agents read-only,
36
+ keyless access to **Hyperliquid** public data - the ~233-perp DEX
37
+ market, ~326 spot pairs, funding, per-account risk and HyperEVM (chain
38
+ 999) token transfers. No API keys, no auth, no signing, no writes:
39
+ every tool reads public endpoints only (`api.hyperliquid.xyz/info` and
40
+ `rpc.hyperliquid.xyz/evm`), cached and rate-limited so an enthusiastic
41
+ agent cannot hammer the upstream.
42
+
43
+ ## Use cases
44
+
45
+ - **Watch a wallet's risk** — per-account margin summary, leverage, liquidation distance on any address (`account risk` view)
46
+ - **Fund the carry, not the noise** — funding history + carry screener across 233 perps to find stable paid positions
47
+ - **Trace HyperEVM flows** — token transfers on chain 999 tied back to the perp markets (`token_transfers`)
48
+ - **Read the book before you enter** — order book + recent trades + all-mids in one pass
49
+ - **Trader scouting** — activity of any address: positions, volume, what they actually trade
50
+
51
+ Full walkthroughs: [examples/use-cases.md](examples/use-cases.md).
52
+
53
+ ## Quickstart
54
+
55
+ stdio (default, for local agents):
56
+
57
+ ```bash
58
+ uvx hyperliquid-agent-gateway
59
+ ```
60
+
61
+ or from a checkout:
62
+
63
+ ```bash
64
+ git clone https://github.com/alekskram/hyperliquid-agent-gateway
65
+ cd hyperliquid-agent-gateway
66
+ uv sync
67
+ uv run hyperliquid-agent-gateway
68
+ ```
69
+
70
+ Claude Desktop / Cursor config:
71
+
72
+ ```json
73
+ {
74
+ "mcpServers": {
75
+ "hyperliquid": {
76
+ "command": "uvx",
77
+ "args": ["--from",
78
+ "git+https://github.com/alekskram/hyperliquid-agent-gateway",
79
+ "hyperliquid-agent-gateway"]
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ Hosted form - streamable HTTP on port **8903**:
86
+
87
+ ```bash
88
+ uv run hyperliquid-agent-gateway --http # 127.0.0.1:8903
89
+ curl http://127.0.0.1:8903/health # -> {"ok": true, "service": "hyperliquid-agent-gateway", ...}
90
+ ```
91
+
92
+ <details>
93
+ <summary><b>Codex</b> (~/.codex/config.toml)</summary>
94
+
95
+ ```toml
96
+ [mcp_servers.hyperliquid]
97
+ command = "uvx"
98
+ args = ["hyperliquid-agent-gateway"]
99
+ ```
100
+ </details>
101
+
102
+ <details>
103
+ <summary><b>ZCode</b> — register the server (copy-paste)</summary>
104
+
105
+ ```bash
106
+ # 1) start the gateway (keep it running)
107
+ uvx hyperliquid-agent-gateway --http --port 8903 &
108
+
109
+ # 2) register it (merges into ~/.zcode/cli/config.json)
110
+ python3 - <<'PY'
111
+ import json, os
112
+ p = os.path.expanduser("~/.zcode/cli/config.json")
113
+ os.makedirs(os.path.dirname(p), exist_ok=True)
114
+ cfg = json.load(open(p)) if os.path.exists(p) else {}
115
+ cfg.setdefault("mcp", {}).setdefault("servers", {})["hyperliquid"] = {
116
+ "type": "http", "url": "http://127.0.0.1:8903/mcp"}
117
+ json.dump(cfg, open(p, "w"), indent=2)
118
+ print("hyperliquid-agent-gateway registered:", p)
119
+ PY
120
+ ```
121
+ </details>
122
+
123
+ Hosted form — streamable HTTP on port **8903**:
124
+
125
+ ```bash
126
+ uvx hyperliquid-agent-gateway --http
127
+ ```
128
+
129
+ ## Tools
130
+
131
+ All 12 tools are read-only (annotated `readOnlyHint: true,
132
+ destructiveHint: false, openWorldHint: true`).
133
+
134
+ | # | Tool | Signature | What it does |
135
+ |---|------|-----------|--------------|
136
+ | 1 | `market_overview` | `market_overview(limit=20, sort="open_interest")` | Perp market snapshot from ONE `metaAndAssetCtxs` call: per-coin mark, open interest, day volume, premium, max leverage + totals. `sort` in {open_interest, volume, premium}. |
137
+ | 2 | `spot_overview` | `spot_overview(limit=20)` | Spot pairs from `spotMeta` + ctxs with HIP-1 to ERC-20 links; `@{index}` names resolved to readable token names. |
138
+ | 3 | `quote` | `quote(coin)` | Bid/ask/mid/spread + top-of-book sizes from `allMids` + `l2Book`. Unknown coin raises with 5 examples. |
139
+ | 4 | `order_book` | `order_book(coin, depth=10)` | Book levels per side with nSigFigs aggregation and per-side total liquidity. |
140
+ | 5 | `candles` | `candles(coin, interval="1h", limit=100)` | OHLCV rows newest-first; intervals 1m/15m/1h/4h/1d/1w/1M; `startTime` computed from `limit`. |
141
+ | 6 | `trades` | `trades(coin, limit=20)` | Recent public fills WITH both sides' addresses (`users: [maker, taker]`). |
142
+ | 7 | `funding_history` | `funding_history(coin, limit=100)` | Hourly funding rows + `premium_now` from the live asset ctx. |
143
+ | 8 | `liquidation_risk` | `liquidation_risk(address)` | Per-account risk: margin summary, cross maintenance margin, per-position leverage + `liquidationPx` when published; when null, an explicitly flagged ESTIMATED distance from the maintenance-margin ratio. Mark px is resolved per coin from `metaAndAssetCtxs` (fallback `allMids`) because live positions carry no `markPx` - see `mark_px_source` on each row. Includes funding drag. |
144
+ | 9 | `trader_activity` | `trader_activity(address, limit=50)` | Fills PnL/fees/volume/win-rate, funding net, open positions, per-coin breakdown. |
145
+ | 10 | `funding_carry_screener` | `funding_carry_screener(topN=10, metric="premium")` | Ranks ALL perps from ONE call; `fundingHistory` fetched only for the topN (weight economy). |
146
+ | 11 | `token_transfers` | `token_transfers(contract, limit=100, from_block=None)` | HyperEVM ERC-20 Transfer logs via adaptive-window `eth_getLogs`; rows carry from/to/value/txHash/blockNumber/ts with per-token `decimals` + `decimals_source` (static map or `assumed_18`). |
147
+ | 12 | `wallet_balance` | `wallet_balance(address)` | Native (eth_getBalance) + up to 20 ERC-20s (eth_call balanceOf, resolved from spotMeta) + Hyperliquid spot balances; every row carries `decimals`/`decimals_source`. |
148
+
149
+ ## Rate limits
150
+
151
+ Two independent, locally enforced budgets protect the upstream:
152
+
153
+ **`/info` - 1200 weight per rolling 60s** (Hyperliquid's documented
154
+ weight pricing), tracked per request type:
155
+
156
+ | type | weight |
157
+ |------|--------|
158
+ | `allMids` | 2 |
159
+ | `l2Book` | 2 |
160
+ | `meta`, `metaAndAssetCtxs`, `spotMeta`, `spotMetaAndAssetCtxs` | 20 |
161
+ | `recentTrades`, `clearinghouseState`, `userFills`, `userFunding`, `spotClearinghouseState` | 20 |
162
+ | `fundingHistory` | 20 base + extra per 20 items beyond the first |
163
+ | `candleSnapshot` | 60 |
164
+
165
+ When the next request would exceed the budget the client waits once
166
+ (<=5s) for the window to roll, then raises a clear error naming the
167
+ limit - it never sleep-blocks forever.
168
+
169
+ **HyperEVM RPC - 100 requests per rolling 60s** (flat 1 per request),
170
+ enforced separately from /info. Over-budget calls raise immediately
171
+ (`rpc-limit`) - tools surface an honest error dict, and
172
+ `wallet_balance` stops its ERC-20 scan at the cap.
173
+
174
+ TTL caches additionally dedupe repeated calls per data type: allMids
175
+ 15s, recentTrades 15s, l2Book 5s, metaAndAssetCtxs 60s, spotMeta 3600s,
176
+ spotMetaAndAssetCtxs 60s, candleSnapshot 300s, fundingHistory 300s,
177
+ per-address account types 60s.
178
+
179
+ ## Data notes
180
+
181
+ - Every numeric from the API is a STRING upstream; the gateway parses
182
+ them with a never-raising helper - `null` always means "not
183
+ available", never zero.
184
+ - Every upstream failure returns an error dict
185
+ `{"error": ..., "source": ..., "reason": ...}`, never a traceback;
186
+ partial data degrades field-by-field with `warnings[]`.
187
+ - `liquidation_risk` never invents a liquidation price: when the venue
188
+ publishes none, `liq_px` stays `null` and the distance is an
189
+ explicitly flagged estimate (formula in the tool's note). Mark px is
190
+ likewise never invented: live positions carry no `markPx`, so it is
191
+ resolved from `metaAndAssetCtxs` (fallback `allMids`) and the row's
192
+ `mark_px_source` says which; no source -> `null`.
193
+ - `funding_drag` / `funding_net`: the venue's `userFunding` returns
194
+ only NON-ZERO funding events, so a live `null`/empty for a fresh or
195
+ quiet address is expected behaviour, not a bug.
196
+ - ERC-20 amounts use a static decimals map for canonical HyperEVM
197
+ tokens (6 for USDC/USDT-style, 18 for PURR/HYPE); unknown tokens
198
+ assume 18 and every row says `decimals_source: "assumed_18"` - do
199
+ not trust 6dp precision for unmapped tokens.
200
+ - Cached responses carry `age_seconds` / `fetched_at` freshness fields.
201
+
202
+
203
+ ## License
204
+
205
+ MIT.
@@ -0,0 +1,178 @@
1
+ # hyperliquid-agent-gateway
2
+
3
+ [![CI](https://github.com/alekskram/hyperliquid-agent-gateway/actions/workflows/tests.yml/badge.svg)](https://github.com/alekskram/hyperliquid-agent-gateway/actions/workflows/tests.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/hyperliquid-agent-gateway.svg)](https://pypi.org/project/hyperliquid-agent-gateway/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](pyproject.toml)
7
+
8
+ An MCP (Model Context Protocol) server that gives AI agents read-only,
9
+ keyless access to **Hyperliquid** public data - the ~233-perp DEX
10
+ market, ~326 spot pairs, funding, per-account risk and HyperEVM (chain
11
+ 999) token transfers. No API keys, no auth, no signing, no writes:
12
+ every tool reads public endpoints only (`api.hyperliquid.xyz/info` and
13
+ `rpc.hyperliquid.xyz/evm`), cached and rate-limited so an enthusiastic
14
+ agent cannot hammer the upstream.
15
+
16
+ ## Use cases
17
+
18
+ - **Watch a wallet's risk** — per-account margin summary, leverage, liquidation distance on any address (`account risk` view)
19
+ - **Fund the carry, not the noise** — funding history + carry screener across 233 perps to find stable paid positions
20
+ - **Trace HyperEVM flows** — token transfers on chain 999 tied back to the perp markets (`token_transfers`)
21
+ - **Read the book before you enter** — order book + recent trades + all-mids in one pass
22
+ - **Trader scouting** — activity of any address: positions, volume, what they actually trade
23
+
24
+ Full walkthroughs: [examples/use-cases.md](examples/use-cases.md).
25
+
26
+ ## Quickstart
27
+
28
+ stdio (default, for local agents):
29
+
30
+ ```bash
31
+ uvx hyperliquid-agent-gateway
32
+ ```
33
+
34
+ or from a checkout:
35
+
36
+ ```bash
37
+ git clone https://github.com/alekskram/hyperliquid-agent-gateway
38
+ cd hyperliquid-agent-gateway
39
+ uv sync
40
+ uv run hyperliquid-agent-gateway
41
+ ```
42
+
43
+ Claude Desktop / Cursor config:
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "hyperliquid": {
49
+ "command": "uvx",
50
+ "args": ["--from",
51
+ "git+https://github.com/alekskram/hyperliquid-agent-gateway",
52
+ "hyperliquid-agent-gateway"]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ Hosted form - streamable HTTP on port **8903**:
59
+
60
+ ```bash
61
+ uv run hyperliquid-agent-gateway --http # 127.0.0.1:8903
62
+ curl http://127.0.0.1:8903/health # -> {"ok": true, "service": "hyperliquid-agent-gateway", ...}
63
+ ```
64
+
65
+ <details>
66
+ <summary><b>Codex</b> (~/.codex/config.toml)</summary>
67
+
68
+ ```toml
69
+ [mcp_servers.hyperliquid]
70
+ command = "uvx"
71
+ args = ["hyperliquid-agent-gateway"]
72
+ ```
73
+ </details>
74
+
75
+ <details>
76
+ <summary><b>ZCode</b> — register the server (copy-paste)</summary>
77
+
78
+ ```bash
79
+ # 1) start the gateway (keep it running)
80
+ uvx hyperliquid-agent-gateway --http --port 8903 &
81
+
82
+ # 2) register it (merges into ~/.zcode/cli/config.json)
83
+ python3 - <<'PY'
84
+ import json, os
85
+ p = os.path.expanduser("~/.zcode/cli/config.json")
86
+ os.makedirs(os.path.dirname(p), exist_ok=True)
87
+ cfg = json.load(open(p)) if os.path.exists(p) else {}
88
+ cfg.setdefault("mcp", {}).setdefault("servers", {})["hyperliquid"] = {
89
+ "type": "http", "url": "http://127.0.0.1:8903/mcp"}
90
+ json.dump(cfg, open(p, "w"), indent=2)
91
+ print("hyperliquid-agent-gateway registered:", p)
92
+ PY
93
+ ```
94
+ </details>
95
+
96
+ Hosted form — streamable HTTP on port **8903**:
97
+
98
+ ```bash
99
+ uvx hyperliquid-agent-gateway --http
100
+ ```
101
+
102
+ ## Tools
103
+
104
+ All 12 tools are read-only (annotated `readOnlyHint: true,
105
+ destructiveHint: false, openWorldHint: true`).
106
+
107
+ | # | Tool | Signature | What it does |
108
+ |---|------|-----------|--------------|
109
+ | 1 | `market_overview` | `market_overview(limit=20, sort="open_interest")` | Perp market snapshot from ONE `metaAndAssetCtxs` call: per-coin mark, open interest, day volume, premium, max leverage + totals. `sort` in {open_interest, volume, premium}. |
110
+ | 2 | `spot_overview` | `spot_overview(limit=20)` | Spot pairs from `spotMeta` + ctxs with HIP-1 to ERC-20 links; `@{index}` names resolved to readable token names. |
111
+ | 3 | `quote` | `quote(coin)` | Bid/ask/mid/spread + top-of-book sizes from `allMids` + `l2Book`. Unknown coin raises with 5 examples. |
112
+ | 4 | `order_book` | `order_book(coin, depth=10)` | Book levels per side with nSigFigs aggregation and per-side total liquidity. |
113
+ | 5 | `candles` | `candles(coin, interval="1h", limit=100)` | OHLCV rows newest-first; intervals 1m/15m/1h/4h/1d/1w/1M; `startTime` computed from `limit`. |
114
+ | 6 | `trades` | `trades(coin, limit=20)` | Recent public fills WITH both sides' addresses (`users: [maker, taker]`). |
115
+ | 7 | `funding_history` | `funding_history(coin, limit=100)` | Hourly funding rows + `premium_now` from the live asset ctx. |
116
+ | 8 | `liquidation_risk` | `liquidation_risk(address)` | Per-account risk: margin summary, cross maintenance margin, per-position leverage + `liquidationPx` when published; when null, an explicitly flagged ESTIMATED distance from the maintenance-margin ratio. Mark px is resolved per coin from `metaAndAssetCtxs` (fallback `allMids`) because live positions carry no `markPx` - see `mark_px_source` on each row. Includes funding drag. |
117
+ | 9 | `trader_activity` | `trader_activity(address, limit=50)` | Fills PnL/fees/volume/win-rate, funding net, open positions, per-coin breakdown. |
118
+ | 10 | `funding_carry_screener` | `funding_carry_screener(topN=10, metric="premium")` | Ranks ALL perps from ONE call; `fundingHistory` fetched only for the topN (weight economy). |
119
+ | 11 | `token_transfers` | `token_transfers(contract, limit=100, from_block=None)` | HyperEVM ERC-20 Transfer logs via adaptive-window `eth_getLogs`; rows carry from/to/value/txHash/blockNumber/ts with per-token `decimals` + `decimals_source` (static map or `assumed_18`). |
120
+ | 12 | `wallet_balance` | `wallet_balance(address)` | Native (eth_getBalance) + up to 20 ERC-20s (eth_call balanceOf, resolved from spotMeta) + Hyperliquid spot balances; every row carries `decimals`/`decimals_source`. |
121
+
122
+ ## Rate limits
123
+
124
+ Two independent, locally enforced budgets protect the upstream:
125
+
126
+ **`/info` - 1200 weight per rolling 60s** (Hyperliquid's documented
127
+ weight pricing), tracked per request type:
128
+
129
+ | type | weight |
130
+ |------|--------|
131
+ | `allMids` | 2 |
132
+ | `l2Book` | 2 |
133
+ | `meta`, `metaAndAssetCtxs`, `spotMeta`, `spotMetaAndAssetCtxs` | 20 |
134
+ | `recentTrades`, `clearinghouseState`, `userFills`, `userFunding`, `spotClearinghouseState` | 20 |
135
+ | `fundingHistory` | 20 base + extra per 20 items beyond the first |
136
+ | `candleSnapshot` | 60 |
137
+
138
+ When the next request would exceed the budget the client waits once
139
+ (<=5s) for the window to roll, then raises a clear error naming the
140
+ limit - it never sleep-blocks forever.
141
+
142
+ **HyperEVM RPC - 100 requests per rolling 60s** (flat 1 per request),
143
+ enforced separately from /info. Over-budget calls raise immediately
144
+ (`rpc-limit`) - tools surface an honest error dict, and
145
+ `wallet_balance` stops its ERC-20 scan at the cap.
146
+
147
+ TTL caches additionally dedupe repeated calls per data type: allMids
148
+ 15s, recentTrades 15s, l2Book 5s, metaAndAssetCtxs 60s, spotMeta 3600s,
149
+ spotMetaAndAssetCtxs 60s, candleSnapshot 300s, fundingHistory 300s,
150
+ per-address account types 60s.
151
+
152
+ ## Data notes
153
+
154
+ - Every numeric from the API is a STRING upstream; the gateway parses
155
+ them with a never-raising helper - `null` always means "not
156
+ available", never zero.
157
+ - Every upstream failure returns an error dict
158
+ `{"error": ..., "source": ..., "reason": ...}`, never a traceback;
159
+ partial data degrades field-by-field with `warnings[]`.
160
+ - `liquidation_risk` never invents a liquidation price: when the venue
161
+ publishes none, `liq_px` stays `null` and the distance is an
162
+ explicitly flagged estimate (formula in the tool's note). Mark px is
163
+ likewise never invented: live positions carry no `markPx`, so it is
164
+ resolved from `metaAndAssetCtxs` (fallback `allMids`) and the row's
165
+ `mark_px_source` says which; no source -> `null`.
166
+ - `funding_drag` / `funding_net`: the venue's `userFunding` returns
167
+ only NON-ZERO funding events, so a live `null`/empty for a fresh or
168
+ quiet address is expected behaviour, not a bug.
169
+ - ERC-20 amounts use a static decimals map for canonical HyperEVM
170
+ tokens (6 for USDC/USDT-style, 18 for PURR/HYPE); unknown tokens
171
+ assume 18 and every row says `decimals_source: "assumed_18"` - do
172
+ not trust 6dp precision for unmapped tokens.
173
+ - Cached responses carry `age_seconds` / `fetched_at` freshness fields.
174
+
175
+
176
+ ## License
177
+
178
+ MIT.
@@ -0,0 +1,16 @@
1
+ [Unit]
2
+ Description=Hyperliquid Agent Gateway - fixture recorder (one pass)
3
+ Documentation=https://github.com/hyperliquid-agent-gateway/hyperliquid-agent-gateway
4
+ After=network-online.target
5
+ Wants=network-online.target
6
+
7
+ [Service]
8
+ Type=oneshot
9
+ # One pass = 13 /info requests (~264 weight, well inside 1200/min) plus
10
+ # 1-2 HyperEVM eth_getLogs calls (inside 100/min). Fixtures land in
11
+ # tests/fixtures/ of the checkout below.
12
+ WorkingDirectory=/opt/hyperliquid-agent-gateway
13
+ ExecStart=/usr/bin/env python scripts/recorder.py --out tests/fixtures
14
+ Environment=PYTHONUNBUFFERED=1
15
+ # A full pass is ~15 requests: 1-3 minutes worst case with retries
16
+ TimeoutStartSec=300
@@ -0,0 +1,15 @@
1
+ [Unit]
2
+ Description=Hyperliquid Agent Gateway - fixture recorder schedule (6h)
3
+ Documentation=https://github.com/hyperliquid-agent-gateway/hyperliquid-agent-gateway
4
+
5
+ [Timer]
6
+ # Every 6 hours; keeps tests/fixtures/ schema-realistic without
7
+ # approaching either rate budget (a pass costs ~264 /info weight and
8
+ # 1-2 rpc requests).
9
+ # OPT-IN ONLY: this unit ships DISABLED. Enable explicitly with
10
+ # systemctl enable --now hyperliquid-recorder.timer
11
+ OnCalendar=*-*-* 00/6:00:00
12
+ Persistent=true
13
+
14
+ [Install]
15
+ WantedBy=timers.target
@@ -0,0 +1,92 @@
1
+ # API notes - Hyperliquid public surfaces (as verified 2026-09-05/06)
2
+
3
+ Facts the gateway depends on. Every claim below was checked against the
4
+ live endpoints before v0.1.0 was coded; fixtures in `tests/fixtures/`
5
+ mirror these shapes.
6
+
7
+ ## /info (POST https://api.hyperliquid.xyz/info)
8
+
9
+ - Single POST endpoint; body is `{"type": "<Type>", ...}`. Keyless,
10
+ read-only. Action types (signing) are NOT used by this gateway.
11
+ - **ALL numerics are STRINGS** (`"42350.1"`, `"-0.00012"`). Parse with
12
+ a never-raising `_f()`; empty/absent optionals become `None`.
13
+ - Weight-based rate limit: **1200 weight / 60s**. Per-request weights
14
+ implemented in `hyperliquid_mcp/info.py`:
15
+
16
+ | type | weight |
17
+ |------|--------|
18
+ | allMids | 2 |
19
+ | l2Book | 2 |
20
+ | meta, metaAndAssetCtxs, spotMeta, spotMetaAndAssetCtxs | 20 |
21
+ | recentTrades, clearinghouseState, userFills, userFunding, spotClearinghouseState | 20 |
22
+ | fundingHistory | 20 base + extra per 20 items beyond the first |
23
+ | candleSnapshot | 60 |
24
+
25
+ - `metaAndAssetCtxs` returns a **pair** `[meta, assetCtxs]`: meta =
26
+ `{"universe": [{"name", "szDecimals", "maxLeverage",
27
+ "onlyIsolated"}]}`; assetCtxs rows align by index with universe and
28
+ carry `dayNtlVlm`, `openInterest` (coin units), `premium`, `markPx`,
29
+ `midPx`, `oraclePx`, `impactPxs {bidPx, askPx}`, `funding`
30
+ (hourly). ~233 perps at capture time.
31
+ - `spotMeta` = `{"universe": [{"name": "@1/PURR", "tokens": [int
32
+ indices], "isCanonical", "index"}], "tokens": [{"name", "index",
33
+ "token"}], "deployAuctionStatus", "registeredContracts": [{"name",
34
+ "builder"}]}`. `token` is the ERC-20 contract (0x...) for HIP-1
35
+ deployed coins, otherwise the coin name. ~326 pairs.
36
+ - `spotMetaAndAssetCtxs` likewise returns `[spotMeta, spotCtxs]`;
37
+ spotCtxs rows carry `coin`, `markPx`, `midPx`, `dayNtlVlm`,
38
+ `prevDayPx`, `circulating`, `funding`.
39
+ - `l2Book` body `{"coin"}` -> `{"coin", "time", "levels": {"bids":
40
+ [{"px","sz","nSigFigs"}], "asks": [...]}}` - already aggregated to
41
+ significant figures by the venue.
42
+ - `candleSnapshot` body `{"coin", "interval", "startTime"}`; intervals
43
+ `1m,15m,1h,4h,1d,1w,1M`; rows oldest-first with `t/T` (ms), `o/c/h/l/v`
44
+ (STRINGS), `n` (int trade count).
45
+ - `recentTrades` body `{"coin"}` -> rows with `side` ('B'/'A'), `px`,
46
+ `sz`, `time` (ms), `hash`, **`users: [maker, taker]`** (both sides).
47
+ - `fundingHistory` body `{"coin", "startTime?", "endTime?"}` -> flat
48
+ `[{coin, fundingRate, premium, time}]`, hourly, newest-last.
49
+ - `clearinghouseState` body `{"user": address}` ->
50
+ `marginSummary {accountValue, totalNtlPos, totalRawUsd,
51
+ totalMarginUsed, withdrawable}`, `crossMaintenanceMarginUsed`,
52
+ `assetPositions [{type, position {coin, szi, leverage {type, value},
53
+ entryPx, positionValue, unrealizedPnl, returnOnEquity,
54
+ liquidationPx | null, marginUsed, maintMarginUsed, isolatedMargin}}]`.
55
+ **`liquidationPx` can be null** - the estimate path in
56
+ `liquidation_risk` exists for exactly that case.
57
+ - `userFills` -> `[{coin, dir, px, sz, time, closedPnl, fee, feeToken,
58
+ builderFee, hash, oid}]`.
59
+ - `userFunding` -> non-zero funding payments `[{coin, fundingRate,
60
+ premium, time, delta}]`; `delta` is the USD payment, negative when
61
+ the trader pays.
62
+ - `spotClearinghouseState` -> `{"balances": [{"coin": "@/PURR",
63
+ "hold", "total"}]}`.
64
+
65
+ ## HyperEVM RPC (POST https://rpc.hyperliquid.xyz/evm)
66
+
67
+ - Chain id **999** (0x3e7). Keyless public node, separate from /info
68
+ budgets: this gateway enforces its own **100 requests/min** cap
69
+ locally.
70
+ - `eth_getLogs` responses are capped (~1MB); active tokens need
71
+ roughly **30-60 block windows**. `evm.get_logs` adapts: start 30
72
+ blocks, halve on failure/too-big (floor 2), double on quiet success
73
+ (cap 60), max 14 slices.
74
+ - Transfer topic
75
+ `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef`;
76
+ `balanceOf(address)` selector `0x70a08231`; native balance via
77
+ `eth_getBalance`.
78
+ - Env override: `HL_EVM_RPC_URL`.
79
+
80
+ ## Endpoints NOT used, and why
81
+
82
+ - **info type `leaderboard`** - returns 403 behind Cloudflare bot
83
+ protection for non-browser clients; unreliable for a gateway.
84
+ - **Blockscout-style explorers** (`explorer.hyperliquid.xyz` API) - the
85
+ free tier was dead/unstable at verification time; all on-chain reads
86
+ go through the raw RPC instead.
87
+ - **WebSocket subscriptions** (`wss://api.hyperliquid.xyz/ws`) -
88
+ deferred to v0.2; the MCP request/response model gains little from
89
+ push feeds and the TTL caches already cover freshness.
90
+ - **Exchange/exchangeAction (signing) endpoints** - out of scope by
91
+ design: this gateway is strictly read-only and keyless; no private
92
+ keys ever enter the process.
@@ -0,0 +1,11 @@
1
+ """hyperliquid_mcp - read-only, keyless MCP gateway to Hyperliquid public data.
2
+
3
+ Package layout:
4
+ info.py stdlib-only POST client for https://api.hyperliquid.xyz/info
5
+ evm.py stdlib-only JSON-RPC client for https://rpc.hyperliquid.xyz/evm
6
+ server.py FastMCP server wiring the 12 read-only tools
7
+
8
+ No private keys, no signing, no order placement - public data only.
9
+ """
10
+
11
+ __version__ = "0.1.1"