Metadata-Version: 2.5
Name: commander-workshop-mcp
Version: 0.1.0
Summary: Local-first Commander deck planning and fulfillment MCP server
Project-URL: Homepage, https://github.com/gregario/commander-workshop-mcp
Project-URL: Repository, https://github.com/gregario/commander-workshop-mcp
Project-URL: Issues, https://github.com/gregario/commander-workshop-mcp/issues
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.28
Requires-Dist: mcp<3,>=2.0
Requires-Dist: platformdirs<5,>=4.3
Requires-Dist: pydantic<3,>=2.11
Provides-Extra: test
Requires-Dist: build<2,>=1.2; extra == 'test'
Requires-Dist: pytest-asyncio<2,>=1.1; extra == 'test'
Requires-Dist: pytest<9,>=8.4; extra == 'test'
Requires-Dist: ruff<1,>=0.12; extra == 'test'
Description-Content-Type: text/markdown

# Commander Workshop MCP

Commander Workshop is a local-first MCP server for building, checking, comparing,
and fulfilling Magic: The Gathering Commander decks. It combines pinned rules
and card data with your explicitly imported collection, keeps deck intent
separate from ownership, and turns a complete plan into pulls, swaps,
substitutes, proxies, or purchases.

<!-- mcp-name: io.github.gregario/commander-workshop -->

The server is useful for unconstrained brewing, inventory-first construction,
Bracket 3 planning, and fixed-budget precon upgrades. It evaluates bounded
complete candidates and says **best found under these constraints** rather than
claiming a universal power score or mathematically perfect deck.

## Features

| Need | What Commander Workshop provides |
| --- | --- |
| Brew or upgrade | Complete, legality-checked plans under composable constraints |
| Understand a deck | Mana, curve, roles, packages, theme evidence, and uncertainty |
| Use cards you own | ManaBox and mapped CSV imports with binder/deck locations |
| Keep plans honest | Decklists never imply ownership; assembled-deck copies are reported |
| Find missing cards | Non-reserving availability plus actionable fulfillment plans |
| Respect a budget | Native USD/EUR constraints and 50-unit Bracket 3 upgrade presets |
| Check prices | Local Scryfall estimates or optional read-only CardTrader quotes |
| Integrate with agents | Eleven MCP tools and immutable, paginated resources over stdio |

## Privacy promise

Your collections, binder names, locations, and physical-deck allocations remain
local by default. Local imports are restricted to configured roots. The server
does not push decks, modify collections, reserve cards, add marketplace items to
a cart, or complete purchases. Optional marketplace pricing sends only the
minimum printing requirements and reads credentials from the environment or MCP
host secret store.

## Installation

Commander Workshop requires Python 3.11 or newer. From a repository checkout:

```bash
python -m venv .venv
. .venv/bin/activate
python -m pip install -e .
```

V1 currently supports POSIX filesystems only. Its immutable publication paths
require directory file descriptors, `O_DIRECTORY`, `O_NOFOLLOW`, hard links,
`fsync`, and advisory locks; Windows is not supported. The stdio server also
expects the verified local inputs prepared next.

## Catalog and rules setup

V1 deliberately makes no network request when the server starts. Download the
Scryfall Default Cards JSONL gzip through Scryfall's documented bulk-data API,
prepare the five-file rules bundle specified in
[docs/rules-bundle.md](docs/rules-bundle.md), then run the tested offline
bootstrap command:

```bash
.venv/bin/commander-workshop-bootstrap \
  --data-dir /absolute/path/to/commander-workshop-data \
  --catalog-jsonl-gz /absolute/path/to/default-cards.jsonl.gz \
  --catalog-url https://data.scryfall.io/default-cards.jsonl.gz \
  --catalog-retrieved-at 2026-08-15T12:00:00+00:00 \
  --rules-bundle /absolute/path/to/rules-bundle
```

