Skip to main content
Glama
index.ts17.6 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; import puppeteer, { Browser, Page } from "puppeteer"; import fetch from "node-fetch"; // Cookie storage let sessionCookies: any[] = []; let browser: Browser | null = null; let currentPage: Page | null = null; // Tool definitions const TOOLS: Tool[] = [ { name: "woolworths_open_browser", description: "Opens a browser and navigates to Woolworths website. This is the first step to establish a session.", inputSchema: { type: "object", properties: { headless: { type: "boolean", description: "Whether to run browser in headless mode (default: false for easier login)", default: false, }, }, }, }, { name: "woolworths_navigate", description: "Navigate to a specific URL on the Woolworths website", inputSchema: { type: "object", properties: { url: { type: "string", description: "The URL to navigate to", }, }, required: ["url"], }, }, { name: "woolworths_get_cookies", description: "Retrieves session cookies from the current browser session. Run this after logging in or establishing a session.", inputSchema: { type: "object", properties: {}, }, }, { name: "woolworths_close_browser", description: "Closes the browser instance", inputSchema: { type: "object", properties: {}, }, }, { name: "woolworths_search_products", description: "Search for products on Woolworths. Requires session cookies to be obtained first.", inputSchema: { type: "object", properties: { searchTerm: { type: "string", description: "The product search term", }, pageNumber: { type: "number", description: "Page number for pagination (default: 1)", default: 1, }, pageSize: { type: "number", description: "Number of results to return (default: 36)", default: 36, }, sortType: { type: "string", description: "Sort order: TraderRelevance, PriceAsc, PriceDesc, Name (default: TraderRelevance)", enum: ["TraderRelevance", "PriceAsc", "PriceDesc", "Name"], default: "TraderRelevance", }, isSpecial: { type: "boolean", description: "Filter for special offers only (default: false)", default: false, }, }, required: ["searchTerm"], }, }, { name: "woolworths_get_product_details", description: "Get detailed information about a specific product by its stockcode", inputSchema: { type: "object", properties: { stockcode: { type: "string", description: "The product stockcode/ID", }, }, required: ["stockcode"], }, }, { name: "woolworths_get_specials", description: "Get current specials and deals from Woolworths", inputSchema: { type: "object", properties: { category: { type: "string", description: "Optional category filter (e.g., 'fruit-veg', 'meat-seafood')", }, pageSize: { type: "number", description: "Number of results to return (default: 20)", default: 20, }, }, }, }, { name: "woolworths_get_categories", description: "Get the list of available product categories", inputSchema: { type: "object", properties: {}, }, }, { name: "woolworths_add_to_cart", description: "Add a product to the shopping cart/trolley", inputSchema: { type: "object", properties: { stockcode: { type: "number", description: "The product stockcode/ID to add", }, quantity: { type: "number", description: "Quantity to add (default: 1)", default: 1, }, }, required: ["stockcode"], }, }, { name: "woolworths_get_cart", description: "Get the contents of the shopping cart/trolley", inputSchema: { type: "object", properties: {}, }, }, { name: "woolworths_remove_from_cart", description: "Remove a product from the shopping cart/trolley", inputSchema: { type: "object", properties: { stockcode: { type: "number", description: "The product stockcode/ID to remove", }, }, required: ["stockcode"], }, }, { name: "woolworths_update_cart_quantity", description: "Update the quantity of a product in the shopping cart/trolley", inputSchema: { type: "object", properties: { stockcode: { type: "number", description: "The product stockcode/ID", }, quantity: { type: "number", description: "New quantity", }, }, required: ["stockcode", "quantity"], }, }, ]; // Helper function to get cookie header string function getCookieHeader(): string { return sessionCookies.map((c) => `${c.name}=${c.value}`).join("; "); } // Helper function to make API requests with cookies async function makeWoolworthsRequest( url: string, options: any = {} ): Promise<any> { if (sessionCookies.length === 0) { throw new Error( "No session cookies available. Please use woolworths_get_cookies first." ); } const headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", Accept: "*/*", "Accept-Language": "en-US,en;q=0.9", Origin: "https://www.woolworths.com.au", Referer: "https://www.woolworths.com.au/", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", Priority: "u=1, i", Cookie: getCookieHeader(), ...options.headers, }; const response = await fetch(url, { ...options, headers, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}. ${errorText}` ); } return response.json(); } // Tool handlers async function handleOpenBrowser(args: any): Promise<any> { if (browser) { return { success: false, message: "Browser is already open. Close it first with woolworths_close_browser.", }; } const headless = args.headless ?? false; browser = await puppeteer.launch({ headless, args: [ "--no-sandbox", "--disable-setuid-sandbox", "--disable-blink-features=AutomationControlled", ], defaultViewport: { width: 1280, height: 800 }, }); currentPage = await browser.newPage(); // Set user agent to appear more like a real browser await currentPage.setUserAgent( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ); // Navigate to Woolworths homepage await currentPage.goto("https://www.woolworths.com.au", { waitUntil: "networkidle2", }); return { success: true, message: "Browser opened and navigated to Woolworths homepage. You can now log in manually if needed, then use woolworths_get_cookies to capture the session.", url: currentPage.url(), }; } async function handleNavigate(args: any): Promise<any> { if (!browser || !currentPage) { throw new Error("Browser is not open. Use woolworths_open_browser first."); } await currentPage.goto(args.url, { waitUntil: "networkidle2" }); return { success: true, url: currentPage.url(), title: await currentPage.title(), }; } async function handleGetCookies(args: any): Promise<any> { if (!browser || !currentPage) { throw new Error("Browser is not open. Use woolworths_open_browser first."); } const cookies = await currentPage.cookies(); sessionCookies = cookies; return { success: true, message: `Captured ${cookies.length} cookies from the current session.`, cookies: cookies.map((c) => ({ name: c.name, domain: c.domain, path: c.path, secure: c.secure, httpOnly: c.httpOnly, })), }; } async function handleCloseBrowser(args: any): Promise<any> { if (!browser) { return { success: false, message: "Browser is not open.", }; } await browser.close(); browser = null; currentPage = null; return { success: true, message: "Browser closed. Session cookies have been preserved.", }; } async function handleSearchProducts(args: any): Promise<any> { const searchTerm = args.searchTerm; const pageNumber = args.pageNumber ?? 1; const pageSize = args.pageSize ?? 36; const sortType = args.sortType ?? "TraderRelevance"; const isSpecial = args.isSpecial ?? false; // Woolworths search API endpoint (POST method) const url = `https://www.woolworths.com.au/apis/ui/Search/products`; const requestBody = { searchTerm, pageNumber, pageSize, sortType, location: `/shop/search/products?searchTerm=${encodeURIComponent(searchTerm)}`, formatObject: JSON.stringify({ name: searchTerm }), isSpecial, isBundle: false, isMobile: false, filters: [], groupEdmVariants: false, }; try { const data = await makeWoolworthsRequest(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); return { success: true, searchTerm, totalResults: data.SearchResultsCount || 0, products: data.Products || [], pagination: data.Pagination || { TotalRecords: data.SearchResultsCount || 0, PageNumber: pageNumber, PageSize: pageSize, }, }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleGetProductDetails(args: any): Promise<any> { const stockcode = args.stockcode; const url = `https://www.woolworths.com.au/apis/ui/product/detail/${stockcode}`; try { const data = await makeWoolworthsRequest(url); return { success: true, product: data, }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleGetSpecials(args: any): Promise<any> { const category = args.category || ""; const pageSize = args.pageSize ?? 20; let url = `https://www.woolworths.com.au/apis/ui/browse/category`; if (category) { url += `?category=${encodeURIComponent(category)}&filter=Specials&pageSize=${pageSize}`; } else { url += `?category=specials&pageSize=${pageSize}`; } try { const data = await makeWoolworthsRequest(url); return { success: true, category: category || "all", totalResults: data.TotalRecordCount || 0, products: data.Products || data.Bundles || [], }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleGetCategories(args: any): Promise<any> { const url = `https://www.woolworths.com.au/apis/ui/PiesCategoriesWithSpecials`; try { const data = await makeWoolworthsRequest(url); return { success: true, categories: data, }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleAddToCart(args: any): Promise<any> { const stockcode = args.stockcode; const quantity = args.quantity ?? 1; const url = `https://www.woolworths.com.au/api/v3/ui/trolley/update`; try { const data = await makeWoolworthsRequest(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ items: [ { stockcode, quantity, source: "ProductDetail", diagnostics: "0", searchTerm: null, evaluateRewardPoints: false, offerId: null, profileId: null, priceLevel: null, }, ], }), }); return { success: true, cart: data, }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleGetCart(args: any): Promise<any> { const url = `https://www.woolworths.com.au/apis/ui/Trolley`; try { const data = await makeWoolworthsRequest(url); return { success: true, cart: data, }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleRemoveFromCart(args: any): Promise<any> { const stockcode = args.stockcode; const url = `https://www.woolworths.com.au/api/v3/ui/trolley/update`; try { const data = await makeWoolworthsRequest(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ items: [ { stockcode, quantity: 0, source: "ProductDetail", diagnostics: "0", searchTerm: null, evaluateRewardPoints: false, offerId: null, profileId: null, priceLevel: null, }, ], }), }); return { success: true, cart: data, }; } catch (error: any) { return { success: false, error: error.message, }; } } async function handleUpdateCartQuantity(args: any): Promise<any> { const stockcode = args.stockcode; const quantity = args.quantity; const url = `https://www.woolworths.com.au/api/v3/ui/trolley/update`; try { const data = await makeWoolworthsRequest(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ items: [ { stockcode, quantity, source: "ProductDetail", diagnostics: "0", searchTerm: null, evaluateRewardPoints: false, offerId: null, profileId: null, priceLevel: null, }, ], }), }); return { success: true, cart: data, }; } catch (error: any) { return { success: false, error: error.message, }; } } // Main server setup async function main() { const server = new Server( { name: "woolworths-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS, })); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { let result; switch (name) { case "woolworths_open_browser": result = await handleOpenBrowser(args || {}); break; case "woolworths_navigate": result = await handleNavigate(args || {}); break; case "woolworths_get_cookies": result = await handleGetCookies(args || {}); break; case "woolworths_close_browser": result = await handleCloseBrowser(args || {}); break; case "woolworths_search_products": result = await handleSearchProducts(args || {}); break; case "woolworths_get_product_details": result = await handleGetProductDetails(args || {}); break; case "woolworths_get_specials": result = await handleGetSpecials(args || {}); break; case "woolworths_get_categories": result = await handleGetCategories(args || {}); break; case "woolworths_add_to_cart": result = await handleAddToCart(args || {}); break; case "woolworths_get_cart": result = await handleGetCart(args || {}); break; case "woolworths_remove_from_cart": result = await handleRemoveFromCart(args || {}); break; case "woolworths_update_cart_quantity": result = await handleUpdateCartQuantity(args || {}); break; default: throw new Error(`Unknown tool: ${name}`); } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error: any) { return { content: [ { type: "text", text: JSON.stringify( { success: false, error: error.message, }, null, 2 ), }, ], isError: true, }; } }); // Cleanup on exit process.on("SIGINT", async () => { if (browser) { await browser.close(); } process.exit(0); }); // Start the server const transport = new StdioServerTransport(); await server.connect(transport); console.error("Woolworths MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

Implementation Reference

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/elijah-g/Woolworths-mcp'

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