MCP
Use Lightpanda via the Model Context Protocol (MCP) to control the browser from AI applications.
Connect
Local
Start the MCP server:
lightpanda mcpFind reference of MCP command options.
Tools
| Name | Description |
|---|---|
| goto | Navigate to a specified URL and load the page in memory so it can be reused later for info extraction. |
| getUrl | Get the URL of the page currently loaded in the browser. |
| getEnv | Read an LP_* environment variable by name, or list the LP_* names that are set when called without a name. |
| getCookies | Get cookies stored in the browser. Defaults to the current page’s host; pass url to filter another host or all to dump every cookie. |
| consoleLogs | Get buffered console.log/warn/error messages from the current page, then clear the buffer. |
| tree | Get the page content as a simplified semantic DOM tree for AI reasoning. If a url is provided, it navigates to that url first. |
| markdown | Get the page content in markdown format. If a url is provided, it navigates to that url first. |
| html | Get the raw serialized HTML of the page, or a single node’s outerHTML when a selector or backendNodeId is given. If a url is provided, it navigates to that url first. |
| nodeDetails | Get details for a node by backendNodeId, including a ready-to-use CSS selector plus tag, role, name, attributes, and state. |
| findElement | Find interactive elements by role and/or accessible name, returning their backend node IDs. |
| interactiveElements | Extract interactive elements from the opened page. If a url is provided, it navigates to that url first. |
| links | Extract all links in the opened page. If a url is provided, it navigates to that url first. |
| extract | Extract structured data from the current page using a schema mapping output field names to CSS-selector specs. |
| structuredData | Extract structured data (like JSON-LD, OpenGraph, etc) from the opened page. If a url is provided, it navigates to that url first. |
| search | Run a web search and return the results as markdown. |
| click | Click on an interactive element. Returns the current page URL and title after the click. |
| hover | Hover over an element, triggering mouseover and mouseenter events. |
| fill | Fill text into an input element. Returns the filled value and current page URL and title. |
| selectOption | Select an option in a <select> dropdown element by its value. Dispatches input and change events. |
| setChecked | Check or uncheck a checkbox or radio button. Dispatches input, change, and click events. |
| press | Press a keyboard key, dispatching keydown and keyup events. |
| scroll | Scroll the page or a specific element. Returns the scroll position and current page URL and title. |
| detectForms | Detect all forms on the page and return their structure including fields, types, and required status. If a url is provided, it navigates to that url first. |
| evaluate | Evaluate JavaScript in the current page context. If a url is provided, it navigates to that url first. |
| save | Save the session as a reusable PandaScript (.js). |
| waitForSelector | Wait for an element matching a CSS selector to appear in the page. Returns the backend node ID of the matched element. |
| waitForState | Wait for the current page to reach a load state (e.g. networkidle), with no navigation. |
| waitForScript | Wait until a JavaScript expression returns truthy, re-evaluating on each tick of the event loop. |
goto
Navigate to a URL and load the page into memory.
{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"goto","arguments":{"url":"https://example.com"}}}Response: "Navigated successfully." — returned even if the URL is unreachable. Always verify with a follow-up content call (see Known behaviors).
markdown
Extract the current page’s content as clean, token-efficient markdown.
{"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"markdown","arguments":{"url":"https://example.com"}}}Response example
{"result":{"content":[{"type":"text","text":"\n# Example Domain\n\nThis domain is for use in illustrative examples in documents...\n\n[More information...](https://www.iana.org/domains/example)\n"}],"isError":false}}Using
markdownwith an inlineurlis the most efficient single-call pattern — it navigates and extracts in one request. Essential for HTTP transport where sessions are stateless.
links
Extract all hyperlinks from the loaded page as a newline-separated list of absolute URLs.
{"jsonrpc":"2.0","id":4,"method":"tools/call",
"params":{"name":"links","arguments":{"url":"https://example.com"}}}Response: One URL per line, e.g. "https://iana.org/domains/example".
evaluate
Execute arbitrary JavaScript in the page context and return the result as a string.
{"jsonrpc":"2.0","id":5,"method":"tools/call",
"params":{"name":"evaluate","arguments":{
"script":"document.title",
"url":"https://example.com"}}}Response: "Example Domain"
Resources
Two read-only resources are available after a page has been loaded via resources/read:
| URI | MIME type | Description |
|---|---|---|
mcp://page/html | text/html | Raw serialized HTML DOM of the loaded page |
mcp://page/markdown | text/markdown | Markdown representation (identical output to the markdown tool) |
{"jsonrpc":"2.0","id":6,"method":"resources/read",
"params":{"uri":"mcp://page/html"}}{"jsonrpc":"2.0","id":7,"method":"resources/read",
"params":{"uri":"mcp://page/markdown"}}The
markdowntool and themcp://page/markdownresource return the same content. The difference is who initiates: tools are called by the agent during its workflow; resources are read by the host application (e.g. an IDE displaying page state in the background).
Connecting an AI agent
Claude Desktop / Cursor / Windsurf
Add to your MCP host configuration:
- Claude Desktop: Settings > Developer > Edit Config
- Cursor:
.cursor/mcp.jsonin your project - Windsurf: Cascade MCP settings
{
"mcpServers": {
"lightpanda": {
"command": "/path/to/lightpanda",
"args": ["mcp"]
}
}
}For robots.txt compliance, use "args": ["mcp", "--obey-robots"].
Replace
/path/to/lightpandawith the actual binary path, e.g./usr/local/bin/lightpanda.
HTTP transport via supergateway
Lightpanda MCP natively supports only stdio. To expose it over HTTP, use supergateway as a bridge.
npx -y supergateway \
--stdio "lightpanda mcp" \
--outputTransport streamableHttp \
--stateful --sessionTimeout 180000 \
--port 8000By default, supergateway is stateless: each HTTP request spawns a fresh process. Use
--stateful --sessionTimeout <ms>for stateful sessions.
With robots.txt: --stdio "lightpanda mcp --obey-robots"
Calling the HTTP endpoint
# Initialize
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2024-11-05","capabilities":{},
"clientInfo":{"name":"curl-test","version":"1.0"}}}'
# Extract markdown (pass url inline - HTTP is stateless by default)
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"markdown","arguments":{"url":"https://example.com"}}}'Known behaviors
goto always returns success
goto returns "Navigated successfully." even for invalid or unreachable URLs. The failure surfaces on the next content call:
# Navigation failed
Reason: CouldntResolveHostAlways check the content result after navigation, not the goto response itself.
Debugging
Lightpanda defaults to --log-level warn. Setting info surfaces HTTP requests, navigation events, resource loading, and robots.txt fetches. All logs go to stderr and never interfere with stdout.
lightpanda mcp --log-level info --log-format pretty
# Or pipe logs to a file
lightpanda mcp --log-level info 2>lightpanda.logUse --log-level debug for the most verbose output. Keep warn in production.