Skip to main content
Glama

get_card

Retrieve complete details for any Magic: The Gathering card, including oracle text, mana cost, type, rulings, and format legality. Supports fuzzy name matching.

Instructions

Get complete details for a specific Magic card including oracle text, mana cost, type, power/toughness, rulings, and format legality. Use this when you know the exact card name (or close to it) and need full information. Supports fuzzy matching — partial names work.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesCard name to look up

Implementation Reference

  • The main handler function that executes the 'get_card' tool logic. It takes a database connection and params (card name), performs exact match then fuzzy LIKE fallback, fetches faces/rulings/legalities/price data, and returns a structured GetCardResult.
    export function handler(db: Database.Database, params: GetCardParams): GetCardResult {
      // 1. Exact match (case-insensitive)
      let card = db.prepare(
        'SELECT * FROM cards WHERE LOWER(name) = LOWER(?)'
      ).get(params.name) as CardRow | undefined;
    
      // 2. Fuzzy fallback via LIKE
      if (!card) {
        card = db.prepare(
          'SELECT * FROM cards WHERE LOWER(name) LIKE LOWER(?)'
        ).get(`%${params.name}%`) as CardRow | undefined;
      }
    
      if (!card) {
        // Try to find suggestions
        const suggestions = db.prepare(
          'SELECT name FROM cards WHERE LOWER(name) LIKE LOWER(?) LIMIT 5'
        ).all(`%${params.name.split(' ')[0]}%`) as Array<{ name: string }>;
    
        return {
          found: false,
          message: `No card found matching "${params.name}"`,
          suggestions: suggestions.length > 0 ? suggestions.map(s => s.name) : undefined,
        };
      }
    
      // Fetch faces
      const faces = db.prepare(
        'SELECT * FROM card_faces WHERE card_id = ? ORDER BY face_index'
      ).all(card.id) as CardFaceRow[];
    
      // Fetch rulings
      const rulings = db.prepare(
        'SELECT * FROM rulings WHERE card_id = ? ORDER BY published_at'
      ).all(card.id) as RulingRow[];
    
      // Fetch legalities
      const legalityRows = db.prepare(
        'SELECT * FROM legalities WHERE card_id = ?'
      ).all(card.id) as LegalityRow[];
    
      const legalities: Record<string, string> = {};
      for (const row of legalityRows) {
        legalities[row.format] = row.status;
      }
    
      const cardDetail: CardDetail = {
        id: card.id,
        name: card.name,
        mana_cost: card.mana_cost,
        cmc: card.cmc,
        type_line: card.type_line,
        oracle_text: card.oracle_text,
        power: card.power,
        toughness: card.toughness,
        loyalty: card.loyalty,
        colors: card.colors ? JSON.parse(card.colors) as string[] : [],
        color_identity: card.color_identity ? JSON.parse(card.color_identity) as string[] : [],
        keywords: card.keywords ? JSON.parse(card.keywords) as string[] : [],
        rarity: card.rarity,
        set_code: card.set_code,
        set_name: card.set_name,
        released_at: card.released_at,
        image_uri: card.image_uri,
        scryfall_uri: card.scryfall_uri,
        edhrec_rank: card.edhrec_rank,
        artist: card.artist,
        faces: faces.map(f => ({
          face_index: f.face_index,
          name: f.name,
          mana_cost: f.mana_cost,
          type_line: f.type_line,
          oracle_text: f.oracle_text,
          power: f.power,
          toughness: f.toughness,
          colors: f.colors ? JSON.parse(f.colors) as string[] : [],
        })),
        rulings: rulings.map(r => ({
          source: r.source,
          published_at: r.published_at,
          comment: r.comment,
        })),
        legalities,
        price_usd: card.price_usd,
        price_usd_foil: card.price_usd_foil,
        price_eur: card.price_eur,
        price_eur_foil: card.price_eur_foil,
        price_tix: card.price_tix,
      };
    
      return { found: true, card: cardDetail };
    }
  • Input schema (GetCardInput) and type (GetCardParams) using Zod – defines the required 'name' string parameter.
    export const GetCardInput = z.object({
      name: z.string().describe('Card name to look up'),
    });
    
    export type GetCardParams = z.infer<typeof GetCardInput>;
  • Output type definitions including CardDetail, CardFaceDetail, RulingDetail, and GetCardResult (discriminated union on 'found').
    export interface CardDetail {
      id: string;
      name: string;
      mana_cost: string | null;
      cmc: number | null;
      type_line: string | null;
      oracle_text: string | null;
      power: string | null;
      toughness: string | null;
      loyalty: string | null;
      colors: string[];
      color_identity: string[];
      keywords: string[];
      rarity: string | null;
      set_code: string | null;
      set_name: string | null;
      released_at: string | null;
      image_uri: string | null;
      scryfall_uri: string | null;
      edhrec_rank: number | null;
      artist: string | null;
      faces: CardFaceDetail[];
      rulings: RulingDetail[];
      legalities: Record<string, string>;
      price_usd: number | null;
      price_usd_foil: number | null;
      price_eur: number | null;
      price_eur_foil: number | null;
      price_tix: number | null;
    }
    
    export interface CardFaceDetail {
      face_index: number;
      name: string;
      mana_cost: string | null;
      type_line: string | null;
      oracle_text: string | null;
      power: string | null;
      toughness: string | null;
      colors: string[];
    }
    
    export interface RulingDetail {
      source: string | null;
      published_at: string | null;
      comment: string;
    }
    
    export type GetCardResult = {
      found: true;
      card: CardDetail;
    } | {
      found: false;
      message: string;
      suggestions?: string[];
    }
  • src/server.ts:93-105 (registration)
    Registration of the 'get_card' tool with the MCP server using server.tool(), linking the schema (GetCardInput.shape) and the handler.
    server.tool(
      'get_card',
      'Get complete details for a specific Magic card including oracle text, mana cost, type, power/toughness, rulings, and format legality. Use this when you know the exact card name (or close to it) and need full information. Supports fuzzy matching — partial names work.',
      GetCardInput.shape,
      async (params) => {
        try {
          const result = getCardHandler(db, params);
          return { content: [{ type: 'text' as const, text: formatGetCard(result) }] };
        } catch (err) {
          return { content: [{ type: 'text' as const, text: `Error getting card: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
        }
      },
    );
  • The formatGetCard function that converts the raw GetCardResult into formatted text output for the LLM, delegating to formatCardDetail.
    export function formatGetCard(result: GetCardResult): string {
      if (!result.found) {
        let text = result.message;
        if (result.suggestions && result.suggestions.length > 0) {
          text += `\n\nDid you mean: ${result.suggestions.join(', ')}?`;
        }
        return text;
      }
    
      const card = result.card;
      return formatCardDetail(card);
    }
Behavior4/5

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

Discloses fuzzy matching behavior and lists returned fields. No annotations provided, but description implies read-only operation; could explicitly state no side effects.

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?

Two sentences efficiently convey purpose, usage, and parameter behavior with no redundancy.

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

Completeness5/5

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

Despite no output schema, description enumerates returned fields (oracle text, mana cost, type, power/toughness, rulings, format legality), covering agent needs fully.

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

Parameters5/5

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

Adds value beyond schema by noting fuzzy matching and partial name support for the 'name' parameter, which schema only labels as 'Card name to look up'.

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

Purpose5/5

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

Description specifies verb (get complete details) and resource (Magic card), listing specific fields returned. Distinguishes from sibling search_cards by focusing on full details for a known name.

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

Usage Guidelines4/5

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

Describes when to use (know exact or partial name, need full info) and mentions fuzzy matching. Lacks explicit alternatives but context is clear.

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

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/mtg-oracle'

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