Skip to main content
Glama
jlgrimes

Pokemon TCG Card Search MCP

by jlgrimes

pokemon-card-search

Search and filter Pokémon TCG cards by name, type, legality, HP, retreat cost, and other attributes to find specific cards or build optimized decks.

Instructions

Searches for Pokemon cards

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
attacksNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The attacks available to this Pokemon card. Use dot notation (.) to search nested fields (e.g., "set.id:sm1" for set ID, "attacks.name:Spelunk" for attack names). For example, "attacks.name:Spelunk" to find cards with a specific attack name. If no attack information is explicitly mentioned, omit this field entirely.
convertedRetreatCostNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. CRITICAL: Query exactly what was asked for. Do not try to be systematic. Do not try to find specific examples. Do not try to be methodical. Just make the exact query requested. The converted retreat cost for a given Pokemon card. If no converted retreat cost is explicitly mentioned, omit this field. If the user explicitly specifies "free retreat", set this to 0. Use ! for exact matching (e.g., "!value" to match only exact value). Use [ and ] for inclusive ranges (e.g., [1 TO 3] for values 1-3). Use { and } for exclusive ranges (e.g., {1 TO 3} for values more than 1 and less than 3). Use * for unbounded ranges (e.g., [* TO 100] for values up to 100, or [100 TO *] for values 100 or higher).
evolvesToNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The Pokemon this card evolves into. Use negative values with a "-" prefix to exclude values (e.g., ["-value"] to exclude value). Use ! for exact matching (e.g., "!value" to match only exact value). If no evolution information is explicitly mentioned, omit this field entirely.
hpNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The HP (Hit Points) of the Pokemon card. Use ! for exact matching (e.g., "!value" to match only exact value). Use [ and ] for inclusive ranges (e.g., [1 TO 3] for values 1-3). Use { and } for exclusive ranges (e.g., {1 TO 3} for values more than 1 and less than 3). Use * for unbounded ranges (e.g., [* TO 100] for values up to 100, or [100 TO *] for values 100 or higher). If no HP is explicitly mentioned, omit this field entirely.
legalitiesNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. CRITICAL: Query exactly what was asked for. Do not try to be systematic. Do not try to find specific examples. Do not try to be methodical. Just make the exact query requested. The legalities for a given card. For each legality passed in, the value is "legal" without quotes. Use negative values with a "-" prefix to exclude values (e.g., ["-value"] to exclude value). Use ! for exact matching (e.g., "!value" to match only exact value). Use dot notation (.) to search nested fields (e.g., "set.id:sm1" for set ID, "attacks.name:Spelunk" for attack names). For example, "legalities.standard:banned" to find cards banned in Standard. If no legalities are explicitly mentioned, omit this field entirely.
nameNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. CRITICAL: Query exactly what was asked for. Do not try to be systematic. Do not try to find specific examples. Do not try to be methodical. Just make the exact query requested. IMPORTANT: For hyphenated names like "chien-pao", you MUST preserve the hyphen exactly as it appears. For example, "chien-pao ex" should have name "chien-pao" (with the hyphen) and subtypes ["EX"]. Never remove or modify hyphens in the name. Use * for wildcard matching (e.g., "char*" to match all cards starting with "char", or "char*der" to match cards starting with "char" and ending with "der"). Use ! for exact matching (e.g., "!value" to match only exact value). IMPORTANT: If no name is explicitly provided in the query, do not include a name field at all.
nationalPokedexNumbersNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The National Pokedex numbers of the Pokemon. Use ! for exact matching (e.g., "!value" to match only exact value). Use [ and ] for inclusive ranges (e.g., [1 TO 3] for values 1-3). Use { and } for exclusive ranges (e.g., {1 TO 3} for values more than 1 and less than 3). Use * for unbounded ranges (e.g., [* TO 100] for values up to 100, or [100 TO *] for values 100 or higher). If no Pokedex numbers are explicitly mentioned, omit this field entirely.
pageNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The page number for pagination. Use ! for exact matching (e.g., "!value" to match only exact value). Use [ and ] for inclusive ranges (e.g., [1 TO 3] for values 1-3). Use { and } for exclusive ranges (e.g., {1 TO 3} for values more than 1 and less than 3). Use * for unbounded ranges (e.g., [* TO 100] for values up to 100, or [100 TO *] for values 100 or higher). If no page is explicitly mentioned, omit this field entirely.
pageSizeNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The number of cards per page. Use ! for exact matching (e.g., "!value" to match only exact value). Use [ and ] for inclusive ranges (e.g., [1 TO 3] for values 1-3). Use { and } for exclusive ranges (e.g., {1 TO 3} for values more than 1 and less than 3). Use * for unbounded ranges (e.g., [* TO 100] for values up to 100, or [100 TO *] for values 100 or higher). If no page size is explicitly mentioned, omit this field entirely.
regulationMarkNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The regulation mark (also known as "block") of the card (e.g., "F", "G", "H"). This indicates which regulation block the card belongs to. Use negative values with a "-" prefix to exclude values (e.g., ["-value"] to exclude value). Use ! for exact matching (e.g., "!value" to match only exact value). If no regulation mark is explicitly mentioned, omit this field entirely.
setNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The set information for this card. Use dot notation (.) to search nested fields (e.g., "set.id:sm1" for set ID, "attacks.name:Spelunk" for attack names). For example, "set.id:sm1" to find cards from a specific set. If no set information is explicitly mentioned, omit this field entirely.
subtypesNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. CRITICAL: Query exactly what was asked for. Do not try to be systematic. Do not try to find specific examples. Do not try to be methodical. Just make the exact query requested. For example, "chien pao ex" should have name "chien pao" and subtypes ["EX"]. If multiple subtypes are present like "basic pikachu ex", use ["Basic", "EX"]. Use negative values with a "-" prefix to exclude values (e.g., ["-value"] to exclude value). Use ! for exact matching (e.g., "!value" to match only exact value). If no subtypes are explicitly mentioned in the query, omit this field entirely.
typesNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The types of the Pokemon card (e.g., ["Grass", "Psychic"]). Use negative values with a "-" prefix to exclude values (e.g., ["-value"] to exclude value). Use ! for exact matching (e.g., "!value" to match only exact value). If no types are explicitly mentioned, omit this field entirely.
weaknessesNoCRITICAL: Never infer or guess values. Never provide default values. Never make assumptions about what the user might want. Never try to be systematic or methodical. Never try to find "specific examples". Just query exactly what was asked for, nothing more and nothing less. The weaknesses of this Pokemon card. Use negative values with a "-" prefix to exclude values (e.g., ["-value"] to exclude value). Use ! for exact matching (e.g., "!value" to match only exact value). Use dot notation (.) to search nested fields (e.g., "set.id:sm1" for set ID, "attacks.name:Spelunk" for attack names). For example, "weaknesses.type:Water" to find cards weak to Water. If no weakness information is explicitly mentioned, omit this field entirely.

