poe2_meta_builds
Retrieve class distribution statistics and meta-build trends for Path of Exile 2 from poe.ninja, showing popular classes with percentage shares and trend directions among ladder characters.
Instructions
Get class distribution statistics for Path of Exile 2 from poe.ninja.
Shows the most popular classes with their percentage share and trend direction among indexed ladder characters.
Args:
league (string): League name (default: "Fate of the Vaal")
class_name (string): Optional — filter by class, e.g. "Witch", "Lich", "Sorceress"
Returns: Class distribution with percentages and trend indicators.
Examples:
"What's the current meta?" → call with defaults
"Most popular Witch builds?" → class_name="Witch"
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| league | No | PoE2 league name | Fate of the Vaal |
| class_name | No | Filter by class name, e.g. Witch, Lich, Warrior, Sorceress |
Implementation Reference
- src/tools/builds.ts:47-116 (handler)Main handler function for poe2_meta_builds tool. Fetches build data from poe.ninja API, filters by league and optional class name, formats class distribution statistics with percentages and trend indicators, and returns formatted markdown output.
async ({ league, class_name }) => { try { const data = await getNinjaBuildIndex(); const queryLower = league.toLowerCase(); // Find matching league by name (case-insensitive contains) or URL slug const entry: BuildLeagueEntry | undefined = data.leagueBuilds.find( (e) => e.leagueName.toLowerCase().includes(queryLower) || e.leagueUrl.toLowerCase() === queryLower, ); if (!entry) { const available = data.leagueBuilds.map((e) => `"${e.leagueName}"`).join(', '); return { content: [ { type: 'text', text: `League "${league}" not found. Available leagues: ${available}.`, }, ], }; } const { statistics, total } = entry; // Build parallel index arrays, optionally filtered by class_name let indices = statistics.class.map((_, i) => i); if (class_name) { const classQuery = class_name.toLowerCase(); indices = indices.filter((i) => statistics.class[i]!.toLowerCase().includes(classQuery)); } const lines: string[] = [ `## Meta Builds Overview — ${entry.leagueName}`, '', `Total indexed characters: ${total.toLocaleString()}`, '', '### Class Distribution', ]; if (indices.length === 0 && class_name) { lines.push( `No classes matching "${class_name}". Available classes: ${statistics.class.join(', ')}`, ); } else { for (const i of indices) { const className = statistics.class[i]!; const pct = statistics.percentage[i] ?? 0; const trend = statistics.trend[i] ?? 0; lines.push(`- **${className}**: ${pct}%${trendLabel(trend)}`); } } return { content: [{ type: 'text', text: lines.join('\n') }], }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); return { isError: true, content: [ { type: 'text', text: `Error fetching meta builds: ${msg}\n\nNote: poe.ninja build API may not be available for all leagues.`, }, ], }; } }, - src/tools/builds.ts:16-46 (schema)Tool registration schema defining input parameters: 'league' (string, defaults to 'Fate of the Vaal') and optional 'class_name' filter. Includes tool metadata, descriptions, and annotations (readOnly, idempotent, openWorld).
server.registerTool( 'poe2_meta_builds', { title: 'PoE2 Meta Build Overview', description: `Get class distribution statistics for Path of Exile 2 from poe.ninja. Shows the most popular classes with their percentage share and trend direction among indexed ladder characters. Args: - league (string): League name (default: "Fate of the Vaal") - class_name (string): Optional — filter by class, e.g. "Witch", "Lich", "Sorceress" Returns: Class distribution with percentages and trend indicators. Examples: - "What's the current meta?" → call with defaults - "Most popular Witch builds?" → class_name="Witch"`, inputSchema: { league: z.string().default(DEFAULT_LEAGUE).describe('PoE2 league name'), class_name: z .string() .optional() .describe('Filter by class name, e.g. Witch, Lich, Warrior, Sorceress'), }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, - src/tools/builds.ts:14-118 (registration)Complete tool registration function registerBuildTools that registers the poe2_meta_builds tool with the MCP server, including schema definition and handler function.
export function registerBuildTools(server: McpServer): void { // ── poe2_meta_builds ────────────────────────────────────────────── server.registerTool( 'poe2_meta_builds', { title: 'PoE2 Meta Build Overview', description: `Get class distribution statistics for Path of Exile 2 from poe.ninja. Shows the most popular classes with their percentage share and trend direction among indexed ladder characters. Args: - league (string): League name (default: "Fate of the Vaal") - class_name (string): Optional — filter by class, e.g. "Witch", "Lich", "Sorceress" Returns: Class distribution with percentages and trend indicators. Examples: - "What's the current meta?" → call with defaults - "Most popular Witch builds?" → class_name="Witch"`, inputSchema: { league: z.string().default(DEFAULT_LEAGUE).describe('PoE2 league name'), class_name: z .string() .optional() .describe('Filter by class name, e.g. Witch, Lich, Warrior, Sorceress'), }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, async ({ league, class_name }) => { try { const data = await getNinjaBuildIndex(); const queryLower = league.toLowerCase(); // Find matching league by name (case-insensitive contains) or URL slug const entry: BuildLeagueEntry | undefined = data.leagueBuilds.find( (e) => e.leagueName.toLowerCase().includes(queryLower) || e.leagueUrl.toLowerCase() === queryLower, ); if (!entry) { const available = data.leagueBuilds.map((e) => `"${e.leagueName}"`).join(', '); return { content: [ { type: 'text', text: `League "${league}" not found. Available leagues: ${available}.`, }, ], }; } const { statistics, total } = entry; // Build parallel index arrays, optionally filtered by class_name let indices = statistics.class.map((_, i) => i); if (class_name) { const classQuery = class_name.toLowerCase(); indices = indices.filter((i) => statistics.class[i]!.toLowerCase().includes(classQuery)); } const lines: string[] = [ `## Meta Builds Overview — ${entry.leagueName}`, '', `Total indexed characters: ${total.toLocaleString()}`, '', '### Class Distribution', ]; if (indices.length === 0 && class_name) { lines.push( `No classes matching "${class_name}". Available classes: ${statistics.class.join(', ')}`, ); } else { for (const i of indices) { const className = statistics.class[i]!; const pct = statistics.percentage[i] ?? 0; const trend = statistics.trend[i] ?? 0; lines.push(`- **${className}**: ${pct}%${trendLabel(trend)}`); } } return { content: [{ type: 'text', text: lines.join('\n') }], }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); return { isError: true, content: [ { type: 'text', text: `Error fetching meta builds: ${msg}\n\nNote: poe.ninja build API may not be available for all leagues.`, }, ], }; } }, ); } - src/services/api.ts:117-120 (helper)getNinjaBuildIndex function that fetches PoE2 build index state from poe.ninja API, returning class distribution statistics for all leagues.
export async function getNinjaBuildIndex(): Promise<BuildIndexStateResponse> { const url = 'https://poe.ninja/poe2/api/data/build-index-state'; return fetchJson<BuildIndexStateResponse>(url, ninjaLimiter); } - src/services/api.ts:80-96 (schema)Type definitions for BuildLeagueStatistics, BuildLeagueEntry, and BuildIndexStateResponse interfaces that define the structure of poe.ninja build API responses.
interface BuildLeagueStatistics { class: string[]; percentage: number[]; trend: number[]; } export interface BuildLeagueEntry { leagueName: string; leagueUrl: string; total: number; status: number; statistics: BuildLeagueStatistics; } export interface BuildIndexStateResponse { leagueBuilds: BuildLeagueEntry[]; }