Skip to main content
Glama
run-via-mcp.js•10.6 kB
// MCP-driven Google search: navigate to Google, accept/dismiss cookies if present, // perform a search for "Playwright", verify results contain the word, take a // full-page screenshot and save it to the project root as `google-playwright-search.png`. (async () => { try { const fs = await import('fs'); const path = await import('path'); const { Client } = await import('@modelcontextprotocol/sdk/client/index.js'); const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js'); const client = new Client({ name: 'mcp-runner', version: '1.0.0' }); // If an MCP server is already running on localhost:8931, reuse it. Otherwise attempt to spawn one. const serverUrl = 'http://localhost:8931/'; let child = null; let serverUp = false; try { const res = await (globalThis.fetch || fetch)(serverUrl, { method: 'GET' }); if (res && (res.status === 200 || res.status === 404 || res.status === 405)) { serverUp = true; console.log('Found existing Playwright MCP server at http://localhost:8931'); } } catch (e) { // not running } if (!serverUp) { const { spawn } = await import('child_process'); const serverCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const serverArgs = ['@playwright/mcp@latest', '--port', '8931', '--caps=vision,pdf,testing,tracing', '--output-dir=playwright-mcp-output']; console.log('Starting Playwright MCP server (HTTP) as child process...'); child = spawn(serverCmd, serverArgs, { stdio: ['ignore', 'pipe', 'pipe'] }); // Wait for the server to print the listening line await new Promise((resolve, reject) => { const onData = (data) => { const text = String(data); process.stdout.write(text); if (text.includes('Listening on')) { cleanup(); resolve(); } }; const onErr = (data) => { const text = String(data); process.stderr.write(text); }; const onExit = (code) => { cleanup(); reject(new Error('MCP server exited early with code ' + code)); }; const cleanup = () => { child.stdout.removeListener('data', onData); child.stderr.removeListener('data', onErr); child.removeListener('exit', onExit); }; child.stdout.on('data', onData); child.stderr.on('data', onErr); child.on('exit', onExit); }); serverUp = true; } // Connect to the server via Streamable HTTP transport const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/dist/cjs/client/streamableHttp.js'); const transport = new StreamableHTTPClientTransport('http://localhost:8931/mcp', { fetch: globalThis.fetch }); console.log('Connecting to MCP server via HTTP transport...'); await client.connect(transport); console.log('Connected to MCP server (HTTP)'); // Ensure browser is installed (idempotent) console.log('Calling tool: browser_install'); try { await client.callTool({ name: 'browser_install', arguments: {} }); } catch (err) { console.warn('browser_install failed or was unnecessary:', err?.message || err); } // 1) Navigate to Google console.log('Navigating to https://www.google.com'); await client.callTool({ name: 'browser_navigate', arguments: { url: 'https://www.google.com' } }); // 2) Try to accept/dismiss cookie dialogs by running a small evaluation in the page. console.log('Attempting to dismiss cookie dialog if present'); try { const clickResult = await client.callTool({ name: 'browser_evaluate', arguments: { function: `() => { const selectors = [ '#L2AGLb', 'button[aria-label="Accept all"]', 'button[id*="accept"]', 'button[jsname="higCR"]', 'button:contains("I agree")', 'button:contains("Accept")', 'form button' ]; for (const s of selectors) { try { const el = document.querySelector(s); if (el) { el.click(); return { clicked: true, selector: s }; } } catch (e) { /* ignore invalid selectors */ } } // Try common cookie button text search const textButtons = Array.from(document.querySelectorAll('button')); for (const b of textButtons) { const t = (b.innerText || '').trim().toLowerCase(); if (t.includes('agree') || t.includes('accept') || t.includes('consent')) { b.click(); return { clicked: true, text: t }; } } return { clicked: false }; }` } }); console.log('cookie dismiss result:', JSON.stringify(clickResult)); } catch (err) { console.warn('cookie dismiss attempt failed (continuing):', err?.message || err); } // 3) Type "Playwright" into the search box and press Enter console.log('Typing query and submitting'); try { await client.callTool({ name: 'browser_type', arguments: { selector: 'input[name="q"]', text: 'Playwright' } }); await client.callTool({ name: 'browser_press_key', arguments: { key: 'Enter' } }); } catch (err) { // Fallback: use evaluate to set the value and dispatch Enter console.warn('browser_type/browser_press_key not available, falling back to evaluate:', err?.message || err); await client.callTool({ name: 'browser_evaluate', arguments: { function: `() => { const q = document.querySelector('input[name="q"]'); if (!q) return { ok: false }; q.focus(); q.value = 'Playwright'; q.dispatchEvent(new Event('input', { bubbles: true })); const form = q.form; if (form) { form.submit(); return { ok: true, method: 'form.submit' }; } q.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', keyCode: 13, which: 13, bubbles: true })); return { ok: true, method: 'keydown' }; }` } }); } // 4) Wait for search results to load. Prefer waiting for '#search' or result snippets. console.log('Waiting for search results to appear'); let found = false; try { // Try the dedicated wait tool first await client.callTool({ name: 'browser_wait_for', arguments: { selector: '#search', timeout: 10000 } }); found = true; } catch (err) { // Fallback: poll the page for the presence of the word "Playwright" in body text const start = Date.now(); const timeout = 15000; while (Date.now() - start < timeout) { const r = await client.callTool({ name: 'browser_evaluate', arguments: { function: '() => document.body && document.body.innerText ? document.body.innerText : ""' } }); let body = ''; if (r && r.structuredContent) body = r.structuredContent; else if (Array.isArray(r?.content) && r.content.length) body = r.content[0].text || ''; if (typeof body === 'string' && body.toLowerCase().includes('playwright')) { found = true; break; } await new Promise((res) => setTimeout(res, 500)); } } if (!found) throw new Error('Search results did not appear or did not contain "Playwright" in time'); // 5) Verify the results page contains the word "Playwright" console.log('Verifying results contain the word "Playwright"'); const verify = await client.callTool({ name: 'browser_evaluate', arguments: { function: '() => (document.body && document.body.innerText || "").includes("Playwright")' } }); let verified = false; if (verify && typeof verify.structuredContent === 'boolean') verified = verify.structuredContent; else if (Array.isArray(verify?.content) && verify.content.length) { const txt = verify.content[0].text; verified = String(txt).toLowerCase().includes('playwright'); } if (!verified) throw new Error('Verification failed: results page does not contain the word "Playwright"'); console.log('Verification succeeded'); // 6) Take a full-page screenshot and save it locally in project root console.log('Taking full-page screenshot via MCP tool'); const ss = await client.callTool({ name: 'browser_take_screenshot', arguments: { fullPage: true } }); // Helper to locate base64 payload in tool result function extractBase64(res) { if (!res) return null; if (typeof res === 'string') return res; if (res.structuredContent && typeof res.structuredContent === 'string') return res.structuredContent; if (Array.isArray(res.content)) { for (const c of res.content) { if (c && typeof c.text === 'string' && c.text.length > 100) return c.text; if (c && typeof c.data === 'string') return c.data; } } if (res.content && typeof res.content === 'string') return res.content; return null; } let base64 = extractBase64(ss); if (!base64 && ss && ss.structuredContent && ss.structuredContent.data) base64 = ss.structuredContent.data; if (!base64) { console.log('No base64 found in response; saving fallback server-side filename if present'); // If server saved the file already (older behavior), try to return path info from tool result const fallbackPath = path.resolve(process.cwd(), 'playwright-mcp-output', 'google-playwright-search.png'); console.log('Fallback path (server-side):', fallbackPath); } else { // Data may be a data URL if (base64.startsWith('data:')) { const parts = base64.split(','); base64 = parts[1]; } const outPath = path.resolve(process.cwd(), 'google-playwright-search.png'); fs.writeFileSync(outPath, Buffer.from(base64, 'base64')); console.log('Saved screenshot to:', outPath); } // Disconnect / close transport (terminate spawned child process) try { if (typeof client.disconnect === 'function') { await client.disconnect(); } else if (transport && typeof transport.close === 'function') { transport.close(); } } catch (e) { console.warn('Error while disconnecting client:', e?.message || e); } try { child.kill(); } catch {} console.log('Disconnected and server stopped'); } catch (err) { console.error('Error running via MCP:', err && err.message ? err.message : err); process.exit(1); } })();

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/Sumit06-09/Playwright_MCP'

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