Skip to main content
Glama
gregario

onepiece-oracle

analyze_cost_curve

Analyze One Piece TCG deck cost distribution to optimize card selection and resource management.

Instructions

Analyze the cost curve of a One Piece TCG deck list. Shows cost distribution, color breakdown, and card type spread.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
deck_listYesDeck list as text. Format: "4 Monkey.D.Luffy" or "4x OP01-001" per line.

Implementation Reference

  • The handler function for the 'analyze_cost_curve' tool, which takes a deck list string, resolves cards, calculates cost distribution, color/type breakdown, and counter values, and returns a formatted analysis.
      async ({ deck_list }) => {
        const entries = parseDeckList(deck_list);
        const allCards = getCards();
    
        const resolved: { card: Card; count: number }[] = [];
        const unresolved: string[] = [];
    
        for (const entry of entries) {
          const card = resolveCard(entry.name, allCards);
          if (card) {
            resolved.push({ card, count: entry.count });
          } else {
            unresolved.push(entry.name);
          }
        }
    
        if (resolved.length === 0) {
          return {
            isError: true,
            content: [
              {
                type: 'text' as const,
                text: 'Could not resolve any cards from the deck list. Use card numbers (e.g., "OP01-001") or exact card names.',
              },
            ],
          };
        }
    
        // Cost distribution
        const costDist = new Map<number, number>();
        let totalCards = 0;
        for (const { card, count } of resolved) {
          const cost = parseInt(card.cost, 10);
          if (!isNaN(cost)) {
            costDist.set(cost, (costDist.get(cost) || 0) + count);
          }
          totalCards += count;
        }
    
        // Color breakdown
        const colorDist = new Map<string, number>();
        for (const { card, count } of resolved) {
          for (const color of card.colors) {
            colorDist.set(color, (colorDist.get(color) || 0) + count);
          }
        }
    
        // Type breakdown
        const typeDist = new Map<string, number>();
        for (const { card, count } of resolved) {
          typeDist.set(card.card_type, (typeDist.get(card.card_type) || 0) + count);
        }
    
        // Counter cards
        let counterCards = 0;
        let totalCounter = 0;
        for (const { card, count } of resolved) {
          if (card.counter !== '-' && card.counter !== '') {
            counterCards += count;
            totalCounter += parseInt(card.counter, 10) * count;
          }
        }
    
        const lines: string[] = [
          `# Deck Analysis (${totalCards} cards)`,
          '',
        ];
    
        if (unresolved.length > 0) {
          lines.push('## Unresolved Cards');
          lines.push('');
          for (const name of unresolved) {
            lines.push(`- ~~${name}~~ *(not found)*`);
          }
          lines.push('');
        }
    
        // Cost curve
        lines.push('## Cost Curve');
        lines.push('');
        const maxCost = Math.max(...costDist.keys(), 0);
        for (let i = 0; i <= maxCost; i++) {
          const count = costDist.get(i) || 0;
          const bar = '█'.repeat(count);
          lines.push(`${i}: ${bar} ${count}`);
        }
    
        // Color breakdown
        lines.push('');
        lines.push('## Color Breakdown');
        lines.push('');
        for (const [color, count] of [...colorDist.entries()].sort((a, b) => b[1] - a[1])) {
          lines.push(`- **${color}**: ${count} cards`);
        }
    
        // Type breakdown
        lines.push('');
        lines.push('## Card Types');
        lines.push('');
        for (const [type, count] of [...typeDist.entries()].sort((a, b) => b[1] - a[1])) {
          lines.push(`- **${type}**: ${count}`);
        }
    
        // Counter summary
        lines.push('');
        lines.push('## Counter Summary');
        lines.push('');
        lines.push(`- **Cards with Counter:** ${counterCards}/${totalCards}`);
        lines.push(`- **Total Counter Value:** +${totalCounter}`);
    
        return {
          content: [{ type: 'text' as const, text: lines.join('\n') }],
        };
      },
    );
  • The 'registerAnalyzeCostCurve' function which registers the 'analyze_cost_curve' tool with the MCP server, defining its schema and handler.
    export function registerAnalyzeCostCurve(server: McpServer): void {
      server.registerTool(
        'analyze_cost_curve',
        {
          description:
            'Analyze the cost curve of a One Piece TCG deck list. Shows cost distribution, color breakdown, and card type spread.',
          inputSchema: {
            deck_list: z
              .string()
              .describe(
                'Deck list as text. Format: "4 Monkey.D.Luffy" or "4x OP01-001" per line.',
              ),
          },
        },
        async ({ deck_list }) => {
          const entries = parseDeckList(deck_list);
          const allCards = getCards();
    
          const resolved: { card: Card; count: number }[] = [];
          const unresolved: string[] = [];
    
          for (const entry of entries) {
            const card = resolveCard(entry.name, allCards);
            if (card) {
              resolved.push({ card, count: entry.count });
            } else {
              unresolved.push(entry.name);
            }
          }
    
          if (resolved.length === 0) {
            return {
              isError: true,
              content: [
                {
                  type: 'text' as const,
                  text: 'Could not resolve any cards from the deck list. Use card numbers (e.g., "OP01-001") or exact card names.',
                },
              ],
            };
          }
    
          // Cost distribution
          const costDist = new Map<number, number>();
          let totalCards = 0;
          for (const { card, count } of resolved) {
            const cost = parseInt(card.cost, 10);
            if (!isNaN(cost)) {
              costDist.set(cost, (costDist.get(cost) || 0) + count);
            }
            totalCards += count;
          }
    
          // Color breakdown
          const colorDist = new Map<string, number>();
          for (const { card, count } of resolved) {
            for (const color of card.colors) {
              colorDist.set(color, (colorDist.get(color) || 0) + count);
            }
          }
    
          // Type breakdown
          const typeDist = new Map<string, number>();
          for (const { card, count } of resolved) {
            typeDist.set(card.card_type, (typeDist.get(card.card_type) || 0) + count);
          }
    
          // Counter cards
          let counterCards = 0;
          let totalCounter = 0;
          for (const { card, count } of resolved) {
            if (card.counter !== '-' && card.counter !== '') {
              counterCards += count;
              totalCounter += parseInt(card.counter, 10) * count;
            }
          }
    
          const lines: string[] = [
            `# Deck Analysis (${totalCards} cards)`,
            '',
          ];
    
          if (unresolved.length > 0) {
            lines.push('## Unresolved Cards');
            lines.push('');
            for (const name of unresolved) {
              lines.push(`- ~~${name}~~ *(not found)*`);
            }
            lines.push('');
          }
    
          // Cost curve
          lines.push('## Cost Curve');
          lines.push('');
          const maxCost = Math.max(...costDist.keys(), 0);
          for (let i = 0; i <= maxCost; i++) {
            const count = costDist.get(i) || 0;
            const bar = '█'.repeat(count);
            lines.push(`${i}: ${bar} ${count}`);
          }
    
          // Color breakdown
          lines.push('');
          lines.push('## Color Breakdown');
          lines.push('');
          for (const [color, count] of [...colorDist.entries()].sort((a, b) => b[1] - a[1])) {
            lines.push(`- **${color}**: ${count} cards`);
          }
    
          // Type breakdown
          lines.push('');
          lines.push('## Card Types');
          lines.push('');
          for (const [type, count] of [...typeDist.entries()].sort((a, b) => b[1] - a[1])) {
            lines.push(`- **${type}**: ${count}`);
          }
    
          // Counter summary
          lines.push('');
          lines.push('## Counter Summary');
          lines.push('');
          lines.push(`- **Cards with Counter:** ${counterCards}/${totalCards}`);
          lines.push(`- **Total Counter Value:** +${totalCounter}`);
    
          return {
            content: [{ type: 'text' as const, text: lines.join('\n') }],
          };
        },
      );
    }
  • The input schema definition for the 'analyze_cost_curve' tool using Zod, expecting a 'deck_list' string.
    {
      description:
        'Analyze the cost curve of a One Piece TCG deck list. Shows cost distribution, color breakdown, and card type spread.',
      inputSchema: {
        deck_list: z
          .string()
          .describe(
            'Deck list as text. Format: "4 Monkey.D.Luffy" or "4x OP01-001" per line.',
          ),
      },
  • Helper function 'parseDeckList' to parse the raw text deck list input into structured card objects.
    function parseDeckList(input: string): { name: string; count: number }[] {
      const lines = input.split('\n').filter((l) => l.trim());
      const entries: { name: string; count: number }[] = [];
    
      for (const line of lines) {
        const match = line.match(/^(\d+)\s*[x×]?\s*(.+)$/i) || line.match(/^(.+?)\s*[x×]\s*(\d+)$/i);
        if (match) {
          const first = match[1].trim();
          const second = match[2].trim();
          // Determine which is the number
          if (/^\d+$/.test(first)) {
            entries.push({ count: parseInt(first, 10), name: second });
          } else {
            entries.push({ count: parseInt(second, 10), name: first });
          }
        } else {
          entries.push({ count: 1, name: line.trim() });
        }
      }
    
      return entries;
    }
  • Helper function 'resolveCard' used to match card names/numbers against the loaded card database.
    function resolveCard(name: string, allCards: Card[]): Card | undefined {
      const lower = name.toLowerCase();
      // Try exact card number match first
      const byNumber = allCards.find((c) => c.card_number.toLowerCase() === lower);
      if (byNumber) return byNumber;
      // Try exact name match
      const byName = allCards.find(
        (c) => c.card_name.toLowerCase() === lower && !c.is_alternate_art,
      );
      if (byName) return byName;
      // Try partial name match
      return allCards.find(
        (c) => c.card_name.toLowerCase().includes(lower) && !c.is_alternate_art,
      );
    }

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other Tools

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/gregario/onepiece-oracle'

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