checkout
Preview or complete Costco orders with browser automation. Use confirm=false to preview before finalizing purchases.
Instructions
Preview or complete a Costco order. Use confirm=false to preview first.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Set true to actually place the order. Default false (preview only). |
Implementation Reference
- src/index.ts:1114-1215 (handler)The handleCheckout function implements the checkout tool logic. It validates user login status, retrieves cart contents from Costco, displays an order summary with items, subtotal, tax, and total. When confirm=true, it navigates to checkout and attempts to place the order, returning order confirmation details.
async function handleCheckout(confirm: boolean) { if (!isLoggedIn()) { return err("Not logged in. Use the `login` tool first."); } return withPage(async (page: Page) => { await page.goto("https://www.costco.com/CheckoutCartDisplayView", { waitUntil: "domcontentloaded", timeout: 30000, }); await page.waitForTimeout(2000); const cartSummary = await page.evaluate(() => { const items = Array.from( document.querySelectorAll('.cart-item, [automation-id="cart-item"], [class*="cart-item"]') ).map((item) => { const title = item.querySelector('[automation-id="cart-item-name"], .item-name')?.textContent?.trim() ?? ""; const price = item.querySelector('[automation-id="cart-item-price"], .price')?.textContent?.trim() ?? ""; const qty = (item.querySelector('[automation-id="cart-item-qty"], input[name*="qty"]') as HTMLInputElement | null) ?.value?.trim() ?? "1"; return `${title} (x${qty}) — ${price}`; }); const subtotal = document.querySelector('[automation-id="order-subtotal"], .subtotal')?.textContent?.trim() ?? ""; const tax = document.querySelector('[automation-id="order-tax"], .tax')?.textContent?.trim() ?? ""; const total = document.querySelector('[automation-id="order-total"], .order-total')?.textContent?.trim() ?? ""; return { items, subtotal, tax, total }; }); if (cartSummary.items.length === 0) { return err("Cart is empty. Add items before checking out."); } const summary = [ `**Order Summary (${cartSummary.items.length} item${cartSummary.items.length !== 1 ? "s" : ""})**\n`, ...cartSummary.items.map((item, i) => `${i + 1}. ${item}`), "", cartSummary.subtotal ? `Subtotal: ${cartSummary.subtotal}` : "", cartSummary.tax ? `Tax: ${cartSummary.tax}` : "", cartSummary.total ? `Total: ${cartSummary.total}` : "", ].filter(Boolean); if (!confirm) { return ok( summary.join("\n") + "\n\n⚠️ This is a preview. Call `checkout` with `confirm: true` to place the order." ); } // Proceed to checkout const checkoutBtn = await page.waitForSelector( '[automation-id="checkout-btn"], button[class*="checkout"], a[href*="checkout" i]', { timeout: 10000 } ); await checkoutBtn.click(); await page.waitForTimeout(3000); const currentUrl = page.url(); if (!currentUrl.toLowerCase().includes("checkout")) { return err( "Failed to navigate to checkout. May require additional verification." ); } // Try to place order const placeOrderBtn = await page.$( '[automation-id="place-order-btn"], button[class*="place-order"], button[id*="placeOrder"]' ); if (!placeOrderBtn) { return ok( summary.join("\n") + "\n\n⚠️ Reached checkout page but could not auto-submit. Please complete manually at: " + currentUrl ); } await placeOrderBtn.click(); await page.waitForTimeout(5000); const confirmationUrl = page.url(); const orderConfirmation = await page.evaluate(() => { const orderNum = document.querySelector( '[automation-id="order-number"], [class*="order-number"], [class*="orderNumber"]' )?.textContent?.trim(); return { orderNum }; }); return ok( summary.join("\n") + "\n\n✅ Order placed successfully!\n" + (orderConfirmation.orderNum ? `Order #: ${orderConfirmation.orderNum}\n` : "") + `Confirmation URL: ${confirmationUrl}` ); }); } - src/index.ts:188-201 (registration)Registration of the 'checkout' tool in the MCP server's ListToolsRequestSchema handler. Defines the tool name, description, and inputSchema with a 'confirm' boolean parameter to control whether to preview or place the order.
name: "checkout", description: "Preview or complete a Costco order. Use confirm=false to preview first.", inputSchema: { type: "object", properties: { confirm: { type: "boolean", description: "Set true to actually place the order. Default false (preview only).", }, }, required: [], }, }, - src/index.ts:336-337 (registration)Routing logic in the CallToolRequestSchema handler that maps the 'checkout' tool name to the handleCheckout function, passing the confirm parameter.
case "checkout": return await handleCheckout(a.confirm === true); - src/browser.ts:104-118 (helper)The withPage helper function manages Playwright browser context and page lifecycle. It creates a new page, adds stealth scripts to avoid bot detection, executes the provided function, saves session cookies, and ensures proper cleanup.
export async function withPage<T>( fn: (page: Page) => Promise<T>, headless = true ): Promise<T> { const ctx = await getBrowserContext(headless); const page = await ctx.newPage(); await page.addInitScript(STEALTH_INIT_SCRIPT); try { const result = await fn(page); await saveSessionCookies(); return result; } finally { await page.close(); } } - src/session.ts:53-55 (helper)The isLoggedIn helper function checks if the user is authenticated by verifying that both cookies.json and auth.json files exist in the session directory.
export function isLoggedIn(): boolean { return fs.existsSync(COOKIES_FILE) && fs.existsSync(AUTH_FILE); }