Skip to content

First Steps with Puppeteer MCP

Version: 1.0.13
Reading Time: 7 minutes

Let’s create your first browser automation in under 5 minutes!

Using cURL (Simplest)
Terminal window
# 1. Start the server (in one terminal)
npx puppeteer-mcp
# 2. Create a session (in another terminal)
SESSION_ID=$(curl -s -X POST http://localhost:8443/api/sessions \
-H "Content-Type: application/json" \
-d '{"baseUrl": "https://example.com"}' \
| jq -r '.sessionId')
# 3. Navigate to a page
curl -X POST http://localhost:8443/api/sessions/$SESSION_ID/navigate \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
# 4. Take a screenshot
curl -X POST http://localhost:8443/api/sessions/$SESSION_ID/screenshot \
-H "Content-Type: application/json" \
-d '{"fullPage": true}' \
| jq -r '.screenshot' | base64 -d > screenshot.png
echo "Screenshot saved as screenshot.png!"
Using Node.js
screenshot.js
const fetch = require('node-fetch');
const fs = require('fs');
async function takeScreenshot() {
const API_BASE = 'http://localhost:8443/api';
// 1. Create a session
const sessionResponse = await fetch(`${API_BASE}/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseUrl: 'https://example.com' }),
});
const { sessionId } = await sessionResponse.json();
// 2. Navigate to the page
await fetch(`${API_BASE}/sessions/${sessionId}/navigate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://example.com' }),
});
// 3. Take a screenshot
const screenshotResponse = await fetch(`${API_BASE}/sessions/${sessionId}/screenshot`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fullPage: true }),
});
const { screenshot } = await screenshotResponse.json();
// 4. Save the screenshot
fs.writeFileSync('screenshot.png', Buffer.from(screenshot, 'base64'));
console.log('Screenshot saved as screenshot.png!');
// 5. Clean up
await fetch(`${API_BASE}/sessions/${sessionId}`, { method: 'DELETE' });
}
takeScreenshot().catch(console.error);

Run it:

Terminal window
node screenshot.js
Using Python
screenshot.py
import requests
import base64
API_BASE = 'http://localhost:8443/api'
# 1. Create a session
session_response = requests.post(f'{API_BASE}/sessions',
json={'baseUrl': 'https://example.com'})
session_id = session_response.json()['sessionId']
# 2. Navigate to the page
requests.post(f'{API_BASE}/sessions/{session_id}/navigate',
json={'url': 'https://example.com'})
# 3. Take a screenshot
screenshot_response = requests.post(
f'{API_BASE}/sessions/{session_id}/screenshot',
json={'fullPage': True})
# 4. Save the screenshot
screenshot_data = screenshot_response.json()['screenshot']
with open('screenshot.png', 'wb') as f:
f.write(base64.b64decode(screenshot_data))
print('Screenshot saved as screenshot.png!')
# 5. Clean up
requests.delete(f'{API_BASE}/sessions/{session_id}')

Run it:

