Matthew Towles

Project

I Want My MTG

live

A collection tracker for Magic: The Gathering, spanning data ingestion, a web app, mobile clients, and an MCP server.

NestJS · TypeScript · Rust · React Native · Expo · PostgreSQL · Docker · AWS Lightsail

Try it

Install

MCP server in Claude Desktop, the quick way

A packaged extension. Claude Desktop installs it without any config file editing, and prompts for an optional API key during setup. Use the manual configuration below for Claude Code, Cursor, or any other client.

MCP server in Claude Desktop, Claude Code, or Cursor

Add this to your client's MCP configuration

{
  "mcpServers": {
    "iwmm": {
      "command": "npx",
      "args": ["-y", "iwantmymtg-mcp"],
      "env": { "IWMM_API_KEY": "iwm_live_..." }
    }
  }
}

Where that file lives, Claude Desktop

macOS    ~/Library/Application Support/Claude/claude_desktop_config.json
Windows  %APPDATA%\Claude\claude_desktop_config.json

Where that file lives, Claude Code

.mcp.json in your project, or ~/.claude/.mcp.json for every project

Where that file lives, Cursor

.cursor/mcp.json in your project, or ~/.cursor/mcp.json for every project

The API key is optional. Card, set, and price lookups work without one, and a key adds the tools that read or change your own collection. Create a key at iwantmymtg.net/user/api-keys. Cursor needs a restart before iwmm shows up under Settings, Features, MCP Servers.

MCP server, run on its own

Without installing anything

npx iwantmymtg-mcp

Or install it globally

npm install -g iwantmymtg-mcp
iwantmymtg-mcp

Requires Node 20 or newer.

Scry ETL container

docker pull ghcr.io/matthewdtowles/scry:latest

The ingestion pipeline, published on every release.

Magic cards are priced by a market that moves daily across tens of thousands of printings. Tracking what a collection is worth means continuously pulling that data, reconciling it against a catalog that changes with every set release, and making it queryable fast enough to browse.

I Want My MTG is four repositories solving one problem. Scry pulls and normalizes the data, PostgreSQL holds it, and three separate clients read from it: a web app, mobile apps, and an MCP server that lets AI assistants query a collection directly.

How the data flows

Everything downstream depends on one ingestion path. Scry is the only writer of card, price, and tournament data; every client is a reader.

card art

MTGJSON bulk files

Card Kingdom pricelist

Tournament deck feed

Scry ETL

PostgreSQL

NestJS API

Web app

Mobile apps

MCP server

RapidAPI marketplace

Scryfall images

Scry runs on a schedule rather than on demand. Prices refresh hourly, but only after a check confirms the upstream file actually changed, so an unchanged feed costs nothing. Tournament decks land nightly, and a weekly retention pass keeps price history from growing without bound.

JobScheduleWhat it does
Ingest chainHourlyRefreshes prices when upstream data changed
Deck ingestNightlyPulls the last two days of tournament decks
RetentionWeeklyPrunes price history
Health checkDailyVerifies the pipeline is current

How it is deployed

The whole system runs on a single AWS Lightsail instance. The web app runs in Docker, Scry runs as a native binary invoked by cron, and both talk to the same PostgreSQL database.

External servicesAWS Lightsail instanceClients

Browser

iOS and Android

AI assistants via MCP

NestJS web app in Docker

cron

Scry binary

PostgreSQL 18

Amazon SES

Stripe billing

Scryfall images

The deployment order matters and is enforced rather than remembered. Scry writes tables that the web repository's migrations create, so the web deploy runs migrations first and only then installs the Scry binary, extracting it from the published container image.

How it ships

Each repository has its own pipeline, and two of them are coupled by that deployment order.

scryi-want-my-mtgbinary extracted duringdeployi-want-my-mtg-mobile

Typecheck

Unit and component tests

Version and tag

iwantmymtg-mcp

Lint

Tests on a Node matrix

Version

Publish to npm

Typecheck, lint, format

Unit, integration, and end to end tests

Version and tag

Build image

Deploy over SSH

Format and clippy

Tests

Version and release

Push image to ghcr.io

Version numbers are derived from pull request titles rather than hand edited, so a merge decides its own release number and the tag, image, and deployed version cannot drift apart.

Source code

Role

Sole engineer