mtg-archetypes
Magic: The Gathering deck archetype classifier
cat ur-delver.txt | mtg-archetypes legacy
# Izzet Delver
Overview
mtg-archetypes is a rule-based deck archetype classifier for MTG across competitive formats (Legacy, Vintage, Modern, Pioneer, Pauper, Premodern, Standard).
- Declarative: One YAML file per archetype with archetype defining card constraints
- Board scope awareness: Enforce cards in mainboard (
in: "main"), sideboard (in: "side"), or anywhere in the 75 - Exact card quantities: Enforce minimum copy counts (e.g.
min: 3Mishra's Workshop -> "Shops") - Multi-group signature logic: Require combinations across multiple card pools (
signature_groups) - Deterministic: A deck either matches a defined archetype or goes unclassified
- CLI & Library: Use the standalone command line tool or use it as a Python module
Installation
pip install mtg-archetypes
Quickstart
CLI Usage
Pipe any decklist directly into mtg-archetypes:
cat delver.txt | mtg-archetypes legacy
# Izzet Delver
Or pass a file argument:
mtg-archetypes vintage jewel_shops.txt
# Jewel Shops
Silent on No Match
When a deck does not match any rules in the format, the CLI outputs nothing at all:
echo "60 Plains" | mtg-archetypes legacy
# (outputs nothing, exit code 0)
Verbose Output (--verbose)
Pass --verbose to inspect priorities, categories, or see "Archetype: Unclassified":
cat delver.txt | mtg-archetypes legacy --verbose
Archetype: Izzet Delver
Priority: 100
Category: Tempo
cat unknown_pile.txt | mtg-archetypes legacy --verbose
Archetype: Unclassified
JSON Output (--json)
cat delver.txt | mtg-archetypes legacy --json
{
"matched": true,
"name": "Izzet Delver",
"slug": "izzet-delver",
"priority": 100,
"category": "Tempo",
"matched_rule": "Izzet Delver"
}
If unclassified:
{
"matched": false,
"name": null,
"slug": null,
"priority": 0,
"category": null,
"matched_rule": null
}
Python Library Usage
from mtg_archetypes import ArchetypeClassifier
classifier = ArchetypeClassifier()
# Or use:
# decklist = "4 Delver of Secrets\n..."
# mtg_archetypes.cards.parse_decklist_text(decklist)
mainboard = [
"4 Delver of Secrets",
"4 Daze",
"4 Force of Will",
"4 Lightning Bolt",
"4 Volcanic Island",
"4 Brainstorm",
]
sideboard = [
"2 Pyroblast",
"1 Meltdown",
"2 Grafdigger's Cage",
]
result = classifier.classify(mainboard, sideboard, format="legacy")
if result.matched:
print(f"Archetype: {result.name} (Priority {result.priority})")
else:
print("Unclassified deck")
Archetype YAML Schema
Each archetype is defined in its own file under archetypes/<format>/<slug>.yaml.
Example: Vintage Jewel Shops
# archetypes/vintage/jewel-shops.yaml
name: "Jewel Shops"
category: "Shops"
priority: 95
min_signatures: 2
# At least 3 Mishra's Workshop copies required in mainboard
# At least one Coveted Jewel
mandatory:
- card: "Mishra's Workshop"
min: 3
in: "main"
- card: "Coveted Jewel"
in: "main"
signatures:
- "Tinker"
- "Trinisphere"
- "The One Ring"
- "Phyrexian Metamorph"
anti_signatures:
# Sphere of Resistance can be in sideboard, but not mainboard!
# Otherwise, it is "Sphere Shops"
- card: "Sphere of Resistance"
in: "main"
- "Doomsday"
Example: Vintage CounterVine
Demonstrating multi-group condition logic (signature_groups):
# archetypes/vintage/countervine.yaml
name: "CounterVine"
category: "Aggro"
priority: 85
mandatory:
- card: "Bazaar of Baghdad"
min: 3
in: "main"
# Multiple signature pools evaluated with AND logic
signature_groups:
- min: 1
in: "main"
cards:
- "Master of Death"
- "Squee, Goblin Nabob"
- min: 2
in: "main"
cards:
- "Basking Rootwalla"
- "Blazing Rootwalla"
Priority System & Conventions
- Priority >= 50: Specific variants should have higher priority than their broader counterparts (eg. Red Prison vs Red Stompy).
- Priority < 50: Broad "fallback" or "good stuff" archetypes (e.g. generic Control, Midrange, or Stompy catch-alls). Also, decks like Stoneblade may be here so they don't match more specific archetypes like Death & Taxes.
- The highest priority archetype where all the "mandatory", "signature_groups", and none of the "anti_signature" rules match will be selected. In the event a deck matches two archetypes of equal priority, the archetype with more rules be selected.
Supported Directives
| Directive | Description |
|---|---|
name |
Required. Display name of the archetype. |
priority |
Required. Integer (0-200) for tie-breaking when a deck matches multiple candidate rules. Higher wins. |
category |
Optional. Format specific strategy categorization (e.g. "Tempo", "Combo", "Aggro", "Control"). |
mandatory |
Optional. List of cards that must be present. Accepts card strings or mappings: { card: "...", min: N, in: "main" | "side" | "any" }. |
signatures |
Optional. List of signature cards evaluated against min_signatures. |
min_signatures |
Optional. Minimum count of signature cards required to match (defaults to 1). |
signature_groups |
Optional. List of group mappings { cards: [...], min: N, in: "main" | "side" | "any" }. All groups must pass. |
anti_signatures |
Optional. Cards that disqualify the deck if present. Accepts card strings or mappings: { card: "...", in: "main" | "side" | "any" }. |
Validation & Card Name Checking
The repository includes a validation script that verifies all YAML files against the schema and can optionally validate card names against the official Scryfall card catalog:
# Validate YAML syntax, required fields, and rules schema
uv run python scripts/validate_archetypes.py
# Also validate every card name against Scryfall catalog
uv run python scripts/validate_archetypes.py --check-cards
# Force update local Scryfall cache
uv run python scripts/validate_archetypes.py --update-scryfall
Development & Testing
# Run test suite
uv run pytest
# Run linter and formatter
uv run ruff check .
uv run ruff format --check .
# Always check all archetype files before submitting a PR
uv run python scripts/validate_archetypes.py --check-cards
Acknowledgments & Prior Art
This project was heavily inspired by Badaro/MTGOFormatData.
Metadata
Release files for mtg-archetypes 2026.9.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mtg_archetypes-2026.9.2.tar.gz | 49.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mtg_archetypes-2026.9.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 143.5 kB
Release files / mtg_archetypes-2026.9.2.tar.gz
| Download URL | mtg_archetypes-2026.9.2.tar.gz |
|---|---|
| Size | 49.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c90544ee6fdd39b232b825ef488b51e6bbf31b79660b8e51e54402be71b27ee
|
|
BLAKE2b-256 checksum How to use checksums |
59de2e175f00c18a3b2eb40676b32dbdec492b256374ba6f5fd131e998387fec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / mtg_archetypes-2026.9.2-py3-none-any.whl
| Download URL | mtg_archetypes-2026.9.2-py3-none-any.whl |
|---|---|
| Size | 94.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9437a400dfd64285bb59a6856a97c3691b66688a20230d204546fe0b2393033
|
|
BLAKE2b-256 checksum How to use checksums |
335c152a53fdd9943c2b72e5fc5d40166b72d2076a1726f1e03434808d6cef3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|