Implementation Reference

  • The async handler function that processes the input parameters, builds the search query, handles negative type filters, calls the ptcg_search API, and returns the result as JSON.
    async ({
      name,
      subtypes,
      legalities,
      convertedRetreatCost,
      hp,
      types,
      evolvesTo,
      attacks,
      weaknesses,
      set,
      nationalPokedexNumbers,
      page,
      pageSize,
      regulationMark,
    }) => {
      // Split types into positive and negative filters
      const positiveTypes = types?.filter(t => !t.startsWith('-')) || [];
      const negativeTypes =
        types?.filter(t => t.startsWith('-')).map(t => t.slice(1)) || [];
    
      let query = buildQuery(
        name,
        subtypes,
        legalities,
        hp,
        positiveTypes,
        evolvesTo,
        convertedRetreatCost,
        nationalPokedexNumbers,
        page,
        pageSize,
        set,
        attacks,
        weaknesses,
        regulationMark
      );
    
      // Add negative type filters
      if (negativeTypes.length > 0) {
        const negativeQuery = buildQuery(
          undefined,
          undefined,
          undefined,
          undefined,
          negativeTypes,
          undefined,
          undefined,
          undefined,
          undefined,
          undefined,
          undefined,
          undefined,
          undefined
        );
        query = `${query} ${negativeQuery}`;
      }
    
      const result = await ptcg_search(query);
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result),
          },
        ],
      };
    }
  • The Zod input schema for the pokemon-card-search tool, defining optional parameters such as name, subtypes, legalities, hp, types, and more, each with detailed descriptions.
    {
      name: z.string().optional().describe(FIELD_DESCRIPTIONS_SPECIFIC.NAME),
      subtypes: z
        .array(
          z.enum([
            'BREAK',
            'Baby',
            'Basic',
            'EX',
            'GX',
            'Goldenrod Game Corner',
            'Item',
            'LEGEND',
            'Level-Up',
            'MEGA',
            'Pokémon Tool',
            'Pokémon Tool F',
            'Rapid Strike',
            'Restored',
            "Rocket's Secret Machine",
            'Single Strike',
            'Special',
            'Stadium',
            'Stage 1',
            'Stage 2',
            'Supporter',
            'TAG TEAM',
            'Technical Machine',
            'V',
            'VMAX',
            'VSTAR',
            'Tera',
          ])
        )
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.SUBTYPES),
      legalities: z
        .object({
          standard: z.enum(['legal', 'banned']).optional(),
          expanded: z.enum(['legal', 'banned']).optional(),
          unlimited: z.enum(['legal', 'banned']).optional(),
        })
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.LEGALITIES),
      convertedRetreatCost: z
        .number()
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.CONVERTED_RETREAT_COST),
      hp: z.string().optional().describe(FIELD_DESCRIPTIONS_SPECIFIC.HP),
      nationalPokedexNumbers: z
        .string()
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.NATIONAL_POKEDEX_NUMBERS),
      page: z.number().optional().describe(FIELD_DESCRIPTIONS_SPECIFIC.PAGE),
      pageSize: z
        .number()
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.PAGE_SIZE),
      types: z
        .array(z.string())
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.TYPES),
      evolvesTo: z
        .array(z.string())
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.EVOLVES_TO),
      attacks: z
        .array(
          z.object({
            name: z.string(),
            cost: z.array(z.string()),
            damage: z.string(),
            text: z.string(),
          })
        )
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.ATTACKS),
      weaknesses: z
        .array(
          z.object({
            type: z.string(),
            value: z.string(),
          })
        )
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.WEAKNESSES),
      set: z
        .object({
          name: z.string(),
          series: z.string(),
        })
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.SET),
      regulationMark: z
        .string()
        .optional()
        .describe(FIELD_DESCRIPTIONS_SPECIFIC.REGULATION_MARK),
    },
  • index.ts:175-344 (registration)
    The server.tool registration call for the 'pokemon-card-search' tool, specifying name, description, input schema, and handler function.
    server.tool(
      'pokemon-card-search',
      'Searches for Pokemon cards',
      {
        name: z.string().optional().describe(FIELD_DESCRIPTIONS_SPECIFIC.NAME),
        subtypes: z
          .array(
            z.enum([
              'BREAK',
              'Baby',
              'Basic',
              'EX',
              'GX',
              'Goldenrod Game Corner',
              'Item',
              'LEGEND',
              'Level-Up',
              'MEGA',
              'Pokémon Tool',
              'Pokémon Tool F',
              'Rapid Strike',
              'Restored',
              "Rocket's Secret Machine",
              'Single Strike',
              'Special',
              'Stadium',
              'Stage 1',
              'Stage 2',
              'Supporter',
              'TAG TEAM',
              'Technical Machine',
              'V',
              'VMAX',
              'VSTAR',
              'Tera',
            ])
          )
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.SUBTYPES),
        legalities: z
          .object({
            standard: z.enum(['legal', 'banned']).optional(),
            expanded: z.enum(['legal', 'banned']).optional(),
            unlimited: z.enum(['legal', 'banned']).optional(),
          })
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.LEGALITIES),
        convertedRetreatCost: z
          .number()
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.CONVERTED_RETREAT_COST),
        hp: z.string().optional().describe(FIELD_DESCRIPTIONS_SPECIFIC.HP),
        nationalPokedexNumbers: z
          .string()
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.NATIONAL_POKEDEX_NUMBERS),
        page: z.number().optional().describe(FIELD_DESCRIPTIONS_SPECIFIC.PAGE),
        pageSize: z
          .number()
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.PAGE_SIZE),
        types: z
          .array(z.string())
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.TYPES),
        evolvesTo: z
          .array(z.string())
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.EVOLVES_TO),
        attacks: z
          .array(
            z.object({
              name: z.string(),
              cost: z.array(z.string()),
              damage: z.string(),
              text: z.string(),
            })
          )
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.ATTACKS),
        weaknesses: z
          .array(
            z.object({
              type: z.string(),
              value: z.string(),
            })
          )
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.WEAKNESSES),
        set: z
          .object({
            name: z.string(),
            series: z.string(),
          })
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.SET),
        regulationMark: z
          .string()
          .optional()
          .describe(FIELD_DESCRIPTIONS_SPECIFIC.REGULATION_MARK),
      },
      async ({
        name,
        subtypes,
        legalities,
        convertedRetreatCost,
        hp,
        types,
        evolvesTo,
        attacks,
        weaknesses,
        set,
        nationalPokedexNumbers,
        page,
        pageSize,
        regulationMark,
      }) => {
        // Split types into positive and negative filters
        const positiveTypes = types?.filter(t => !t.startsWith('-')) || [];
        const negativeTypes =
          types?.filter(t => t.startsWith('-')).map(t => t.slice(1)) || [];
    
        let query = buildQuery(
          name,
          subtypes,
          legalities,
          hp,
          positiveTypes,
          evolvesTo,
          convertedRetreatCost,
          nationalPokedexNumbers,
          page,
          pageSize,
          set,
          attacks,
          weaknesses,
          regulationMark
        );
    
        // Add negative type filters
        if (negativeTypes.length > 0) {
          const negativeQuery = buildQuery(
            undefined,
            undefined,
            undefined,
            undefined,
            negativeTypes,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined,
            undefined
          );
          query = `${query} ${negativeQuery}`;
        }
    
        const result = await ptcg_search(query);
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(result),
            },
          ],
        };
      }
    );
  • Helper function buildQuery that constructs the search query string for the Pokemon TCG API based on the provided parameters, handling filters, ranges, nested fields, etc.
    function buildQuery(
      name?: string,
      subtypes?: string[],
      legalities?:
        | {
            standard?: 'legal' | 'banned';
            expanded?: 'legal' | 'banned';
            unlimited?: 'legal' | 'banned';
          }
        | string,
      hp?: string,
      types?: string[],
      evolvesTo?: string[],
      convertedRetreatCost?: number,
      nationalPokedexNumbers?: string,
      page?: number | string,
      pageSize?: number | string,
      set?: { id?: string; name?: string; series?: string } | string,
      attacks?:
        | Array<{ name?: string; cost?: string[]; damage?: string; text?: string }>
        | string,
      weaknesses?: Array<{ type?: string; value?: string }> | string,
      regulationMark?: string
    ): string {
      const parts: string[] = [];
    
      // Handle name with special cases
      if (name) {
        if (name.startsWith('!') || name.includes('*')) {
          parts.push(`name:${name}`);
        } else {
          parts.push(`name:"${name}"`);
        }
      }
    
      // Generic filter handler for arrays
      function addFilter(
        values: string[] | undefined,
        field: string,
        negative = false
      ) {
        if (!values?.length) return;
    
        const query = values
          .map(value => {
            if (value.includes('.')) return value;
            if (value.startsWith('!')) return `${field}:${value}`;
            if (negative) return `-${field}:${value}`;
            return `${field}:${value}`;
          })
          .join(' OR ');
    
        parts.push(values.length === 1 ? query : `(${query})`);
      }
    
      // Generic nested filter handler
      function addNestedFilter(value: string | object | undefined, field: string) {
        if (!value) return;
    
        if (typeof value === 'string') {
          parts.push(value.includes('.') ? value : `${field}:${value}`);
        } else {
          Object.entries(value).forEach(([key, val]) => {
            if (val !== undefined) {
              parts.push(`${field}.${key}:${val}`);
            }
          });
        }
      }
    
      // Generic range filter handler
      function addRangeFilter(value: string | number | undefined, field: string) {
        if (!value) return;
    
        const strValue = String(value);
        if (
          strValue.startsWith('[') ||
          strValue.startsWith('{') ||
          strValue.startsWith('!')
        ) {
          parts.push(`${field}:${strValue}`);
        } else {
          parts.push(`${field}:${value}`);
        }
      }
    
      // Add all filters
      addFilter(subtypes, 'subtypes');
      addNestedFilter(legalities, 'legalities');
      addFilter(types, 'types');
      addFilter(evolvesTo, 'evolvesTo');
    
      addRangeFilter(hp, 'hp');
      addRangeFilter(convertedRetreatCost, 'convertedRetreatCost');
      addRangeFilter(nationalPokedexNumbers, 'nationalPokedexNumbers');
      addRangeFilter(page, 'page');
      addRangeFilter(pageSize, 'pageSize');
    
      addNestedFilter(set, 'set');
      addNestedFilter(attacks, 'attacks');
      addNestedFilter(weaknesses, 'weaknesses');
    
      // Add regulation mark filter
      if (regulationMark) {
        parts.push(`regulationMark:${regulationMark}`);
      }
    
      return parts.join(' ');
    }
  • Helper function ptcg_search that fetches card data from the Pokemon TCG API using the constructed query.
    async function ptcg_search(query: string): Promise<PtcgResponse> {
      const response = await fetch(
        `https://api.pokemontcg.io/v2/cards?q=${encodeURIComponent(query)}`
      );
      return response.json() as Promise<PtcgResponse>;
    }
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but offers no behavioral information. It doesn't disclose whether this is a read-only operation, if it requires authentication, rate limits, pagination behavior, or what the output looks like. For a search tool with 14 parameters, this lack of transparency is critical.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at three words, with zero wasted text. It is front-loaded and efficiently states the core function without unnecessary elaboration, though this brevity contributes to other deficiencies.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (14 parameters, nested objects, no output schema, no annotations), the description is completely inadequate. It fails to explain the search scope, result format, pagination, or how parameters interact. For a rich search tool, this minimal description leaves the agent with insufficient context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all 14 parameters with detailed descriptions. The tool description adds no parameter information beyond what's in the schema, meeting the baseline score of 3 for high schema coverage without additional value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Searches for Pokemon cards' restates the tool name with minimal elaboration. It specifies the verb ('Searches') and resource ('Pokemon cards'), but lacks detail on scope, filtering capabilities, or how it differs from the sibling tool 'pokemon-card-price'. This is a tautology that provides no meaningful distinction.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives. The description does not mention the sibling tool 'pokemon-card-price' or any context for choosing between search and price lookup. There is no indication of prerequisites, typical use cases, or limitations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related 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/jlgrimes/ptcg-mcp'

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