Use the actual download URL and retrieval time. `--data-dir` is a safe install
root; it may already contain unrelated files, which the bootstrap never removes
or overwrites. The command verifies the gzip and rules, builds a
content-addressed environment beneath `environments/<sha256>`, and publishes an
atomic `READY` marker only after the complete representation is durable. A
retry recovers an exact incomplete environment and returns the same version.
Published catalog and rules files are exclusive byte copies with no retained
stage aliases. Symlinked inputs, hard-link aliases, unexpected sidecars, and
conflicting existing content fail closed.

Before building, bootstrap discovers a fully verified READY environment by its
versioned input identity, so normal same-input retries create no new stage.
Failed stages are inert and may remain for manual inspection; bootstrap never
recursively deletes stage content it can no longer prove it owns.

The printed data directory remains the mutable install root, where MCP records
are written under `records/`. The printed catalog and rules paths point into the
attested ready version. Use all three values together; directories without a
valid `READY` marker are incomplete bootstrap state and are never returned.

| Environment variable | Purpose | Bootstrap output shape |
| --- | --- | --- |
| `COMMANDER_WORKSHOP_DATA_DIR` | Mutable runtime root for content-addressed MCP records | `<install-root>` |
| `COMMANDER_WORKSHOP_CATALOG_CURRENT` | Catalog `current.json` pointer | `<ready-environment>/catalog/current.json` |
| `COMMANDER_WORKSHOP_RULESET` | Ruleset JSON | `<ready-environment>/rules/current.json` |
| `COMMANDER_WORKSHOP_IMPORT_ROOTS` | Allowed local import roots, separated by the OS path separator | no file imports |
| `CARDTRADER_API_TOKEN` | Optional read-only CardTrader authentication | unset |

After bootstrap, set `COMMANDER_WORKSHOP_IMPORT_ROOTS` to one or more existing
absolute directories and use the printed values for the other variables. A
missing, stale, or invalid critical source fails closed. See
[the architecture guide](docs/architecture.md) for the data flow.

## Claude, Codex, and generic stdio configuration

For Claude Desktop or another host using the common JSON shape, copy
[`examples/mcp-config.json`](examples/mcp-config.json) and replace its absolute
placeholder paths.

Codex can use the same executable from `config.toml`:

```toml
[mcp_servers.commander_workshop]
command = "/absolute/path/to/commander-workshop-mcp/.venv/bin/commander-workshop-mcp"
args = []

[mcp_servers.commander_workshop.env]
COMMANDER_WORKSHOP_DATA_DIR = "/absolute/install-root"
COMMANDER_WORKSHOP_CATALOG_CURRENT = "/absolute/install-root/environments/<printed-sha256>/catalog/current.json"
COMMANDER_WORKSHOP_RULESET = "/absolute/install-root/environments/<printed-sha256>/rules/current.json"
COMMANDER_WORKSHOP_IMPORT_ROOTS = "/absolute/path/to/imports"
```

Any MCP v2 client can launch that absolute virtual-environment executable as a
local stdio subprocess with those environment variables. There is no HTTP
service or account setup in v1. A pipx installation is also workable: use the
absolute path returned by `pipx environment --value PIPX_BIN_DIR` joined with
`commander-workshop-mcp`.

## End-to-end example: unconstrained brewing

1. Use `search_cards` to inspect the pinned catalog and `load_deck` for each
   complete candidate deck produced during the brew.
2. Call `plan_deck` with operation `build`, card pool `all_legal`, the desired
   bracket/theme/proxy dimensions, and one to eight complete candidate
   submissions. Read the returned `commander://plans/...` resource.
3. Optionally load a collection, call `resolve_availability` for the winning
   plan, then call `create_fulfillment_plan`. Availability remains hypothetical;
   fulfillment allocates a copy only inside that immutable result.

