list_markets
Discover all trading symbols on Hyperliquid and builder dexes with mark price, 24h volume, funding rate, open interest, and change.
Instructions
CANONICAL market discovery tool. Returns every trading symbol on Hyperliquid and its builder dexes with dex, mark price, 24h volume, funding rate, open interest, and 24h change. Use this whenever the user asks 'what markets are available?', mentions a commodity (gold, silver, oil), stock (TSLA, AAPL, NVDA), or builder-dex market. Prefer this over pulse_market_overview (same data, kept only for backward compat). For asset-level grouping across venues, use list_assets instead.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| useToonFormat | No | Return data in compact toon format (default: true). Set to false for standard JSON. | |
| dex | No | Filter by dex. 'hl' for native Hyperliquid, 'xyz' for commodities/stocks, 'cash' for equities, 'km' for energy, etc. Omit for all markets. |
Implementation Reference
- src/index.ts:269-283 (handler)The handler function for the list_markets tool. It registers the tool with the MCP server, accepts optional 'dex' filter (enum of hl, xyz, flx, vntl, hyna, km, abcd, cash) and optional 'useToonFormat', then calls the API endpoint /pulse/market-overview with the dex parameter.
if (shouldRegister("list_markets")) server.registerTool( "list_markets", { description: "CANONICAL market discovery tool. Returns every trading symbol on Hyperliquid and its builder dexes with dex, mark price, 24h volume, funding rate, open interest, and 24h change. Use this whenever the user asks 'what markets are available?', mentions a commodity (gold, silver, oil), stock (TSLA, AAPL, NVDA), or builder-dex market. Prefer this over pulse_market_overview (same data, kept only for backward compat). For asset-level grouping across venues, use list_assets instead.", inputSchema: { useToonFormat: useToonFormatSchema, dex: z.enum(["hl", "xyz", "flx", "vntl", "hyna", "km", "abcd", "cash"]).optional().describe("Filter by dex. 'hl' for native Hyperliquid, 'xyz' for commodities/stocks, 'cash' for equities, 'km' for energy, etc. Omit for all markets."), }, }, async ({ useToonFormat, dex }) => { const params: Record<string, string> = {}; if (dex) params.dex = dex; return toolResult(await callAPI(useToonFormat, "/pulse/market-overview", params)); } ); - src/index.ts:273-276 (schema)Input schema for list_markets: accepts optional 'useToonFormat' boolean (default true) and optional 'dex' enum filter for narrowing to a specific exchange.
inputSchema: { useToonFormat: useToonFormatSchema, dex: z.enum(["hl", "xyz", "flx", "vntl", "hyna", "km", "abcd", "cash"]).optional().describe("Filter by dex. 'hl' for native Hyperliquid, 'xyz' for commodities/stocks, 'cash' for equities, 'km' for energy, etc. Omit for all markets."), }, - src/index.ts:30-34 (registration)list_markets is registered as a free-tier tool (line 33), meaning it's available without an API key. It is in the FREE_TIER_TOOLS set.
const FREE_TIER_TOOLS = new Set([ 'pulse_global_stats', 'pulse_market_overview', 'list_markets', 'market_price', - src/index.ts:94-166 (helper)The callAPI helper function used by list_markets to make the HTTP request to the Coinversa API endpoint /pulse/market-overview. Handles retries, timeouts, and error formatting.
async function callAPI(useToon: boolean, path: string, params?: Record<string, string>): Promise<any> { const url = new URL(`${BASE}${path}`); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== "") { url.searchParams.set(key, value); } }); } let lastError: Error | null = null; for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); const headers: Record<string, string> = {}; if (API_KEY) headers["X-API-Key"] = API_KEY; const response = await fetch(url.toString(), { headers, signal: controller.signal, }); clearTimeout(timeout); if (response.status === 429) { // Rate limited — retry after delay if (attempt < MAX_RETRIES) { await new Promise((r) => setTimeout(r, RETRY_DELAY_MS * (attempt + 1))); continue; } throw new Error("Rate limit exceeded. Please wait a moment and try again."); } if (response.status === 404) { throw new Error("Not found. The requested resource does not exist — check the address or symbol."); } if (response.status === 401) { throw new Error("Invalid API key. Check your COINVERSAA_API_KEY environment variable."); } if (!response.ok) { const body = await response.json().catch(() => null); const msg = body?.error || response.statusText; throw new Error(`Request failed (${response.status}): ${msg}`); } const data = await response.json(); return useToon ? toonEncode(data) : data; } catch (err: any) { if (err.name === "AbortError") { lastError = new Error("Request timed out after 30 seconds. The server may be under heavy load — try again."); } else if (err.cause?.code === "ECONNREFUSED" || err.cause?.code === "ENOTFOUND") { lastError = new Error("Cannot connect to the Coinversa API. Check your COINVERSAA_API_URL setting and network connection."); } else { lastError = err; } // Retry on transient network errors if (attempt < MAX_RETRIES && (err.name === "AbortError" || err.cause?.code === "ECONNRESET")) { await new Promise((r) => setTimeout(r, RETRY_DELAY_MS * (attempt + 1))); continue; } throw lastError; } } throw lastError || new Error("Request failed after retries"); }