record_transaction
Record a buy or sell transaction for MTG cards. Automatically adjusts inventory by adding or subtracting quantities.
Instructions
Record a buy or sell transaction. By default this also adjusts inventory (BUY adds, SELL subtracts). This is a real write. Requires IWMM_API_KEY.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| cardId | Yes | Internal IWMM card UUID. | |
| type | Yes | ||
| quantity | Yes | ||
| pricePerUnit | Yes | Per-unit price in USD. | |
| isFoil | Yes | ||
| date | Yes | ISO 8601 date (YYYY-MM-DD). | |
| source | No | Where the transaction happened (e.g. 'TCGPlayer', 'LGS'). | |
| fees | No | ||
| notes | No | ||
| skipInventorySync | No | If true, record the transaction without adjusting inventory. Default false - transactions normally update inventory. |
Implementation Reference
- src/tools/transactions.ts:48-55 (handler)The main handler for the record_transaction tool. It sends a POST request to /api/v1/transactions with the validated input body, requiring authentication. The handler accepts a transactionCreate schema (cardId, type, quantity, pricePerUnit, isFoil, date, optional source/fees/notes/skipInventorySync) and calls apiFetch.
export const recordTransactionTool = { name: "record_transaction", description: "Record a buy or sell transaction. By default this also adjusts inventory (BUY adds, SELL subtracts). This is a real write. Requires IWMM_API_KEY.", inputSchema: transactionCreate, handler: (input: z.infer<typeof transactionCreate>) => apiFetch({ path: "/api/v1/transactions", method: "POST", body: input, authenticated: true }), }; - src/tools/transactions.ts:4-18 (schema)The Zod input schema (transactionCreate) for record_transaction, defining validation rules for cardId (string UUID), type (BUY/SELL), quantity (positive int), pricePerUnit (non-negative number), isFoil (boolean), date (ISO 8601), and optional source, fees, notes, skipInventorySync fields.
const transactionCreate = z.object({ cardId: z.string().describe("Internal IWMM card UUID."), type: z.enum(["BUY", "SELL"]), quantity: z.number().int().min(1), pricePerUnit: z.number().min(0).describe("Per-unit price in USD."), isFoil: z.boolean(), date: z.string().describe("ISO 8601 date (YYYY-MM-DD)."), source: z.string().optional().describe("Where the transaction happened (e.g. 'TCGPlayer', 'LGS')."), fees: z.number().min(0).optional(), notes: z.string().optional(), skipInventorySync: z .boolean() .optional() .describe("If true, record the transaction without adjusting inventory. Default false - transactions normally update inventory."), }); - src/tools/index.ts:65-66 (registration)recordTransactionTool is imported from transactions.ts and registered in the tools array under 'Transactions (auth)' section. Also mapped in toolsByName for dispatch by the server.
listTransactionsTool, recordTransactionTool, - src/tools/index.ts:41-45 (registration)The ToolDefinition interface that recordTransactionTool conforms to, defining the shape: name, description, inputSchema, and handler.
export interface ToolDefinition { name: string; description: string; inputSchema: z.ZodTypeAny; handler: (input: any) => Promise<unknown>; - src/api-client.ts:26-67 (helper)The apiFetch helper function used by the handler to make authenticated HTTP requests to the IWMM backend API with bearer token authorization.
export async function apiFetch<T = unknown>(req: ApiRequest): Promise<T> { const url = new URL(req.path, config.baseUrl); if (req.query) { for (const [k, v] of Object.entries(req.query)) { if (v !== undefined && v !== null && v !== "") { url.searchParams.set(k, String(v)); } } } const headers: Record<string, string> = { Accept: "application/json", "User-Agent": "iwantmymtg-mcp/0.0.1", }; if (req.authenticated) { const { requireApiKey } = await import("./config.js"); headers["Authorization"] = `Bearer ${requireApiKey()}`; } if (req.body !== undefined) { headers["Content-Type"] = "application/json"; } const res = await fetch(url, { method: req.method ?? "GET", headers, body: req.body !== undefined ? JSON.stringify(req.body) : undefined, }); if (!res.ok) { const text = await res.text(); throw new ApiError(res.status, text, { limit: res.headers.get("X-RateLimit-Limit") ?? undefined, remaining: res.headers.get("X-RateLimit-Remaining") ?? undefined, reset: res.headers.get("X-RateLimit-Reset") ?? undefined, }); } if (res.status === 204) return undefined as T; return (await res.json()) as T; }