This flow is unconstrained by inventory while brewing, yet can still show what
is on hand and what is missing before the user decides to act. The complete
[`plan_deck` request template](examples/requests/unconstrained-build.json) is
schema-validated by the acceptance suite.

## End-to-end example: owned-only construction

1. Export the physical collection as ManaBox CSV or a CSV with explicit column
   mappings, then call `load_collection_snapshot` in strict mode.
2. Use `search_library` to locate candidate cards by name, printing, binder, or
   assembled-deck allocation.
3. Call `plan_deck` with card pool `owned_only` and the collection snapshot ID.
   A candidate with an unowned non-basic card fails the hard constraint.
4. Read the plan and availability resources to see binder pulls and conflicts.
   A copy already allocated to a deck is reported, not silently treated as free.

Use the complete [owned-only request template](examples/requests/owned-only.json)
after replacing its deck and collection IDs with prior tool results.

## End-to-end example: Bracket 3 USD/EUR 50 precon upgrade

1. Export or paste the precon and call `load_deck`; it remains deck intent, not
   an ownership claim.
2. Load complete upgraded candidates and call `plan_deck` with operation
   `upgrade`, the precon as `starting_deck_id`, Bracket 3, and either the
   `precon-upgrade-50-usd` or `precon-upgrade-50-eur` scenario preset semantics.
3. Inspect exact additions/cuts, dated bracket evidence, Game Changers, theme
   facts, and the guaranteed native-currency acquisition bound. Missing required
   price evidence cannot pass the cap as zero.
4. Optionally resolve inventory, choose pulls/swaps/substitutes/proxies with
   `create_fulfillment_plan`, then call `price_purchases` for the remaining
   purchase lines. Pricing is informational and never places an order.

The [USD 50 request template](examples/requests/precon-upgrade-50.json) contains
the full scenario, candidate evidence, score definitions, bracket policy, and
starting-deck field. Change both currency and total policy for the EUR preset.

## Supported imports

- ManaBox collection CSV, separating physical binders and registered decks from
  wishlists or other non-owning lists.
- Generic collection CSV with an explicit mapping for quantity and exact card
  identity, plus optional printing and location fields.
- Arena-style and common quantity/name/set/collector-number deck text.
- User-exported deck text from Moxfield, Archidekt, Deckbox, Deckstats,
  TopDecked, and similar hosts when it matches the supported text dialect.

Paste or select an export you are authorized to use. V1 does not scrape account
pages, reuse browser cookies, or call undocumented deck-host endpoints. Direct
hosted URLs return `unsupported_source`; export the deck and import its text.

## Tools

The public surface is exactly eleven tools:

| Tool | Purpose |
| --- | --- |
| `search_cards` | Search the verified local catalog with bounded filters and pagination. |
| `load_collection_snapshot` | Normalize a strict or explicitly partial local collection snapshot. |
| `load_deck` | Normalize deck text without inferring ownership. |
| `search_library` | Search physical copies, locations, printings, and deck allocations. |
| `check_deck` | Check exact construction, legality, Rule Zero, and scenario facts. |
| `analyze_deck` | Report structural metrics, roles, packages, evidence, and uncertainty. |
| `plan_deck` | Select a complete best-found candidate under a composed scenario. |
| `compare_decks` | Compare exact changes and evidence without an aggregate power score. |
| `resolve_availability` | Find exact copies, conflicts, alternatives, proxies, and gaps without reserving. |
| `price_purchases` | Create a dated read-only price snapshot for unresolved purchases. |
| `create_fulfillment_plan` | Choose pulls, deck swaps, substitutes, proxies, and purchases. |

Every tool returns a versioned envelope with a stable result ID, status, concise
summary, issues, source stamps, and usually a resource URI.

## Resources

```text
commander://cards/{oracle_id}
commander://searches/{result_id}
commander://collections/{snapshot_id}
commander://decks/{deck_id}
commander://plans/{plan_id}
commander://availability/{resolution_id}
commander://prices/{snapshot_id}
commander://fulfillment/{plan_id}
commander://rules/current
commander://presets
```

