HTTP/SSE Transport Mode
Starting with version 1.0.7, Playwright MCP Server supports HTTP/SSE (Server-Sent Events) transport mode in addition to the standard stdio mode. This enables new deployment scenarios and use cases.
Overviewβ
The HTTP/SSE transport allows the MCP server to run as a standalone HTTP server, enabling:
- Remote server deployments
- Multiple concurrent client connections
- Better integration with certain IDEs (e.g., VS Code)
- Health monitoring and debugging endpoints
- Background service operation
- DRY-compliant implementation for maximum maintainability
Transport Modes Comparisonβ
| Feature | stdio Mode | HTTP/SSE Mode |
|---|---|---|
| Default | β Yes | β No |
| Claude Desktop | β Supported | β³ Coming Soon |
| VS Code Copilot | β Supported | β Supported |
| Remote Deployment | β No | β Yes |
| Multiple Clients | β No | β Yes |
| Health Endpoint | β No | β Yes |
| Setup Complexity | Simple | Medium |
Quick Startβ
Starting the HTTP Serverβ
Recommended: Using npx (Version 1.0.9+)β
npx @executeautomation/playwright-mcp-server --port 8931

npx support requires version 1.0.9 or later. If you're using an older version, you'll need to use global installation (see below).
Alternative: Global Installationβ
If you prefer global installation or are using version 1.0.8 or earlier:
# Install globally
npm install -g @executeautomation/playwright-mcp-server
# Run the server
playwright-mcp-server --port 8931
- npx (recommended): Always uses the latest version, no installation needed
- Global install: Faster startup, uses specific installed version
Troubleshooting npxβ
If npx doesn't show console output (versions < 1.0.9):
# Option 1: Update to latest version
npx @executeautomation/playwright-mcp-server@latest --port 8931
# Option 2: Use global installation instead
npm install -g @executeautomation/playwright-mcp-server
playwright-mcp-server --port 8931
Server Outputβ
When started successfully, you'll see:
β³ Initializing Playwright MCP Server on port 8931...
π Starting Playwright MCP Server (HTTP Mode)...
==============================================
Playwright MCP Server (HTTP Mode)
==============================================
Listening: 127.0.0.1:8931 (localhost only)
Version: 1.0.9
SECURITY: Server is bound to localhost only.
Not accessible from external networks.
ENDPOINTS:
- SSE Stream: GET http://localhost:8931/sse
- Messages: POST http://localhost:8931/messages?sessionId=<id>
- MCP (unified): GET http://localhost:8931/mcp
- MCP (unified): POST http://localhost:8931/mcp?sessionId=<id>
- Health Check: GET http://localhost:8931/health
CLIENT CONFIGURATION:
{
"mcpServers": {
"playwright": {
"url": "http://localhost:8931/mcp",
"type": "http"
}
}
}
==============================================
Monitoring HTTP server listening on port 55123
Health check: http://localhost:55123/health
Metrics: http://localhost:55123/metrics
If you don't see the console output above:
- Check your version: Run
npx @executeautomation/playwright-mcp-server@latest --port 8931 - Use global install:
npm install -g @executeautomation/playwright-mcp-serverthenplaywright-mcp-server --port 8931 - Verify server is running:
curl http://localhost:8931/health(should return{"status":"ok"})
Client Configurationβ
For VS Code GitHub Copilotβ
Step 1: Start the HTTP server
playwright-mcp-server --port 8931
Step 2: Add to VS Code settings (.vscode/settings.json or User Settings):
{
"github.copilot.chat.mcp.servers": {
"playwright": {
"url": "http://localhost:8931/mcp",
"type": "http"
}
}
}
The "type": "http" field is required for HTTP/SSE transport. Without it, the client will not connect properly.
Step 3: Reload VS Code
For Claude Desktopβ
Claude Desktop currently requires stdio mode. Use the standard configuration instead:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@executeautomation/playwright-mcp-server"]
}
}
}
HTTP transport support for Claude Desktop is expected in future updates.
For Custom MCP Clientsβ
{
"mcpServers": {
"playwright": {
"url": "http://localhost:8931/mcp",
"type": "http"
}
}
}
Required Fields:
url- The HTTP endpoint URLtype- Must be "http" for HTTP/SSE transport
Without "type": "http", the client may attempt stdio mode and fail to connect.
Available Endpointsβ
Health Checkβ
curl http://localhost:8931/health
Response:
{
"status": "ok",
"version": "1.0.7",
"activeSessions": 0
}
SSE Stream (Legacy)β
curl -N http://localhost:8931/sse
Establishes an SSE connection and returns the endpoint for sending messages.
MCP Unified Endpoint (Recommended)β
curl -N http://localhost:8931/mcp
The unified endpoint that handles both SSE streaming and message sending.
Monitoring Metricsβ
The monitoring system runs on PORT+1:
curl http://localhost:8932/health
curl http://localhost:8932/metrics
Command-Line Optionsβ
playwright-mcp-server [OPTIONS]
OPTIONS:
--port <number> Run in HTTP mode on the specified port
--help, -h Show help message
EXAMPLES:
# Run in stdio mode (default)
playwright-mcp-server
# Run in HTTP mode on port 8931
playwright-mcp-server --port 8931
# Run in HTTP mode on custom port
playwright-mcp-server --port 3000
Use Casesβ
1. Remote Server Deploymentβ
Run the server on a remote machine:
# On remote server
ssh user@remote-server
playwright-mcp-server --port 8931
# Configure client to connect remotely
{
"mcpServers": {
"playwright": {
"url": "http://remote-server:8931/mcp"
}
}
}
2. Background Serviceβ
Run as a persistent background service:
# Using nohup
nohup playwright-mcp-server --port 8931 > server.log 2>&1 &
# Check status
curl http://localhost:8931/health
# View logs
tail -f server.log
3. Docker Deploymentβ
FROM node:20
WORKDIR /app
RUN npm install -g @executeautomation/playwright-mcp-server
EXPOSE 8931
CMD ["playwright-mcp-server", "--port", "8931"]
Run the container:
docker build -t playwright-mcp .
docker run -p 8931:8931 playwright-mcp
4. Multiple Concurrent Clientsβ
HTTP mode supports multiple clients simultaneously:
# Start server
playwright-mcp-server --port 8931
# Client 1 connects
# Client 2 connects
# Client 3 connects
# Check active sessions
curl http://localhost:8931/health
# Returns: {"status":"ok","activeSessions":3}
Session Managementβ
Each client connection creates a unique session:
- Session ID: Automatically generated UUID
- Isolation: Each session has its own browser instance
- Cleanup: Automatic cleanup on connection close
- Monitoring: Track active sessions via health endpoint
Example Session Flowβ
- Client connects β GET
/mcp - Server responds β Returns session endpoint
- Client sends messages β POST
/mcp?sessionId=<id> - Server processes β Returns results via SSE
- Client disconnects β Session automatically cleaned up
Testing HTTP Modeβ
Automated Testingβ
# Clone the repository
git clone https://github.com/executeautomation/mcp-playwright.git
cd mcp-playwright
# Build
npm run build
# Run automated tests
./test-http-mode.sh
The test script validates:
- β Server startup
- β Health endpoint
- β SSE connections
- β Error handling
- β Session management
- β Monitoring endpoints
Manual Testingβ
# Terminal 1: Start server
playwright-mcp-server --port 8931
# Terminal 2: Test endpoints
curl http://localhost:8931/health
curl -N http://localhost:8931/mcp | head -5
curl http://localhost:8932/metrics
Using MCP Inspectorβ
# Start server
playwright-mcp-server --port 8931
# Test with official MCP inspector
npx @modelcontextprotocol/inspector http://localhost:8931/mcp
Monitoring and Debuggingβ
Health Monitoringβ
The /health endpoint provides real-time status:
curl http://localhost:8931/health | jq
Monitoring points:
- Server status
- Version information
- Active session count
- Response time
Metrics Collectionβ
The monitoring server uses dynamic port allocation to avoid conflicts. Check the console output for the actual port:
# Server output shows:
# Monitoring HTTP server listening on port 59055
# Health check: http://localhost:59055/health
# Metrics: http://localhost:59055/metrics
Access detailed metrics:
# Use the port from server output
curl http://localhost:59055/metrics | jq
Available metrics:
- Request counts
- Response times
- Error rates
- Memory usage
- System health
Note: The monitoring port is dynamically allocated (using port 0) to prevent conflicts with other services.
Logsβ
Server logs are written to stdout/stderr in JSON format:
# View logs in real-time
tail -f server.log
# Filter by level
grep '"level":"error"' server.log
# Track specific session
grep '"sessionId":"abc-123"' server.log
# Monitor all incoming requests
grep '"message":"Incoming request"' server.log
Log Levels:
info- Normal operations (requests, connections, transport events)warn- Potential issues (unknown sessions, missing parameters)error- Failures (connection errors, handler exceptions)
Key Log Messages:
// Client connects
{"level":"info","message":"Incoming request","context":{"method":"GET","path":"/mcp"}}
{"level":"info","message":"SSE connection request received","context":{"endpoint":"/mcp"}}
{"level":"info","message":"Transport registered","context":{"sessionId":"...","activeTransports":1}}
// Client sends message
{"level":"info","message":"Incoming request","context":{"method":"POST","path":"/mcp"}}
{"level":"info","message":"POST message received","context":{"sessionId":"...","availableTransports":[...]}}
// Client disconnects
{"level":"info","message":"SSE connection closed","context":{"sessionId":"...","endpoint":"/mcp"}}
{"level":"info","message":"Transport unregistered","context":{"activeTransports":0}}
Code Qualityβ
The HTTP server implementation follows software engineering best practices:
DRY Principle (Don't Repeat Yourself)β
All endpoint logic is centralized in reusable helper functions:
Before (with duplication):
// /sse endpoint - 26 lines of code
app.get('/sse', async (req, res) => {
// SSE connection logic...
});
// /mcp endpoint - 26 lines of identical code
app.get('/mcp', async (req, res) => {
// Same SSE connection logic...
});
After (DRY compliant):
// Reusable helper - 29 lines total
const handleSseConnection = async (endpoint, messageEndpoint, req, res) => {
// SSE connection logic (single source of truth)
};
// Simple endpoint definitions - 2 lines total
app.get('/sse', (req, res) => handleSseConnection('/sse', '/messages', req, res));
app.get('/mcp', (req, res) => handleSseConnection('/mcp', '/mcp', req, res));
Benefitsβ
- 40% code reduction in endpoint handlers
- Zero duplication - single source of truth
- Consistent behavior - all endpoints work the same way
- Easy maintenance - update once, applies everywhere
- Lower bug risk - no chance of inconsistent implementations
Maintainabilityβ
The implementation prioritizes long-term maintainability:
- Clear separation of concerns
- Reusable, testable functions
- Comprehensive error handling
- Context-aware logging
- Type-safe TypeScript implementation
Architectureβ
Componentsβ
βββββββββββββββββββββββ
β HTTP Server β
β (Express) β
ββββββββββββ¬βββββββββββ
β
ββββββββ΄βββββββ
β β
βββββΌββββ βββββΌβββββ
β SSE β β Health β
β Trans β β Check β
β port β β β
βββββ¬ββββ ββββββββββ
β
βββββΌβββββββββββββββββ
β MCP Server Core β
β - Request Handler β
β - Tool Handler β
β - Browser Manager β
ββββββββββββββββββββββ
Transport Layerβ
The implementation uses MCP SDK's native SSEServerTransport with DRY-compliant helper functions:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
// Reusable helper function handles all SSE connections
const handleSseConnection = async (endpoint, messageEndpoint, req, res) => {
const transport = new SSEServerTransport(messageEndpoint, res);
await server.connect(transport);
// ... session management, logging, error handling
};
// All endpoints use the same logic
app.get('/sse', (req, res) => handleSseConnection('/sse', '/messages', req, res));
app.get('/mcp', (req, res) => handleSseConnection('/mcp', '/mcp', req, res));
Benefits of this approach:
- Single source of truth for endpoint logic
- Consistent behavior across all endpoints
- Easy to maintain and update
- Reduced code duplication (40% less code)
- Lower bug risk
Session Lifecycleβ
Client Request
β
GET /mcp
β
Create Session
β
Return Session ID
β
Client POSTs Messages
β
Server Processes via SSE
β
Connection Closes
β
Cleanup Session
Security Considerationsβ
The server binds to 127.0.0.1 (localhost only) by default. This is a critical security feature that prevents external access.
Network Bindingβ
SECURITY: The server binds to 127.0.0.1 (localhost only):
- β Accessible only from the local machine
- β Not exposed to external networks
- β Secure by default
- β Suitable for local development
- β No accidental external exposure
- β Protects against unauthorized access
Why This Matters:
- Prevents external machines from accessing your MCP server
- Protects browser automation capabilities from network exposure
- Ensures no accidental exposure in shared network environments
- Follows security best practices for local development tools
Remote Access (Secure Method)β
DO NOT modify the code to bind to 0.0.0.0. Instead, use SSH tunneling:
# On the remote server (server still binds to localhost)
playwright-mcp-server --port 8931
# On your local machine (create secure tunnel)
ssh -L 8931:localhost:8931 user@remote-server
# Now access securely via localhost:8931
Benefits of SSH Tunneling:
- β Encrypted connection
- β Authentication required
- β Server stays bound to localhost
- β Industry standard practice
Production Deploymentβ
For production environments, use a reverse proxy:
# nginx example
server {
listen 80;
server_name your-domain.com;
location /mcp {
proxy_pass http://127.0.0.1:8931;
# Add authentication
auth_basic "MCP Server";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}
Benefits:
- β Server stays on localhost
- β Add authentication layer
- β Enable SSL/TLS
- β Implement rate limiting
- β Better logging and monitoring
Example: SSH Tunnelβ
# On local machine
ssh -L 8931:localhost:8931 user@remote-server
# Server now accessible at localhost:8931
Troubleshootingβ
Debugging Toolsβ
Version 1.0.7 includes comprehensive logging to help diagnose issues:
Request Logging: Every HTTP request is logged with:
- Method (GET, POST)
- Path and URL
- Query parameters
- Headers (Accept, Content-Type)
Session Tracking: Monitor SSE sessions with:
- Transport registration logs
- Active transport counts
- Session ID tracking
- Connection close events
Example Debug Output:
{"level":"info","message":"Incoming request","context":{"method":"GET","path":"/mcp"}}
{"level":"info","message":"Transport registered","context":{"sessionId":"abc-123","activeTransports":1}}
{"level":"info","message":"POST message received","context":{"sessionId":"abc-123","endpoint":"/mcp"}}
Common Issuesβ
Issue: npx Shows No Console Outputβ
Symptom: Running npx @executeautomation/playwright-mcp-server --port 8931 shows no output or seems to hang.
Cause: Versions prior to 1.0.9 had an output buffering issue with npx.
Solutions:
-
Update to latest version (recommended):
npx @executeautomation/playwright-mcp-server@latest --port 8931 -
Use global installation:
npm install -g @executeautomation/playwright-mcp-server
playwright-mcp-server --port 8931 -
Verify server is actually running:
# Even without visible output, the server might be running
curl http://localhost:8931/healthIf this returns
{"status":"ok","version":"1.0.x","activeSessions":0}, the server is running successfully despite no console output.
Why this happens:
- Node.js buffers
console.log()output when stdout is not a TTY - Fixed in version 1.0.9 by using
process.stdout.write() - Global installation doesn't have this issue
Issue: "No transport found for sessionId"β
Symptom: Client receives 400 error with message about missing transport.
Causes:
- GET request to establish SSE connection didn't complete
- Client using stale/cached sessionId
- Server restarted but client kept old sessionId
Solution:
# Check logs for "Transport registered" - should appear before POST messages
# Look for this sequence:
# 1. "Incoming request" GET /mcp
# 2. "SSE connection request received"
# 3. "Transport registered" with sessionId
# 4. "POST message received" with same sessionId
# If sequence is wrong, client needs to reconnect
Port Already in Useβ
# Check what's using the port
lsof -i :8931
# Use a different port
playwright-mcp-server --port 9000
Connection Refusedβ
# Verify server is running
ps aux | grep playwright-mcp-server
# Check server logs
tail -f server.log
# Test with curl
curl -v http://localhost:8931/health
SSE Connection Issuesβ
Check if SSE stream is established:
# Should show continuous stream, not immediate close
curl -N http://localhost:8931/mcp
# Look for:
# event: endpoint
# data: /mcp?sessionId=<uuid>
Verify session is registered:
# In server logs, look for:
grep "Transport registered" server.log
# Should show sessionId matching what client uses
Browser Issuesβ
# Install Playwright browsers
npx playwright install chromium
# For headless mode on servers without display
export DISPLAY=:99
Session Not Foundβ
This error occurs when:
- Session has expired
- Server was restarted
- Client used invalid session ID
Solution: Reconnect to establish a new session.
Performance Considerationsβ
Concurrent Connectionsβ
The server handles multiple concurrent clients efficiently:
- Each session is isolated
- Browser instances are managed per session
- Automatic resource cleanup
Resource Limitsβ
Monitor resource usage:
# Check memory usage
curl http://localhost:8932/metrics | jq '.memory'
# Check active sessions
curl http://localhost:8931/health | jq '.activeSessions'
Migration Guideβ
From stdio to HTTP Modeβ
Before (stdio):
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@executeautomation/playwright-mcp-server"]
}
}
}
After (HTTP):
- Start the server:
playwright-mcp-server --port 8931
- Update configuration:
{
"mcpServers": {
"playwright": {
"url": "http://localhost:8931/mcp"
}
}
}
Backward Compatibilityβ
- stdio mode remains the default
- All existing tools work in both modes
- No breaking changes to API
- Configurations are independent
FAQβ
Q: Should I use npx or global installation?β
Use npx (recommended):
- β Always uses latest version
- β No installation needed
- β Easy to update
- β οΈ Requires version 1.0.9+ for console output
Use global installation:
- β Faster startup (no download)
- β Works with all versions
- β More predictable (pinned version)
- β οΈ Needs manual updates
Quick test which works for you:
# Try npx first
npx @executeautomation/playwright-mcp-server@latest --port 8931
# If no output, use global install
npm install -g @executeautomation/playwright-mcp-server
playwright-mcp-server --port 8931
Q: Should I use HTTP or stdio mode?β
Use stdio mode if:
- You're using Claude Desktop
- You want simpler setup
- You're running locally only
Use HTTP mode if:
- You need remote access
- You want multiple clients
- You need health monitoring
- You're using VS Code Copilot
Q: Can I run both modes simultaneously?β
Yes! Run them on different ports or different machines.
Q: How do I secure HTTP mode?β
Consider:
- SSH tunneling for remote access
- Reverse proxy with authentication
- Network firewall rules
- HTTPS/TLS certificates
Q: What's the performance difference?β
Both modes have similar performance. HTTP mode has slightly higher overhead due to HTTP protocol, but it's negligible for typical use cases.
Additional Resourcesβ
Supportβ
If you encounter issues:
- Check the troubleshooting section
- Run the automated test:
./test-http-mode.sh - Review server logs
- Open an issue on GitHub
Version Added: 1.0.7
Protocol Version: MCP 2024-11-05+
Status: Stable