Terminal window
python screenshot.py
Extract Text from a Website
extract-content.js
const fetch = require('node-fetch');
async function extractContent() {
const API_BASE = 'http://localhost:8443/api';
// Create session and navigate
const sessionResponse = await fetch(`${API_BASE}/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseUrl: 'https://example.com' }),
});
const { sessionId } = await sessionResponse.json();
await fetch(`${API_BASE}/sessions/${sessionId}/navigate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://example.com' }),
});
// Extract specific content
const evaluateResponse = await fetch(`${API_BASE}/sessions/${sessionId}/evaluate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
script: `
({
title: document.title,
headings: Array.from(document.querySelectorAll('h1, h2')).map(h => h.textContent),
links: Array.from(document.querySelectorAll('a')).map(a => ({
text: a.textContent,
href: a.href
})).slice(0, 5) // First 5 links
})
`,
}),
});
const content = await evaluateResponse.json();
console.log('Page Content:', JSON.stringify(content.result, null, 2));
// Clean up
await fetch(`${API_BASE}/sessions/${sessionId}`, { method: 'DELETE' });
}
extractContent().catch(console.error);
Automate Form Interaction
form-automation.js
const fetch = require('node-fetch');
async function automateForm() {
const API_BASE = 'http://localhost:8443/api';
// Create session
const sessionResponse = await fetch(`${API_BASE}/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseUrl: 'https://example-form.com' }),
});
const { sessionId } = await sessionResponse.json();
// Navigate to form page
await fetch(`${API_BASE}/sessions/${sessionId}/navigate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://example-form.com/contact' }),
});
// Fill form fields
await fetch(`${API_BASE}/sessions/${sessionId}/fill`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
selector: 'input[name="email"]',
value: 'test@example.com',
}),
});
await fetch(`${API_BASE}/sessions/${sessionId}/fill`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
selector: 'textarea[name="message"]',
value: 'This is an automated test message.',
}),
});
// Click submit button
await fetch(`${API_BASE}/sessions/${sessionId}/click`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
selector: 'button[type="submit"]',
}),
});
// Wait for navigation or response
await fetch(`${API_BASE}/sessions/${sessionId}/wait`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
selector: '.success-message',
timeout: 5000,
}),
});
console.log('Form submitted successfully!');
// Clean up
await fetch(`${API_BASE}/sessions/${sessionId}`, { method: 'DELETE' });
}
automateForm().catch(console.error);
async function scrapeProducts() {
const session = await createSession('https://shop.example.com');
// Navigate to products page
await navigate(session.id, '/products');
// Extract product data
const products = await evaluate(
session.id,
`
Array.from(document.querySelectorAll('.product')).map(product => ({
name: product.querySelector('.name')?.textContent,
price: product.querySelector('.price')?.textContent,
image: product.querySelector('img')?.src,
link: product.querySelector('a')?.href
}))
`,
);
console.log(`Found ${products.length} products`);
return products;
}
async function generatePDF(url, outputPath) {
const session = await createSession(url);
// Navigate to the page
await navigate(session.id, url);
// Wait for content to load
await waitForSelector(session.id, 'body');
// Generate PDF
const pdfResponse = await fetch(`${API_BASE}/sessions/${session.id}/pdf`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
format: 'A4',
printBackground: true,
margin: {
top: '1cm',
bottom: '1cm',
left: '1cm',
right: '1cm',
},
}),
});
const { pdf } = await pdfResponse.json();
fs.writeFileSync(outputPath, Buffer.from(pdf, 'base64'));
console.log(`PDF saved to ${outputPath}`);
}
async function loginAndScrape() {
const session = await createSession('https://app.example.com');
// Navigate to login page
await navigate(session.id, '/login');
// Fill login form
await fill(session.id, '#username', 'myuser@example.com');
await fill(session.id, '#password', 'mypassword');
// Submit form
await click(session.id, 'button[type="submit"]');
// Wait for dashboard to load
await waitForSelector(session.id, '.dashboard');
// Now you can access authenticated content
const userData = await evaluate(
session.id,
`
({
name: document.querySelector('.user-name')?.textContent,
balance: document.querySelector('.balance')?.textContent
})
`,
);
return userData;
}

Already shown in examples above. Use standard HTTP requests.

const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8443');
ws.on('open', () => {
// Subscribe to browser events
ws.send(
JSON.stringify({
type: 'subscribe',
sessionId: 'your-session-id',
events: ['console', 'network', 'page'],
}),
);
});
ws.on('message', (data) => {
const event = JSON.parse(data);
console.log('Browser event:', event);
});
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// Load proto file
const packageDefinition = protoLoader.loadSync('puppeteer.proto');
const puppeteerProto = grpc.loadPackageDefinition(packageDefinition);
// Create client
const client = new puppeteerProto.PuppeteerService(
'localhost:50051',
grpc.credentials.createInsecure(),
);
// Use the service
client.createSession({ baseUrl: 'https://example.com' }, (err, response) => {
if (!err) {
console.log('Session created:', response.sessionId);
}
});

See Claude Desktop Setup for MCP integration.

const session = await createSession();
try {
// Your automation code here
} finally {
// Always clean up
await deleteSession(session.id);
}
async function safeScrape(url) {
let session;
try {
session = await createSession(url);
// Your scraping logic
} catch (error) {
console.error('Scraping failed:', error);
// Handle specific errors
if (error.message.includes('timeout')) {
console.log('Page took too long to load');
}
} finally {
if (session) {
await deleteSession(session.id);
}
}
}
// Set reasonable timeouts
await waitForSelector(session.id, '.content', {
timeout: 10000, // 10 seconds
});
// For slow pages
await navigate(session.id, url, {
waitUntil: 'networkidle0',
timeout: 30000, // 30 seconds
});
// Reuse sessions when possible
const session = await createSession();
// Process multiple pages with one session
for (const url of urls) {
await navigate(session.id, url);
await processPage(session.id);
}
await deleteSession(session.id);
ActionEndpointDescription
Create SessionPOST /api/sessionsStart a new browser session
NavigatePOST /api/sessions/:id/navigateGo to a URL
ClickPOST /api/sessions/:id/clickClick an element
FillPOST /api/sessions/:id/fillFill input field
ScreenshotPOST /api/sessions/:id/screenshotCapture screenshot
PDFPOST /api/sessions/:id/pdfGenerate PDF
EvaluatePOST /api/sessions/:id/evaluateRun JavaScript
WaitPOST /api/sessions/:id/waitWait for element
Delete SessionDELETE /api/sessions/:idClean up session
Terminal window
DEBUG=puppeteer:* npx puppeteer-mcp
// Take screenshot when debugging
const debugScreenshot = await screenshot(session.id);
fs.writeFileSync('debug.png', Buffer.from(debugScreenshot, 'base64'));
// Get page content
const html = await evaluate(session.id, 'document.documentElement.outerHTML');
fs.writeFileSync('debug.html', html);
Terminal window
PUPPETEER_HEADLESS=false npx puppeteer-mcp

Now that you’ve completed your first automations:

  1. Set up authentication - Secure your API
  2. Integrate with Claude Desktop - Enable AI automation
  3. Explore advanced features - Full API reference

Pro Tip: Start simple, then gradually add complexity. Browser automation is powerful but can be tricky - take it step by step! 🎯