Kasper Grøntved Front page →

mtg-search · agentic retrieval · Magic: The Gathering

Does a search engine help a deck-building agent?

MAGIC! No, not that kind of magic: the Gathering. I gave an AI agent a search engine over every card, and gave it tools to validate deck building.

270×

fewer tokens per task than reading every card: 10.6k vs 2.91M. The card text does not fit in a 1M-token context.

98% vs 87%

relevant cards in the top 10, search vs grep with the same instructions, with 26% fewer tokens.

0.45 → 0.65

deck fit of the agent's card adds, from memory vs with search, graded blind on 14 test decks.

I recently got into Magic: The Gathering, and without knowing it signed up for a learning curve of more than 34,000 unique cards. Yes, over 34,000. Once I got the taste for it, I started looking for cards that might fit my decks, and I quickly turned to an LLM and a handful of websites full of card data to get me up to speed.

The first wall I hit was the rules. The LLM could not reliably check that the cards it wanted to add were allowed in the deck: wrong colours, cards that are not legal, cards already in the list. The second wall was taste. It kept reaching for the same popular, mainstream cards everyone already plays. So I started building structure around it: a database of every card's official text, tools the agent can call to check its picks, and a search over the text. Then the ball started rolling, and this post is where it rolled to.

01 · The dataEvery Magic card's official text: 3.1M tokens, too much to read for every task

Everything here runs on Scryfall's Oracle cards data. Scryfall is the community's card database, and it publishes a fresh copy every day. The Oracle file has one entry per distinct card, whatever the printing, with the card's current official rules text: the Oracle text that Wizards of the Coast keeps up to date. That text can differ from the printed card: older printings of Lightning Bolt say “target creature or player”, while the Oracle text says “any target”.

Each entry also carries the mana cost, type line, colour identity and legality in every format. Scryfall's Tagger project adds community labels for what cards do; the tools show them as roles.

Cards
34,646one per distinct card; tokens, emblems and art cards dropped
Rules text
3.1M tokensabout 7.5M characters across all cards
Function tags
4,577Tagger labels such as mana-rock and sweeper
Rulings
78,973official clarifications, shown by get_card
Lightning Bolt from Double Masters 2022, with coloured boxes around its name, mana cost, type line, set symbol and rules text, and dashed boxes around the art and the flavour text.

What search_cards returns for it

1. Lightning Bolt · {R} (MV 1) · Red · Instant · uncommon Lightning Bolt deals 3 damage to any target. roles: burn-any legal: commander ✓
  • name
  • mana cost, and the colour and mana value it gives
  • type line
  • rarity, from the set symbol's colour
  • rules text, as the current Oracle wording
  • art and flavour text: never sent
  • not on the card: search rank, Tagger roles, format legality
Lightning Bolt as printed in Double Masters 2022, and the search result the agent gets for it. The agent reads text only. Card image: Scryfall, © Wizards of the Coast.

The simplest design would skip search entirely and give the agent every card to read. I tried that.

Tokens, on one linear scale. To read everything, the agent went through the cards in 5 parts of at most 1.8M characters, then made one more call to rank the candidates from all parts. That cost 2.91M tokens and 54 s per task, against 10.6k tokens and 10 s with search.

On a 12-task sample, reading everything gave slightly better answers than search (not statistically significant). The full 113 tasks would have read about 302M tokens, so I stopped there.

Reading everything is a ceiling, not a design. The agent needs a way to find cards: a search engine, or at least grep over the text.

02 · How the test sets are builtThree test sets, and two graders

363queries

Search benchmark

Which engine ranks the right cards first?

Built from
  • 65Tagger function tags phrased as a player would ask
  • 46handwritten rules-text queries
  • 222rewordings with no rules-text words
  • 30open-ended deck needs
Correct cards
The cards carrying the tag, or matching a rules-text pattern that was checked against Scryfall's own search. The top answers to each deck need were reviewed by hand.

113agent tasks

Agent search tasks

Does an agent with search find more relevant cards than one with only grep?

Built from
Requests from the benchmark:
  • 65deck needs
  • 15card attributes, such as a 1-mana red burn instant
  • 33specific cards to find
Protocol
Up to 5 tool calls, then a ranked list of up to 20 cards.
Scored by
Share of the top 10 that is relevant: on the benchmark's list of correct cards, or graded relevant by the relevance grader.

183precon decks

Precon decks

Does the agent add, cut and swap better cards in real Commander decks?

Built from
Preconstructed decks, the ready-made Commander decks Wizards sells (decklists from MTGJSON):
  • 14test decks, released after 1 Nov 2025
  • 15tuning decks, released earlier
