0.0 / 30
What changed in the harness
Selection accuracy 100→93, token cost up 15%, unconfirmed writes 0%→0%.
Category breakdown
Where the score comes from.
Earned points across the four signals Gradable measures. Safety and Legibility are scored out of 30; Economics and Discoverability are scored out of 20.
01Safety
02Legibility
19.4 / 30
03Economics
14.0 / 20
04Discoverability
12.8 / 20
Highest-impact fix
Estimated gain +30 pointsAdd explicit identity and permission preflight tools
Expose machine-readable principal/tenant confirmation and a non-mutating permission check so agents can verify both before destructive actions.
Description evidence
Defects and rewrites.
46 defects found across the exposed tool descriptions. Suggested rewrites make purpose, inputs, boundaries, and returns easier for an agent to understand.
| Tool | Defect types | Suggested rewrite |
|---|---|---|
get_card_prices |
name_restates_behavior no_return_description |
Look up the current retail prices for a single card printing, identified by set code and collector number (e.g. setCode 'lea', setNumber '161'). Returns that printing's normal and foil price data so you can see what it costs to buy. For sell-to-vendor offers use get_card_buylist instead. |
get_card_price_history |
no_return_description |
Get the price history for a card printing, identified by set code and collector number (e.g. 'lea'/'161'). Returns a price-over-time series for both normal and foil finishes, covering the most recent 30 days at daily resolution with older data retained at a weekly/monthly cadence beyond that. Use it to judge a card's price trend; for the current price use get_card_prices. |
search_sets |
params_unexplained |
List Magic: The Gathering sets, optionally paginated. Returns set code, name, release date, type, and aggregate prices for each set. page is the 1-based page index (default first page) and limit is the page size (1-100, default server-side). Omit both to get the first page. |
get_set |
name_restates_behavior params_unexplained no_return_description |
Look up a single Magic set by its code (e.g. 'lea' or 'mh3'), which is the 3-5 character identifier used across this API. Returns the set's metadata: set code, name, release date, type, and aggregate prices. Use search_sets first to discover codes, or list_set_cards to enumerate the cards inside the set. |
list_set_cards |
params_unexplained no_return_description |
List the cards that belong to a single set, identified by code (e.g. 'mh3'), paginated. Optional filters: rarity (common/uncommon/rare/mythic), type (substring match on the card type line), format combined with legality (legality defaults to 'legal' when format is set). page is the 1-based page index and limit is the page size (1-100). Returns the matching cards with prices and basic metadata, like search_cards. |
get_sealed_products |
params_unexplained no_return_description |
List the sealed products (booster boxes, bundles, commander decks, etc.) available for a set. code is the set code (e.g. 'mh3'). Returns the set's sealed products as a list; each entry includes product info and a TCGPlayer purchase URL. Use get_sealed_product with an entry's UUID for fuller detail. |
get_sealed_product |
no_return_description |
Get the full detail for a single sealed product, identified by uuid (e.g. one obtained from get_sealed_products). Returns that product's data including its current pricing and a TCGPlayer purchase URL. |
get_set_price_history |
no_return_description |
Get the price history for an entire set by code (e.g. 'mh3'), showing the set's aggregate value over time. Returns the set's value-over-time series; days (minimum 1) optionally limits how far back the returned history goes, otherwise the full history is returned. For a single card's history use get_card_price_history. |
list_buy_list |
no_return_description |
List the authenticated user's buy-list (want-list): the cards they want to acquire. Returns each wanted card with its quantity, finish, and current prices. Requires IWMM_API_KEY. |
add_buy_list |
no_return_description |
Increment the quantity for a card+finish on the authenticated user's buy-list, creating the row if it does not exist yet. cardId is the internal IWMM card UUID (from search_cards or get_card), isFoil selects the finish (defaults to false), quantity is how many to add (defaults to 1). This is a real write. Use update_buy_list to set an absolute quantity or remove_buy_list to delete the row. Requires IWMM_API_KEY. |
update_buy_list |
no_return_description |
Set the absolute quantity for a buy-list card+finish (not a delta). cardId is the internal IWMM card UUID (from search_cards or get_card), isFoil selects the finish (defaults to false), and quantity is the exact total to set, with 0 removing the row. Use add_buy_list to increment instead. Requires IWMM_API_KEY. |
remove_buy_list |
name_restates_behavior no_return_description |
Permanently delete the row for a card+finish from the authenticated user's buy-list. cardId is the internal IWMM card UUID (from search_cards or get_card) and isFoil selects whether to remove the foil or non-foil row. Use this when the card is no longer wanted; use add_buy_list/update_buy_list to change quantities. Requires IWMM_API_KEY. |
list_inventory |
params_unexplained |
List the authenticated user's card inventory, paginated. Returns the owned cards with quantities, prices, and metadata. page is the 1-based page index and limit is the page size (1-100); omit both for the first page. Requires IWMM_API_KEY. |
get_inventory_quantities |
params_unexplained |
Batch lookup of how many of each card the authenticated user owns. cardIds is a list of internal IWMM card UUIDs (from search_cards or get_card), 1 to 200 per call. Returns the owned quantity for each requested card, split into normal and foil. Useful before recommending adds. Requires IWMM_API_KEY. |
add_inventory |
params_unexplained no_return_description |
Add one or more cards to the authenticated user's inventory. items is an array where each entry specifies cardId (internal IWMM UUID), quantity (total for that card+finish; 0 removes the row), and isFoil. Accepts a batch - pass a single-item array for one card. This is a real write. Use update_inventory to change quantities or remove_inventory to delete a row. Requires IWMM_API_KEY. |
update_inventory |
params_unexplained no_return_description |
Update quantities for one or more existing inventory rows of the authenticated user. items is an array where each entry specifies cardId (internal IWMM UUID), quantity (total for that card+finish; 0 removes the row), and isFoil. Accepts a batch. Use remove_inventory to delete a row entirely. Requires IWMM_API_KEY. |
remove_inventory |
name_restates_behavior params_unexplained no_return_description |
Permanently delete an inventory row for the authenticated user. cardId is the internal IWMM card UUID (from search_cards or get_card) and isFoil selects whether to remove the foil or non-foil row; both are required. Use this to delete a row entirely rather than zeroing its quantity. Requires IWMM_API_KEY. |
list_sealed_inventory |
params_unexplained no_return_description |
List the authenticated user's sealed-product inventory (booster boxes, bundles, precons), paginated, and return each row with its product details, quantity, and price. page: 1-based page number to request (default 1). limit: maximum items per page (1-100). For loose cards use list_inventory. Requires IWMM_API_KEY. |
set_sealed_inventory |
no_return_description |
Add or update a sealed product in the authenticated user's inventory by setting its absolute quantity; this upserts the row for that product, creating it if absent or overwriting the existing quantity. Returns the saved inventory row with the product and its new quantity. This is a real write and is Premium-gated. Use remove_sealed_inventory to delete a row. Requires IWMM_API_KEY. |
remove_sealed_inventory |
name_restates_behavior no_return_description |
Delete a sealed product row from the authenticated user's inventory so it no longer appears in list_sealed_inventory, identified by its sealedProductUuid (from get_sealed_products or get_sealed_product). Returns confirmation that the product was removed. Premium-gated. Requires IWMM_API_KEY. |
list_transactions |
params_unexplained no_return_description |
List the authenticated user's transactions, paginated, and return the matching transaction rows (id, type, card, quantity, price). page: 1-based page to fetch (default 1). limit: maximum transactions per page (1-100). type: filter to only BUY or only SELL transactions, omit for both. Free tier sees the last 30 days only; Premium gets full history. Requires IWMM_API_KEY. |
record_transaction |
params_unexplained no_return_description |
Record a BUY or SELL transaction and return the created transaction (with its id, usable with update_transaction/delete_transaction). By default this also adjusts inventory (BUY adds cards, SELL subtracts them); pass skipInventorySync=true to record without touching inventory. type: 'BUY' or 'SELL', determines whether inventory increases or decreases. quantity: number of cards in the transaction (>=1). isFoil: whether the cards are the foil version. fees: additional costs paid on the transaction such as shipping or taxes (>=0). notes: free-form text to attach. This is a real write. Requires IWMM_API_KEY. |
update_transaction |
params_unexplained no_return_description |
Update an existing transaction by ID; only the supplied fields are changed and anything else keeps its current value, returning the updated transaction. quantity: new card count (>=1). pricePerUnit: new per-unit price in USD (>=0). date: new transaction date. source: where the transaction happened (e.g. 'TCGPlayer', 'LGS'). fees: new fees amount (>=0). notes: new free-form notes. Card identity and type (BUY/SELL) cannot be changed via this endpoint - delete and re-create instead. Requires IWMM_API_KEY. |
delete_transaction |
name_restates_behavior params_unexplained no_return_description |
Permanently remove a previously recorded transaction by its ID (the id returned by list_transactions or record_transaction), so it no longer appears in the user's history. Returns confirmation once the deletion completes. Requires IWMM_API_KEY. |
get_cost_basis |
params_unexplained no_return_description |
Return the FIFO cost basis for one card and finish for the authenticated user (the acquisition cost computed in first-in-first-out order), identified either by cardId or by (setCode, setNumber). cardId: internal card UUID - use search_cards or get_card. setCode: the card's set code (used with setNumber). setNumber: the card's collector number within the set (used with setCode). isFoil: whether to use the foil or non-foil finish (default false). Requires IWMM_API_KEY. |
get_portfolio_history |
name_restates_behavior no_return_description |
Return the authenticated user's portfolio total value over time as a series of dated value points: days: how many days of history to fetch (1-3650), server default applies if omitted. Premium-gated: free tier receives 403. Requires IWMM_API_KEY. |
get_card_performance |
params_unexplained no_return_description |
Return the authenticated user's best- or worst-performing cards ranked by P&L, with per-card performance figures (defaults to the best, top 10). type: 'best' ranks by highest P&L, 'worst' by lowest. limit: how many cards to return (1-100). Premium-gated. Requires IWMM_API_KEY. |
get_cash_flow |
no_return_description |
Return the user's cash flow totals: money in from SELL transactions versus money out from BUY transactions, including the net difference, for the authenticated user. Premium-gated. Requires IWMM_API_KEY. |
get_realized_gains |
no_return_description |
Return the authenticated user's realized gains (and losses) computed from SELL transactions against FIFO cost basis, i.e. sale proceeds minus the FIFO cost of the cards sold. Premium-gated. Requires IWMM_API_KEY. |
get_portfolio_breakdown_cards |
no_return_description |
Return the individual cards inside one slice of a portfolio breakdown, each with its value and count, matching the aggregate shown for that slice; this is the drill-down for get_portfolio_breakdown. by, key, and colors must match the original breakdown call. Premium-gated. Requires IWMM_API_KEY. |
refresh_portfolio |
no_return_description |
Recalculate the authenticated user's portfolio P&L figures so portfolio summaries, history, and breakdowns reflect recent activity, and return confirmation once recalculation completes. Use after recording a batch of transactions if you want immediate fresh numbers. Requires IWMM_API_KEY. |
get_deck |
name_restates_behavior no_return_description |
Fetch a single deck owned by the authenticated user identified by deckId (from list_decks or create_deck), returning the deck's metadata plus its card contents with quantities and whether each card is in the mainboard or sideboard. Requires IWMM_API_KEY. |
create_deck |
no_return_description |
Create a new empty deck for the authenticated user and return the new deck (with its id) ready to be filled. This is a real write. Use add_deck_card to fill it, or import_deck to create from pasted text instead. Requires IWMM_API_KEY. |
update_deck |
no_return_description |
Rename a deck or change its format; omitting format clears the deck's format. Returns the updated deck with its new name and format. Requires IWMM_API_KEY. |
delete_deck |
name_restates_behavior no_return_description |
Permanently remove a deck and all of the cards it contains, so it no longer appears in list_decks. This is permanent, and returns confirmation of the deletion. Requires IWMM_API_KEY. |
add_deck_card |
no_return_description |
Add a card to a deck, incrementing its quantity (creating the row if absent), and return the updated row for that card + board in the deck. Use set_deck_card_quantity to set an absolute quantity or remove_deck_card to delete a row. Requires IWMM_API_KEY. |
set_deck_card_quantity |
no_return_description |
Set the absolute quantity for a card + board in a deck (not a delta); a quantity of 0 removes the row. Returns the updated row with its new quantity, or confirmation of removal when set to 0. Use add_deck_card to increment instead. Requires IWMM_API_KEY. |
remove_deck_card |
name_restates_behavior no_return_description |
Permanently removes a card from a deck together with its whole board row - mainboard and sideboard are tracked as separate rows, so pass isSideboard true for the sideboard row and false for the mainboard row. Get deckId from list_decks or create_deck and cardId from search_cards or get_card. Returns confirmation that the card and its board row were removed. Requires IWMM_API_KEY. |
list_price_alerts |
no_return_description |
List the authenticated user's price alerts, returning for each alert its id, the target card, the increase/decrease threshold percentages, and whether it is active. Free tier is capped at 5 active alerts and a single threshold direction per alert; Premium removes both limits. Requires IWMM_API_KEY. |
create_price_alert |
no_return_description |
Create a price alert for a card and return the new alert including its id and the thresholds that were set. At least one of increasePct (trigger when price rises by at least this percent) or decreasePct (trigger when price falls by at least this percent) must be supplied; both are allowed on Premium. Requires IWMM_API_KEY. |
update_price_alert |
params_unexplained no_return_description |
Update an existing price alert by id. increasePct sets the percent increase that triggers the alert; pass null to clear the increase trigger. decreasePct sets the percent decrease trigger; pass null to clear it. Free users must keep exactly one threshold direction; Premium users may clear both. isActive toggles the alert between enabled (true) and disabled (false) without deleting it. Returns the updated alert with its id, thresholds, and active status. Requires IWMM_API_KEY. |
delete_price_alert |
name_restates_behavior params_unexplained no_return_description |
Permanently deletes a price alert so it stops monitoring price changes. id is the numeric alert identifier returned by list_price_alerts. Returns confirmation that the alert was deleted. Requires IWMM_API_KEY. |
list_notifications |
no_return_description |
List the authenticated user's price alert notifications, newest first, returning each notification's id, the alert that triggered it, when it fired, and its read/unread status. Includes both read and unread notifications. Requires IWMM_API_KEY. |
get_unread_notification_count |
name_restates_behavior no_return_description |
Returns the integer count of notifications for the authenticated user that have not yet been marked read, e.g. for an unread badge or indicator. Marking notifications read (mark_notification_read or mark_all_notifications_read) reduces this count. Requires IWMM_API_KEY. |
mark_notification_read |
name_restates_behavior params_unexplained no_return_description |
Marks a single notification as read so it no longer counts toward the unread total from get_unread_notification_count. id is the numeric notification identifier returned by list_notifications. Returns confirmation that the notification was marked read. Requires IWMM_API_KEY. |
mark_all_notifications_read |
name_restates_behavior no_return_description |
Marks every notification for the authenticated user as read in one call, clearing the unread count returned by get_unread_notification_count - useful for quickly resetting a notification badge. Returns confirmation that all notifications were marked read. Requires IWMM_API_KEY. |
Selection evidence
Confusable tool pairs.
15 pairs where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
get_card |
get_card_prices |
high | A task like 'what's the current price of this card?' maps to both: get_card_prices returns current normal/foil prices, but get_card also returns full detail 'including current prices,' so an agent may grab the broader tool or the specific one and either works, making the wrong pick plausible. |
get_card_prices |
get_card_price_history |
medium | 'Check this card's price' can mean current value or its trend; names differ only by the word 'history' and inputs are identical, so a task like 'show me how this card's price has moved' may select get_card_prices instead of the history tool. |
get_portfolio_breakdown |
get_portfolio_breakdown_cards |
medium | 'Show my portfolio breakdown' is ambiguous between aggregate slices and the actual cards inside a slice; the near-identical names plus cross-referencing descriptions make a vague drill-down request map to either tool. |
mark_notification_read |
mark_all_notifications_read |
medium | 'Mark my notifications as read' without the word 'all' is ambiguous: plural 'notifications' could mean a specific item or every notification, risking the heavier mark-all action when only one was intended. |
list_inventory |
list_sealed_inventory |
medium | A plain 'show me my inventory/collection' task is genuinely ambiguous since loose cards and sealed products are both inventory; only the descriptions distinguish them, so the agent must guess which scope the user meant. |
remove_inventory |
remove_sealed_inventory |
medium | 'Remove this box from my inventory' likely gets phrased as just 'remove this from my inventory'; the user may not know sealed items use a separate tool, so the agent must infer from the item type which remove variant to call. |
add_buy_list |
update_buy_list |
medium | 'I need 3 copies on my buy list' is ambiguous between add (delta increment) and update (absolute quantity) since both take cardId and quantity; only the descriptions explain the delta-vs-absolute difference. |
add_deck_card |
set_deck_card_quantity |
medium | Same increment-vs-absolute ambiguity as the buy list: 'I want 4 copies of this card in the deck' could map to add_deck_card (increment) or set_deck_card_quantity (set absolute), and both accept a quantity parameter. |
get_sealed_products |
get_sealed_product |
low | 'Get the sealed product for this set' is ambiguous on singular/plural; descriptions clarify a UUID-detail vs listing split, but a task referencing a product by name (no UUID) could plausibly pick the detail tool and fail. |
add_buy_list |
import_buy_list |
low | 'Add these several cards to my buy list' could be read as repeated per-card add calls or as the bulk CSV import; the import tool's distinct 'text' input usually disambiguates, but a natural-language bulk request still trips an agent occasionally. |
get_card_prices |
get_card_buylist |
low | 'What's this card going for / what can I get for it' is ambiguous between retail (get_card_prices) and sell-to-vendor (get_card_buylist); descriptions cross-reference each other, but a sell-side phrasing can still land on the retail tool. |
get_card |
get_card_buylist |
low | 'How much can I sell this card for' might be answered by get_card since it already returns 'current prices'; the buylist tool is the correct sell-side source but its purpose is easy to miss beside the generic get_card. |
get_cash_vs_credit |
get_cash_flow |
low | A vague 'how's my cash situation' task is ambiguous between the cash-vs-credit buylist recommendation and the money-in/out cash flow report; both are cash analytics with no inputs, and the names differ only by the vs/flow token. |
get_set |
get_set_price_history |
low | 'How much is MH3 worth / what's MH3's price' could map to the set's aggregate price history or to plain set detail; descriptions only partially disambiguate since neither tool obviously owns 'set value'. |
get_portfolio_summary |
get_portfolio_breakdown |
low | 'Show me my portfolio' could mean the summary figure (value, P&L) or a value breakdown by dimension; both are no/minimal-input portfolio analytics, so a general portfolio question can go either way. |
Compare the field