Build a Minimal WebMCP Agent with Playwright and Gemini A developer built a minimal WebMCP agent using Playwright and the Gemini API to test WebMCP tools with stronger AI models. The agent uses Playwright to control a real Chrome browser, enabling tool discovery and execution beyond the limitations of the Model Context Tool Inspector. The project demonstrates how to wire Gemini's tool-calling capabilities with WebMCP in a Node.js environment. WebMCP lets a web page expose tools that AI agents can discover and execute inside the browser. That sounds simple until you want to test those tools with a model outside the Model Context Tool Inspector https://chromewebstore.google.com/detail/webmcp-model-context-tool/gbpdfapgefenggkahomfgkhfehlcenpd Chrome extension. A while ago, I built a small puzzle game https://dev.to/gramli/tower-before-dusk-i-built-a-puzzle-game-for-humans-and-ai-oao that exposes WebMCP tools. I tested and debugged those tools using the Model Context Tool Inspector, which is great for quick experiments, the limitation is that it only gives access to a small set of lightweight Gemini models and I wanted to test the same WebMCP tools with stronger ones. My first idea was to build another Chrome extension, but that felt like overkill. WebMCP tools need a real browser context: the browser must open the page directly, discover the tools and execute them inside the page. So instead of building another extension, I looked for something that could simply open Chrome and control the page. And that is where Playwright fits nicely. So in this article, I will show how to create a simple agent that wires up the Gemini API with WebMCP through Playwright. Gemini requests a tool call and Playwright executes the matching WebMCP tool inside a real Chrome browser. For this example, you need: The first thing we need to do is enable WebMCP in Chrome https://developer.chrome.com/docs/ai/webmcp . WebMCP is still experimental, so for local development it must be enabled through a Chrome flag: Open Chrome and navigate to chrome://flags/ enable-webmcp-testing Set the flag to Enabled. Relaunch Chrome to apply the changes. After that, we can create a small Node.js project: mkdir custom-agent cd custom-agent npm init -y Next, install Playwright as a development dependency. I also use tsx to run TypeScript files directly and dotenv to read environment variables from a .env file: npm install -D playwright tsx dotenv typescript @types/node This gives us everything we need to run TypeScript code, open Chrome and access environment variables. Because the agent will also call an AI model, we need to install the Gemini SDK. For this example, I use @google/genai : npm install @google/genai The last preparation step is to add a script to package.json : { "scripts": { "agent": "tsx agent.ts" } } This command will run the agent.ts file, where we will put the main logic. Now that the project is prepared, let’s create the first version of agent.ts . At this stage, I only want to check whether modelContext is available inside the browser page. js import { chromium } from "playwright"; const gameUrl = process.argv 2 ?? "http://localhost:5173"; async function main { const context = await chromium.launchPersistentContext "./.chrome-agent-profile", { channel: "chrome", headless: false, args: "--enable-experimental-web-platform-features" , }, ; const page = await context.newPage ; await page.goto gameUrl, { waitUntil: "networkidle" } ; const result = await page.evaluate = { userAgent: navigator.userAgent, hasNavigatorModelContext: "modelContext" in navigator, hasDocumentModelContext: "modelContext" in document, } ; console.log result ; } main .catch error = { console.error error ; process.exit 1 ; } ; This code opens Chrome, navigates to the game page, and checks if modelContext exists on navigator or document . One important detail is that I am not using the bundled Chromium from Playwright. Instead, I am opening the real Chrome installed on my machine by using launchPersistentContext with channel: "chrome" . This matters because WebMCP is still experimental. In my case, the isolated Chromium browser did not discover the WebMCP tools correctly, while real Chrome with the enabled flag worked. Note: Because launchPersistentContext creates a local Chrome profile, do not forget to add this folder to .gitignore : .chrome-agent-profile/ The profile can contain local browser data such as cache, cookies, and other Chrome state. It should not be committed to the repository. The first check only tells us whether modelContext exists. The next step is to read the tools exposed by the page. We can do that by calling modelContext.getTools inside the page.evaluate method: js const result = await page.evaluate async = { const modelContext = navigator.modelContext; if modelContext { return { hasModelContext: false, tools: , }; } const tools = await modelContext.getTools ; return { hasModelContext: true, tools: tools.map tool = { name: tool.name, description: tool.description, inputSchema: tool.inputSchema, origin: tool.origin, } , }; } ; This code returns the list of tools exposed by the current page. For each tool, I print basic metadata such as the name, description, input schema and origin. At this point, it is useful to print the result as formatted JSON: console.log JSON.stringify result, null, 2 ; This makes it easier to verify that Chrome discovered the WebMCP tools correctly. Reading tools is useful, but the real goal is to execute them. In my game, one of the exposed tools is called getGameState . It returns the current state of the puzzle, including the map, remaining moves and collected wood. For the first test, I can find this tool by name and execute it directly: js const gameState = await page.evaluate async = { const modelContext = navigator as any .modelContext; if modelContext { throw new Error "modelContext is empty" ; } const tools = await modelContext.getTools ; const getGameStateTool = tools.find tool: any = tool.name === "getGameState" ; if getGameStateTool { throw new Error "getGameState tool not found" ; } return await modelContext.executeTool getGameStateTool, "{}" ; } ; This proves that Playwright can open the page, access modelContext , find a WebMCP tool and execute it inside the browser context. However, hardcoding the tool execution like this is not ideal. The agent should be able to execute any tool by name, so I extracted the logic into a reusable helper function: python import type { Page } from "playwright"; export async function executeWebMcpTool