Protocol
The agent sees all 100 cards and proposes 20 adds, 10 cuts or 10 swaps.
Scored by
Player add rates from EDHREC, which pools thousands of players' decklists: the share of players' copies of each deck that add or cut each card. And the deck-fit grader, for adds.

Each grader is a separate Claude model that grades cards one batch at a time, 15 cards per call, in random order. It never sees which agent proposed a card, and it gets no popularity or price data. Hidden random cards are mixed into the batches, to check that the grader does not accept everything.

Relevance grader

for the agent search tasks

Sees
The request with its constraints (colour, card type, mana value, format), and each card's rules text
Grades
  1. 2does what was asked and meets every constraint
  2. 1a weaker, narrower or indirect version a player might still consider
  3. 0does not do it, or breaks a constraint
Relevant
Grade 1 or 2
Check
It accepted 0.6% of hidden random cards that met the constraints

Deck-fit grader

for the precon decks

Sees
The deck (the commander's rules text with its name hidden, plus the other 99 card names) and each proposed add's rules text
Grades
  1. 2a clear upgrade: supports the commander or the deck's plan, or fills a role the deck is short of (ramp, card draw, removal, interaction), and beats the deck's weakest cards
  2. 1a reasonable but low-priority upgrade
  3. 0no specific reason to be in this deck, or breaks a deck rule
It is told not to reward a card for being widely played.
Deck-fit score
Mean grade of the 20 adds, scaled 0 to 1; adds that break a deck rule count 0
Check
It accepted 12% of hidden random legal cards and none of the cards already in the deck

Agents and graders are Claude Sonnet 5.5 in Claude Code, with no web, file or shell access: only the tools under test. No list of correct cards and no popularity or price data ever reaches an agent. The deck-fit criteria were settled on the tuning decks before any test deck was graded.

03 · Search vs no search infrastructureWith the same instructions, search finds more of the right cards, with fewer tokens

The same agent ran the 113 agent search tasks from Section 2 three ways.

  • Search: the mtg-search search tool, with minimal instructions: the request, a budget of 5 tool calls and the answer format, and nothing on how to search.
  • Grep, same prompt: a regular expression over the plain card text, with no ranking and no filters, and the same minimal instructions as search.
  • Grep, tuned prompt: the identical grep tool, with a prompt written after a first test run. It shows how a card is laid out in the file, explains how matching works, and adds general search advice and an example pattern. The only difference between the two grep runs is the prompt.
Relevant cards in the top 10 over the 80 deck and attribute tasks (relevant: on the task's list of correct cards, or graded relevant by the relevance grader), and mean tokens per task over all 113 tasks, cached input and output included. The hatched bars are the grep run with the tuned prompt.
SearchGrep, same promptGrep, tuned promptRead everything*
Per task, 113 tasks
Tokens10.6k14.3k20.9k2.91M
Claude Code turns4.04.64.95.6
Tool calls2.02.52.90
Seconds10.317.0**10.953.6
Relevant in the top 10
Deck needs (65)97.8%95.4%97.7%–
Card attributes (15)100%48.0%92.7%–
Specific card found
Find-this-card (33)100%85%94%–

*Read everything: the 12-task sample from Section 1. **Two slow tasks (159 s and 322 s); the median is 11.3 s. Rescored on 15 October 2026 after a fix to card-name matching: a few answers, such as "Lightning Bolt", had been matched to a double-faced card that shares the name. Every change is under a point.

With the same instructions, grep falls behind on card attributes, such as a 1-mana red burn instant. Only 48% of its top 10 is relevant, against 100% for search. A card's mana cost, type line and rules text sit on separate lines of the file, and without a description of the layout grep guessed it wrong: 29% of its calls matched nothing. On deck needs, whose rules-text phrases sit on one line, the two stay close (95% vs 98%).

Search

  1. 1
    “deals damage to any target”
    colour Rtype instantmana value 1
    15 cards
  2. 2
    “red instant 1 mana deals 3 damage to any target”
    colour identity Rtype instantmana value ≤ 1
    15 cards

Top 10 of its 17 answers

  1. 1Lightning Bolt✓
  2. 2Shock✓
  3. 3Play with Fire✓
  4. 4Tarfire✓
  5. 5Galvanic Blast✓
  6. 6Wild Slash✓
  7. 7Melt Through✓
  8. 8Spikefield Hazard // Spikefield Cave✓
  9. 9Collateral Damage✓
  10. 10Sonic Seizure✓

2 calls · 9.5k tokens

Grep

  1. 1
    ^[^\n]*\{R\}\n?Instant\b[^\n]*deals \d damage to any target
    0 matches
  2. 2
    (?i)\{R\}.*instant.*deals? (\d|X) damage to (any target|target creature or (player|planeswalker))
    0 matches
  3. Agent: “Format unknown; try simple.”
  4. 3
    Lightning Bolt
    6 matches
  5. 4
    ^[^{\n]*\{R\} — Instant — .*deals? \d damage to any target
    0 matches
  6. Agent: “Name precedes cost; allow name chars.”
  7. 5
    ^[^{]*\{R\} — Instant — .*damage to any target
    0 matches

Its whole answer

  1. 1Lightning Bolt✓
  2. 2empty
  3. 3empty
  4. 4empty
  5. 5empty
  6. 6empty
  7. 7empty
  8. 8empty
  9. 9empty
  10. 10empty

5 calls · 15.5k tokens

The request “a one-mana red instant that deals damage to any target”, given to Claude Sonnet 5.5 with the same minimal prompt for both tools, one run each. ✓: on the task's list of 23 correct cards; search returned 17 of them, grep 1. Grep's results show each card on one line, and calls 4 and 5 copy that layout. In the file, cost, type line and rules text sit on separate lines, so both match nothing. Over all 15 card-attribute tasks, 100% of search's top 10 was relevant and 48% of grep's.

Search needed no prompt tuning. Its first prompt, never tuned, gives the same results as the minimal one: 98.6% vs 98.3% relevant cards in the top 10.

With the same instructions, search finds more of the right cards than grep, with fewer tokens. Grep catches up only with a prompt tuned to the card file, and then uses twice the tokens.

04 · Choosing the enginemLateOn reads rules text best, and the wording of the query matters most

I compared search engines on the search benchmark: 363 queries, each with a list of the cards that answer it. The score is nDCG@10 on a 0 to 100 scale; 100 means the correct cards fill the top 10, best first. mLateOn is a multi-vector text model: it matches each word of the query to the closest word in each card (the late interaction primer draws it).

nDCG@10 × 100. Top: 46 queries written in rules-text wording; mLateOn − dense embedding: +34.0 [+25.4, +42.9]. Bottom: 252 vague requests with mLateOn. “I keep losing to fliers” becomes “Destroy target creature with flying.”

So mtg-search runs mLateOn, and its tool description tells the agent to write queries the way card text is worded. The Docker image returns exactly the same top 10 as the tested search on every benchmark query, in 130 to 340 ms per search. It has three read-only tools, search_cards, get_card and find_similar, with five filters.

The agent searches only when told when to. Offered the tools without a cue, Sonnet made 0 tool calls in 42 deck tasks. Each tool description therefore opens with “Before recommending cards for a deck, call this to find candidates instead of listing them from memory.” With that sentence the agent searched in 25 of 30 tasks on the tuning decks; without it, in 10 of 30.

05 · Why one way of scoring is not enoughPlayer add rates favour popular cards, and miss a real gain in deck fit

Every automatic list of correct cards has a blind spot. The tag and pattern lists miss cards that do the job in other words: in the agent search tasks, 22 to 23% of each agent's top-10 cards were graded relevant but were missing from the lists (636 such cards among all the agents' picks). The patterns also reward exactly the wording grep can rebuild.

Player add rates have a different blind spot. They reward what many players add, and that is mostly the popular cards. A deck can also be improved in many different ways.

Valid adds on the 14 test decks, from memory and with mtg-search together. "Commonly added" means at least 5% of players' copies of that deck add the card. The grader rated 92% of those commonly added cards a reasonable upgrade or better, and 74% of the rest.
Auntie Ool, Cursewretch card

Commander · test deck

Auntie Ool, Cursewretch

Blight Curse, Lorwyn Eclipsed Commander 2026 · black, red, green

From memory fit 0.33

No tool calls. All 20 adds are legal.

  1. 1Bloodtithe Harvester
  2. 1Vraska, Golgari Queen
  3. 1Toxic Deluge
  4. 0Damnation
  5. 0Blood Artist
  6. 0Zulaport Cutthroat
  7. 0Pitiless Plunderer
  8. 2Overgrown Tomb
  9. 2Blood Crypt
  10. 1Stomping Ground
  11. 1Cultivate
  12. 1Kodama's Reach
  13. 0Pawn of Ulamog
  14. 1Deadly Rollick
  15. 0Bloodghast
  16. 0Jade Mage
  17. 1Sheoldred, the Apocalypse
  18. 0Doubling Season
  19. 1Vampire Nighthawk
  20. 0Rhythm of the Wild

With mtg-search fit 0.95

One search_cards call, colour identity BRG · legal in Commander:

  • Put a -1/-1 counter on target creature.
  • Whenever you put one or more -1/-1 counters on a creature
  • Proliferate.
  • Put a -1/-1 counter on each creature your opponents control
  1. 2Obelisk Spider
  2. 2Spitting Dilophosaurus
  3. 2Shadow Urchin
  4. 2Grim Affliction
  5. 1High Perfect Morcant
  6. 2Persistent Constrictor
  7. 2Nightmare Sower
  8. 2Decimator Beetle
  9. 2Exemplar of Strength
  10. 2Blight Rot
  11. 2Scar
  12. 2Contagion Engine
  13. 2Soulstinger
  14. 2Bile-Vial Boggart
  15. 2Festering Mummy
  16. 2Cultbrand Cinder
  17. 2Instill Infection
  18. 2Defiant Greatmaw
  19. 2Blur of Blades
  20. 1Grief Tyrant
2clear upgrade1reasonable0no reason to be in this deck
The 40 adds come from one run per side on one of the 14 test decks, the one with the second-largest gap. From memory: run 3 of 3, deck fit 0.33 (runs: 0.28, 0.33, 0.33). With mtg-search: run 1 of 3, deck fit 0.95 (runs: 0.95, 0.95, 0.50; the 0.50 run made no search call). The plot places each add by its EDHREC rank among Commander cards, on a log scale with the most played on the left; the dots carry the grades from the panels. Card image: Scryfall, © Wizards of the Coast.
Add task, 14 test decks, 3 repeatsFrom memoryWith mtg-searchTest
Tasks in which the agent searched0%40%p = 2×10⁻⁶
Adds that break a deck rule13.2%3.0%p = 0.002
Deck-fit score (0 to 1)0.450.65p = 0.002
Match with player add rates (nDCG@10, 0 to 1)0.1740.262p = 0.057, not established

From memory: the agent was offered the tools without any cue to use them, and never called them. With mtg-search: the server with a cue to search; in the other 60% of tasks the agent still answered from memory. Breaking a deck rule means outside the commander's colours, not legal in Commander, or already in the deck.

On the same adds, the two ways of scoring disagree. The deck-fit score shows a clear gain: better on 13 of 14 decks, and it holds on rule-abiding adds alone (0.52 vs 0.67). The match with player add rates is not significant.

The reason is popularity. With mtg-search, the agent's typical add is around the 1,000th most-played card on EDHREC, against the 300th from memory, and player add rates favour the most-played cards. The deck-fit grader shows no such pull: if anything, its top grades went to less popular cards.

In the swap task (10 one-for-one swaps), the deck-fit gain was smaller and not significant. The deck-fit grader is the same model family as the agent.

With mtg-search, the agent's adds fit the deck better: 0.45 → 0.65, better on 13 of 14 test decks. Player add rates barely register this (p = 0.057), so each test set is scored two ways: by an automatic list of correct cards and by a grader.

06 · Reading the resultThis is a small corpus: at scale the gap between grep and search will grow

34,646 cards and 3.1M tokens of text is a small corpus. A grep over it takes a fraction of a second, and a strong agent can page through the matches. Grep also had a second advantage: Sonnet knows Magic's rules-text templating by heart, so it writes patterns that hit.

Even so, grep matched search's relevance only with a prompt that describes the card file, and then it used twice the tokens, because its matches come unranked, in file order. That cost scales with the corpus. More documents mean more matches per pattern to page through, while ranked search still returns the best ten. In a larger collection, or one whose wording the model has not memorised, expect the token and turn gap to widen, and the quality gap with it. This is an expectation, not something measured here.

Read the grep result as the best case for no search infrastructure: a small corpus, a model that knows its wording, and a prompt tuned to the file. Search needed none of that here, and the effect of embeddings over grep should be more pronounced on anything bigger.


Still openWhat I would check next

  • A grader from another model family, to check that Sonnet does not favour its own picks. It needs API budget.
  • Fresh test decks, once players' data on them matures, around late November.
  • Repeats: the share of tasks in which the agent searches swings by 20 points between identical runs.

The server, the benchmarks and the run records are private for now: Scryfall's terms forbid republishing its card data, and the deck data comes from EDHREC with permission for private evaluation only. Sources: Scryfall bulk data, Scryfall Tagger, mLateOn, MTGJSON, EDHREC. Card text © Wizards of the Coast, used under the Fan Content Policy.