Skip to main content
Glama
sergeyklay

poe2-mcp-server

by sergeyklay

poe2_item_price

Look up current market prices for Path of Exile 2 items from poe.ninja. Search by item name across exchange categories like Currency, Essences, Runes, and Fragments to get chaos-equivalent values and trade volumes.

Instructions

Look up the current market price of an item in Path of Exile 2 from poe.ninja.

Searches by partial name match across exchange categories (Currency, Fragments, Essences, Soul Cores, Idols, Runes, etc.).

Args:

  • name (string): Item name or partial name, e.g. "divine", "essence", "rune"

  • type (string): Exchange category to search. If omitted, searches all categories.

  • league (string): League name (default: "Fate of the Vaal")

Returns: Matching items with chaos-equivalent values and trade volumes.

Examples:

  • "How much is a Divine Orb?" → name="divine", type="Currency"

  • "Price of essences" → name="essence", type="Essences"

  • "Find rune prices" → name="rune", type="Runes"

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesItem name or partial match
typeNoExchange category to search. If omitted, searches all categories.
leagueNoPoE2 league nameFate of the Vaal

Implementation Reference

  • Main handler function for poe2_item_price tool. Accepts item name, type, and league parameters. Searches across exchange categories on poe.ninja API, matches items by partial name, calculates chaos-equivalent values, and returns formatted results with prices and volumes.
    async ({ name, type, league }) => {
      try {
        const typesToSearch = type ? [type] : [...EXCHANGE_TYPES];
        const query = name.toLowerCase();
        const results: Array<{
          name: string;
          type: string;
          chaos: number;
          volume: number;
        }> = [];
    
        for (const t of typesToSearch) {
          try {
            const data = await getNinjaExchangeOverview(league, t);
    
            const coreNames = new Map<string, string>();
            for (const item of data.core.items) {
              coreNames.set(item.id, item.name);
            }
    
            const chaosRate = data.core.rates[data.core.secondary] ?? 1;
    
            for (const line of data.lines) {
              const itemName = coreNames.get(line.id);
              const matchesQuery =
                line.id.toLowerCase().includes(query) ||
                (itemName?.toLowerCase().includes(query) ?? false);
    
              if (matchesQuery) {
                results.push({
                  name: displayName(line.id, coreNames),
                  type: t,
                  chaos: line.primaryValue * chaosRate,
                  volume: line.volumePrimaryValue ?? 0,
                });
              }
            }
          } catch {
            // Some categories may not be available, skip silently
          }
        }
    
        if (results.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: `No items found matching "${name}" in ${league}.\n\nTip: Try a shorter name. Available categories: ${EXCHANGE_TYPES.join(', ')}`,
              },
            ],
          };
        }
    
        results.sort((a, b) => b.chaos - a.chaos);
        const top = results.slice(0, 15);
    
        const lines: string[] = [
          `## Item Prices: "${name}" — ${league}`,
          `Found ${results.length} match(es)`,
          '',
        ];
        for (const r of top) {
          lines.push(`**${r.name}** [${r.type}]`);
          lines.push(`- Chaos: ${r.chaos.toFixed(1)} | Volume: ${r.volume}`);
          lines.push('');
        }
    
        if (results.length > 15) {
          lines.push(`... and ${results.length - 15} more results.`);
        }
    
        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: ${msg}` }],
        };
      }
    },
  • Input schema definition using Zod. Validates 'name' (required string), 'type' (optional enum from EXCHANGE_TYPES), and 'league' (string with default 'Fate of the Vaal').
    inputSchema: {
      name: z.string().min(1).describe('Item name or partial match'),
      type: z
        .enum(EXCHANGE_TYPES)
        .optional()
        .describe('Exchange category to search. If omitted, searches all categories.'),
      league: z.string().default(DEFAULT_LEAGUE).describe('PoE2 league name'),
    },
  • Tool registration with server.registerTool. Defines metadata including title, description, inputSchema, and annotations (readOnly, idempotent, openWorld). Binds the handler function to the 'poe2_item_price' tool name.
      server.registerTool(
        'poe2_item_price',
        {
          title: 'PoE2 Item Price Lookup',
          description: `Look up the current market price of an item in Path of Exile 2 from poe.ninja.
    
    Searches by partial name match across exchange categories (Currency, Fragments, Essences, Soul Cores, Idols, Runes, etc.).
    
    Args:
      - name (string): Item name or partial name, e.g. "divine", "essence", "rune"
      - type (string): Exchange category to search. If omitted, searches all categories.
      - league (string): League name (default: "Fate of the Vaal")
    
    Returns: Matching items with chaos-equivalent values and trade volumes.
    
    Examples:
      - "How much is a Divine Orb?" → name="divine", type="Currency"
      - "Price of essences" → name="essence", type="Essences"
      - "Find rune prices" → name="rune", type="Runes"`,
          inputSchema: {
            name: z.string().min(1).describe('Item name or partial match'),
            type: z
              .enum(EXCHANGE_TYPES)
              .optional()
              .describe('Exchange category to search. If omitted, searches all categories.'),
            league: z.string().default(DEFAULT_LEAGUE).describe('PoE2 league name'),
          },
          annotations: {
            readOnlyHint: true,
            destructiveHint: false,
            idempotentHint: true,
            openWorldHint: true,
          },
        },
  • getNinjaExchangeOverview function - API helper that fetches exchange overview data from poe.ninja for a given league and type. Includes rate limiting and error handling.
    export async function getNinjaExchangeOverview(
      league: string,
      type: string,
    ): Promise<NinjaExchangeResponse> {
      const url = `${NINJA_POE2_BASE}/exchange/current/overview?league=${encodeURIComponent(league)}&type=${encodeURIComponent(type)}`;
      return fetchJson<NinjaExchangeResponse>(url, ninjaLimiter);
    }
  • displayName utility function - converts item IDs to title-case display names using a mapping from core item data.
    function displayName(id: string, coreNames: Map<string, string>): string {
      return coreNames.get(id) ?? id.charAt(0).toUpperCase() + id.slice(1);
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sergeyklay/poe2-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server