Holder Concentration
holder_concentrationAssess token holder concentration risk with Gini coefficient, top-10/top-50 percentages, and supply distribution buckets from on-chain data.
Instructions
Get Gini coefficient and distribution metrics for a token. Shows concentration risk, top-10/top-50 holder percentages, and supply distribution buckets. Cost: $0.02 per query. Source: On-chain token analytics.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| token | Yes | Token contract address | |
| chain | No | Blockchain network (default: ethereum) |
Implementation Reference
- src/tools/holders.ts:108-132 (handler)Handler function for holder_concentration tool. Calls GET /api/v1/holders/{token}/concentration and returns the distribution metrics JSON.
async ({ token, chain }) => { const res = await apiGet<{ dataset: string; data: Record<string, unknown> }>( `/api/v1/holders/${encodeURIComponent(token)}/concentration`, { chain }, ); if (!res.ok) { return { content: [ { type: "text" as const, text: `API error (${res.status}): ${JSON.stringify(res.data)}`, }, ], isError: true, }; } const warn = stalenessWarning(res); return { content: [ { type: "text" as const, text: `${warn}${JSON.stringify(res.data.data, null, 2)}` }, ], }; }, - src/tools/holders.ts:98-106 (schema)Input schema for holder_concentration: token (string, required) and chain (enum, optional, defaults to ethereum).
inputSchema: { token: z .string() .describe("Token contract address"), chain: z .enum(["ethereum", "arbitrum", "polygon", "base", "bsc"]) .optional() .describe("Blockchain network (default: ethereum)"), }, - src/tools/holders.ts:90-133 (registration)Registration of holder_concentration tool on the MCP server via server.registerTool().
server.registerTool( "holder_concentration", { title: "Holder Concentration", description: "Get Gini coefficient and distribution metrics for a token. Shows concentration " + "risk, top-10/top-50 holder percentages, and supply distribution buckets. " + "Cost: $0.02 per query. Source: On-chain token analytics.", inputSchema: { token: z .string() .describe("Token contract address"), chain: z .enum(["ethereum", "arbitrum", "polygon", "base", "bsc"]) .optional() .describe("Blockchain network (default: ethereum)"), }, }, async ({ token, chain }) => { const res = await apiGet<{ dataset: string; data: Record<string, unknown> }>( `/api/v1/holders/${encodeURIComponent(token)}/concentration`, { chain }, ); if (!res.ok) { return { content: [ { type: "text" as const, text: `API error (${res.status}): ${JSON.stringify(res.data)}`, }, ], isError: true, }; } const warn = stalenessWarning(res); return { content: [ { type: "text" as const, text: `${warn}${JSON.stringify(res.data.data, null, 2)}` }, ], }; }, ); - src/index.ts:51-51 (registration)Top-level registration call that wires registerHolderTools into the MCP server.
registerHolderTools(server); - src/client.ts:44-76 (helper)Helper HTTP client function used by the handler to fetch concentration data from the Verilex API.
export async function apiGet<T = unknown>( path: string, params?: Record<string, string | number | undefined>, ): Promise<ApiResponse<T>> { const url = buildUrl(path, params); const headers: Record<string, string> = { Accept: "application/json", "User-Agent": "verilex-mcp-server/0.1.0", }; // Forward x402 payment token if present in env (for paid endpoints) const paymentToken = process.env.VERILEX_PAYMENT_TOKEN; if (paymentToken) { headers["X-Payment-Token"] = paymentToken; } const res = await fetch(url, { headers }); const data = (await res.json()) as T; const stale = res.headers.get("X-Data-Stale"); const lastUpdated = res.headers.get("X-Data-Last-Updated"); const ageSeconds = res.headers.get("X-Data-Age-Seconds"); return { ok: res.ok, status: res.status, data, stale: stale === "true", lastUpdated: lastUpdated ?? undefined, ageSeconds: ageSeconds ? Number(ageSeconds) : undefined, }; }