Skip to main content
Glama

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
NameRequiredDescriptionDefault
confirmNoSet true to actually place the order. Default false (preview only).

Implementation Reference

  • 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);
  • 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();
      }
    }
  • 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);
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/markswendsen-code/mcp-costco'

If you have feedback or need assistance with the MCP directory API, please join our Discord server