Skip to Content

MCP

Use Lightpanda via the Model Context Protocol (MCP) to control the browser from AI applications.

Connect

Start the MCP server:

lightpanda mcp

Find reference of MCP command options.

Tools

NameDescription
gotoNavigate to a specified URL and load the page in memory so it can be reused later for info extraction.
getUrlGet the URL of the page currently loaded in the browser.
getEnvRead an LP_* environment variable by name, or list the LP_* names that are set when called without a name.
getCookiesGet cookies stored in the browser. Defaults to the current page’s host; pass url to filter another host or all to dump every cookie.
consoleLogsGet buffered console.log/warn/error messages from the current page, then clear the buffer.
treeGet the page content as a simplified semantic DOM tree for AI reasoning. If a url is provided, it navigates to that url first.
markdownGet the page content in markdown format. If a url is provided, it navigates to that url first.
htmlGet 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.
nodeDetailsGet details for a node by backendNodeId, including a ready-to-use CSS selector plus tag, role, name, attributes, and state.
findElementFind interactive elements by role and/or accessible name, returning their backend node IDs.
interactiveElementsExtract interactive elements from the opened page. If a url is provided, it navigates to that url first.
linksExtract all links in the opened page. If a url is provided, it navigates to that url first.
extractExtract structured data from the current page using a schema mapping output field names to CSS-selector specs.
structuredDataExtract structured data (like JSON-LD, OpenGraph, etc) from the opened page. If a url is provided, it navigates to that url first.
searchRun a web search and return the results as markdown.
clickClick on an interactive element. Returns the current page URL and title after the click.
hoverHover over an element, triggering mouseover and mouseenter events.
fillFill text into an input element. Returns the filled value and current page URL and title.
selectOptionSelect an option in a <select> dropdown element by its value. Dispatches input and change events.
setCheckedCheck or uncheck a checkbox or radio button. Dispatches input, change, and click events.
pressPress a keyboard key, dispatching keydown and keyup events.
scrollScroll the page or a specific element. Returns the scroll position and current page URL and title.
detectFormsDetect 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.
evaluateEvaluate JavaScript in the current page context. If a url is provided, it navigates to that url first.
saveSave the session as a reusable PandaScript (.js).
waitForSelectorWait for an element matching a CSS selector to appear in the page. Returns the backend node ID of the matched element.
waitForStateWait for the current page to reach a load state (e.g. networkidle), with no navigation.
waitForScriptWait 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 markdown with an inline url is the most efficient single-call pattern — it navigates and extracts in one request. Essential for HTTP transport where sessions are stateless.

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:

URIMIME typeDescription
mcp://page/htmltext/htmlRaw serialized HTML DOM of the loaded page
mcp://page/markdowntext/markdownMarkdown 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 markdown tool and the mcp://page/markdown resource 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.json in 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/lightpanda with 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 8000

By 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: CouldntResolveHost

Always 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.log

Use --log-level debug for the most verbose output. Keep warn in production.

References