playwright-mcp-azure
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@playwright-mcp-azureOpen example.com, fill in the login form, and take a screenshot."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
playwright-mcp-azure
A remote MCP server that gives an MCP client general-purpose headless-browser control via Playwright, exposed over Streamable HTTP and deployable to Azure Container Apps.
A single process holds one shared headless Chromium browser. Each client connection gets its
own Playwright BrowserContext + active Page, tracked by an in-memory session manager with
idle eviction and a concurrency cap. Sessions are in-memory only — nothing persists across a
restart.
Prerequisites
Node.js 20+
npm
Docker (only needed for building/deploying the container)
Azure CLI (only needed for deploy)
Related MCP server: Playwright MCP Server
Local setup
npm install
npx playwright install chromium # one-time, downloads the Chromium binaryThe server requires a bearer token for auth:
export MCP_BEARER_TOKEN=some-local-dev-token # PowerShell: $env:MCP_BEARER_TOKEN = "..."
npm run devThis starts the server on http://localhost:3000, with the MCP endpoint mounted at /mcp
(Streamable HTTP) and an unauthenticated health check at /healthz.
Environment variables
Variable | Required | Default | Description |
| yes | — | Bearer token clients must send as |
| no |
| Milliseconds of inactivity before a session's browser context is closed. |
| no |
| Maximum concurrent browser sessions. |
| no |
| Port the HTTP server listens on. |
| no | — | Password |
Running tests
npm testIntegration tests launch real headless Chromium instances, so the Playwright browser install step above is required first.
Building
npm run build # compiles src/ to dist/
npm start # runs the compiled server (dist/server.js)Connecting an MCP client
Point an MCP client that supports Streamable HTTP at http://<host>:<port>/mcp, sending
Authorization: Bearer <MCP_BEARER_TOKEN> on every request. The server exposes these tools:
browser_start_session, browser_navigate, browser_navigate_back, browser_click,
browser_type, browser_fill_form, browser_login, browser_hover, browser_press_key,
browser_select_option, browser_handle_dialog, browser_snapshot, browser_find,
browser_screenshot, browser_evaluate, browser_wait_for, browser_tabs,
browser_network_requests, browser_console_messages, browser_close.
Browser sessions are identified by an app-level sessionId, not by the MCP transport's own
session id -- some MCP clients (notably Azure AI Foundry's agent tool integration) open a
brand-new transport connection for every single tool call and never reuse the transport-level
session, so nothing tied to that id would survive between calls. Call browser_start_session
once at the start of a task to get a sessionId, then pass that same sessionId as an argument
on every other browser_* call for that task. If a call returns an error naming
browser_start_session, the session expired or was closed (idle timeout, or an explicit
browser_close) -- start a new one and resume from there.
browser_snapshot returns a role/name listing of interactive elements, each stamped with a
ref (e.g. e3); pass that ref to browser_click/browser_type/browser_fill_form to act on
that element.
browser_network_requests records XHR and fetch GET/POST requests across every tab in the
session (up to the last 200; older entries drop off). Call it with { "action": "list" } for
{ url, method, requestBody, status, responseBody } per request, optionally adding
"urlPattern": "*/api/*" to filter by a *-wildcard match on the URL, or
{ "action": "clear" } to empty the log.
Logging in without exposing the password
browser_login fills the current page's password field with the password stored for that
page's hostname, without the value ever reaching the model. It takes only a sessionId: the
hostname of the current page selects the secret. It refuses — filling nothing and saying why —
unless all three hold:
the page is served over https;
a
PWD_<hostname>secret exists for the page's exact hostname (no parent-domain fallback: a secret forlinkedin.comis not used onwww.linkedin.com);the page's main frame shows exactly one visible
input[type="password"].
The tool only fills the password. Type the username yourself with browser_type first, and
submit the form yourself afterwards.
Every stored password is also a redaction pattern. Before any tool result leaves the server,
each text item is scanned for every stored value — raw, JSON-escaped, and percent-encoded —
and each occurrence is replaced with [REDACTED]. That covers browser_snapshot,
browser_find, browser_evaluate, browser_network_requests (whose captured request and
response bodies would otherwise contain the login POST), error messages, and the server's own
logs. Screenshots are not scrubbed, but browsers mask password inputs themselves.
Tool failures are never reported via the MCP protocol's isError flag -- Azure AI Foundry's
agent orchestration treats an isError result as a fatal tool-call failure and stops the run
instead of letting the model see and react to it. Instead, a failed call still returns a normal
result whose content is a JSON object like { "error": "<description>" }, so the model can read
the error and decide how to proceed (retry, call browser_start_session again, etc.).
Deploying to Azure Container Apps
Infrastructure (infra/main.bicep) provisions an Azure Container Registry, a Log Analytics
workspace, a Container Apps environment, and a single Container App (HTTPS-only ingress,
pinned to 1 replica). It needs a container image to deploy, and the registry doesn't exist
until the infra is applied — so the first deploy provisions with a placeholder image, then
infra/deploy.ps1 builds and pushes the real one.
Log in and create a resource group (skip if you already have one):
az login az group create --name rg-playwright-mcp --location westeuropeProvision the infrastructure with a placeholder image:
az deployment group create \ --resource-group rg-playwright-mcp \ --template-file infra/main.bicep \ --parameters mcpBearerToken=<a-strong-random-token> \ containerImage=mcr.microsoft.com/k8se/quickstart:latestNote the
acrLoginServeroutput (or a name prefix segment of it) — that's the-AcrNamevalue for the next step.Build, test, and deploy the real image:
./infra/deploy.ps1 -ResourceGroup rg-playwright-mcp -AcrName <acr-name-from-step-2>This runs
npm ci, installs the Chromium browser, builds, runs the test suite, then builds and pushes a Docker image and updates the Container App to use it. Re-run this script for subsequent deploys — the infra step only needs to run once (or again ifinfra/main.bicepchanges).Get the URL:
az containerapp show --name playwright-mcp-app --resource-group rg-playwright-mcp \ --query properties.configuration.ingress.fqdn -o tsvThe MCP endpoint is
https://<fqdn>/mcp.
To change IDLE_TIMEOUT_MS, MAX_SESSIONS, or PORT in Azure, edit their values in
infra/main.bicep's container env block and redeploy the infra step.
Storing a password for browser_login
./infra/set-login-secret.ps1 -ResourceGroup rg-playwright-mcp -Domain www.linkedin.comIt prompts for the password (so it stays out of your shell history), stores it as a Container
Apps secret named pwd-www-linkedin-com, and points the PWD_www.linkedin.com environment
variable at that secret. Both steps create a new revision, which restarts the container — the
server reads its secrets once at startup. Add -Remove to delete a stored password.
Warning: stored passwords do not survive a bicep deploy.
infra/main.bicepdeclares the fullsecretsandenvarrays, so re-running theaz deployment group createstep in this section resets them to what the template lists and drops everypwd-*secret. Re-runset-login-secret.ps1for each domain afterwards.infra/deploy.ps1is safe — it only callsaz containerapp update --image, which leaves secrets and environment variables untouched.
This server cannot be deployed
Maintenance
Related MCP Connectors
Browserless MCP — wraps the Browserless headless-Chromium REST API (browserless.io)
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Undetectable cloud browser sessions for AI agents and scrapers. Navigate, extract, click, captcha.
- TabfleetOAuthcom.tabfleet
Launch, inspect, control, and share isolated cloud browsers for your agents.
Related MCP Servers
- FlicenseBqualityDmaintenancePlaywright wrapper for MCP that enables LLM-powered clients to control a browser for automation tasks.101-
- AlicenseBqualityDmaintenanceEnables web browser automation through Playwright, providing tools for navigation, element interaction, screenshot capture, and accessibility snapshots. Uses streamableHttp transport for seamless integration with MCP clients.6673 npmMIT
- FlicenseNot gradedqualityDmaintenanceProvides browser automation capabilities via HTTP endpoints by wrapping the official Playwright MCP package, enabling serverless deployments and cloud environments where STDIO-based communication is not possible.1-
- AlicenseNot gradedqualityDmaintenanceEnables browser automation and web scraping by exposing Playwright tools through an HTTP-based MCP server. Users can navigate pages, interact with web elements, capture screenshots, and extract structured content using a persistent Chromium instance.MIT