Records are immutable and content-addressed. Collections and broad searches are
bounded and paginated; tool summaries avoid echoing collection contents.

## Scenario dimensions

A planning scenario composes these independent choices:

- operation: build, upgrade, or analyze;
- bracket: 1 through 5, with dated turn and Game Changer evidence;
- card pool: `all_legal`, `prefer_owned`, or `owned_only`;
- budget: native currency total and optional per-card cap, including USD/EUR 50
  precon-upgrade presets;
- proxy policy: none, owned-once, or unlimited where the playgroup permits it;
- theme policy: strict contract, flexible preference, or strength-oriented;
- protected assembled decks, pinned ruleset, and deterministic seed.

Bracket fit and strategic power are not collapsed into one score. Official
Commander rules remain authoritative, and Rule Zero overrides must be explicit.

## Pricing semantics

`price_purchases` separates item subtotal, provider subtotal, shipping, and
estimated landed total. Unknown items or shipping remain `null`, never zero. A
guaranteed budget can pass only when every required purchase has eligible known
evidence under the selected policy.

Scryfall catalog values are indicative `PriceEstimate` observations, not
checkout quotes. CardTrader listings are read-only `PurchaseQuote` observations.
Planning estimates use a declared freshness window of at most 24 hours;
CardTrader may use a shorter listing lifetime. Partial and stale observations
carry explicit `unknown_price`, authentication, outage, or rate-limit issues.

## Provider limitations

- Scryfall estimates come from the verified local catalog, may lack a currency,
  finish, condition, language, quantity, or shipping, and are not purchasable.
- CardTrader is optional, requires `CARDTRADER_API_TOKEN`, uses only fixed
  documented GET endpoints, does not follow redirects, and does not optimize a
  perfect multi-seller basket.
- Cardmarket and TCGplayer are not enabled by default; access restrictions and
  provider terms must be respected by any future adapter.
- No provider can change deck legality, collection ownership, proxy policy, or
  reserve inventory. No provider exposes cart or purchase operations.

Normal tests and CI make no live marketplace or deck-host requests.

## Development

```bash
python -m pip install -e '.[test]'
python scripts/check_public_tree.py
ruff check src tests scripts
pytest -q
python -m build
```

The privacy scanner inspects the tracked tree for private paths, credentials,
databases, unsafe data exports, symlinks, and undocumented/oversized fixtures.
All test data is synthetic and deterministic.

## Architecture

See [docs/architecture.md](docs/architecture.md) for component boundaries,
immutable storage, trust boundaries, planning/fulfillment flow, and extension
points.

## Security reporting

Follow [SECURITY.md](SECURITY.md). Use a private security advisory and a minimal
synthetic reproduction; never attach a collection export, binder inventory,
token, or other personal data.

## Attribution

Card names, rules text, and catalog metadata may be derived from
[Scryfall](https://scryfall.com/docs/api), whose data and attribution requirements
apply. Magic: The Gathering rules and Commander bracket policy are published by
[Wizards of the Coast](https://magic.wizards.com/en/formats/commander). Provider
names and marketplace data remain the property of their respective owners.

## Non-affiliation

Commander Workshop MCP is unofficial fan software. It is not affiliated with,
endorsed, sponsored, or approved by Wizards of the Coast, Scryfall, CardTrader,
or any other deck host or marketplace. Magic: The Gathering, Commander, and
related marks belong to Wizards of the Coast and their respective owners.

Commander Workshop MCP is unofficial Fan Content permitted under the Fan Content
Policy. Not approved/endorsed by Wizards. Portions of the materials used are
property of Wizards of the Coast. ©Wizards of the Coast LLC.

## License

The software is released under the [MIT License](LICENSE). Third-party card,
rules, and marketplace data remains subject to its own terms and licenses.
