Symptom
deck_list filters=[{type: card-type, mode: include, values: ["land"]}] → 0 cards
deck_list filters=[{type: card-type, mode: include, values: ["Land"]}] → 1 card (correct)
Lowercase "land" returns an empty result with no warning, indistinguishable from "this deck has no lands". Same applies to deck_curve.
Root cause
packages/shared/src/filters/index.ts:52 — filter.values.includes(primaryType) is an exact string match against PrimaryType values (Creature, Land, …).
color (line 46) compares against W/U/B/R/G/C exactly; ownership (line 60) compares against unknown/owned/need_to_buy exactly; cmc (line 41) uses includes on numbers, so a string "3" never matches.
- The tool schema in
packages/mcp-server/src/tools/schemas.ts:14 declares values: { type: 'array', items: {} } — untyped, so nothing tells the caller what the legal values are.
- Nothing validates that a value matched anything.
Fix
- Normalise case in
matchesFilter (or in the MCP arg validator validateDeckListArgs / validateDeckCurveArgs in tools/types.ts) — lowercase both sides for card-type, color, ownership; coerce numeric strings for cmc.
- Document the legal values per filter type in
CARD_FILTER_SCHEMA (a oneOf per type, or at minimum the value lists in the description). PRIMARY_TYPES in packages/shared/src/types/index.ts is the source of truth for card-type.
- Reject a filter value that is not in the legal set with an error naming the valid values, rather than returning an empty list.
Test
packages/shared/src/filters/index.test.ts (applyFilters) — "land", "LAND", and "Land" all match; "lnad" throws (or whatever the chosen behaviour is).
Symptom
Lowercase
"land"returns an empty result with no warning, indistinguishable from "this deck has no lands". Same applies todeck_curve.Root cause
packages/shared/src/filters/index.ts:52—filter.values.includes(primaryType)is an exact string match againstPrimaryTypevalues (Creature,Land, …).color(line 46) compares againstW/U/B/R/G/Cexactly;ownership(line 60) compares againstunknown/owned/need_to_buyexactly;cmc(line 41) usesincludeson numbers, so a string"3"never matches.packages/mcp-server/src/tools/schemas.ts:14declaresvalues: { type: 'array', items: {} }— untyped, so nothing tells the caller what the legal values are.Fix
matchesFilter(or in the MCP arg validatorvalidateDeckListArgs/validateDeckCurveArgsintools/types.ts) — lowercase both sides forcard-type,color,ownership; coerce numeric strings forcmc.CARD_FILTER_SCHEMA(aoneOfpertype, or at minimum the value lists in the description).PRIMARY_TYPESinpackages/shared/src/types/index.tsis the source of truth forcard-type.Test
packages/shared/src/filters/index.test.ts(applyFilters) —"land","LAND", and"Land"all match;"lnad"throws (or whatever the chosen behaviour is).