Browser profiles for AI agents
Give your agent a browser that stays signed in. Each XDLogin profile is a real Chrome on your machine with its own fingerprint, proxy, timezone and cookies. Agents reach it over MCP or the Chrome DevTools Protocol, and the local API and MCP server are on every plan, the free one included.
What an agent gets from a real profile
A browser started fresh for every run forgets every login, and each site sees a new visitor each time. A profile is the opposite: a browser that keeps its session, its history and an identity of its own between runs.
Sessions that persist
Log in once, by hand or from a script. Cookies, local storage and cache stay in the profile’s folder on your disk, so the next run starts signed in. Nothing is uploaded unless you turn on encrypted sync (Pro and above).
One identity per profile
A profile takes its user agent, client hints, screen, GPU string, CPU and memory from one real device model, and sends its traffic through the proxy you give it. Its timezone and language are the ones you set. Two agents in two profiles share no cookies and no canvas hash.
On your machine
Every profile is a Chrome process on your computer. The REST API, the MCP server and each profile’s DevTools endpoint listen on 127.0.0.1 only, and the browsing data stays on your disk.
No per-hour bill
XDLogin charges per plan, not per browser-hour, per session or per gigabyte of proxy traffic. The free plan has 10 profiles and the same API and MCP server as the paid plans.
Two ways in
MCP: the agent manages profiles
Your agent lists, creates, launches, navigates and stops profiles through tools, from Claude Code, Cursor, Claude Desktop, VS Code, Codex CLI or another MCP client. From desktop 3.5.8 they can also read a page and click, type and scroll in it; earlier versions can’t.
CDP: the agent works inside one
Launch a profile through the REST API, read its DevTools endpoint, and hand it to browser-use, Playwright or Puppeteer. The framework reads and clicks; the profile supplies the identity and the logins.
Connect your MCP client
-
Start the server
In XDLogin, open Settings → API & Auto and press Start MCP under MCP Server (AI Agents). It listens on
http://127.0.0.1:8931and speaks Streamable HTTP; a client set to the older SSE transport gets a405. From then on the server starts with XDLogin, until you press Stop MCP. -
Set a key
Under Authentication, press Generate, then stop and start the server so the key applies. Clients send it as
Authorization: Bearer <key>, and the same key protects the REST API. Without a key, any program on your computer can drive your profiles. From 3.5.8, web pages can’t, key or not: both servers answer403to a request whoseOriginorHostnames anything but127.0.0.1,localhostor[::1], as a website’s requests do. Scripts and MCP clients send noOrigin, so they’re unaffected. The Linux build (3.5.6) and the macOS beta (3.5.5) don’t check: with no key set, a page open in your browser could send them commands. -
Add it to your client
Pick your client below. Without a key, leave out the header lines.
# With an API key (Settings > API & Auto > Authentication)
claude mcp add --transport http xdlogin http://127.0.0.1:8931 --header "Authorization: Bearer YOUR-API-KEY"
# Without a key
claude mcp add --transport http xdlogin http://127.0.0.1:8931
# For every project, not just this one: --scope user before the name
claude mcp add --transport http --scope user xdlogin http://127.0.0.1:8931 --header "Authorization: Bearer YOUR-API-KEY"
# Check the connection
claude mcp list{
"mcpServers": {
"xdlogin": {
"url": "http://127.0.0.1:8931",
"headers": {
"Authorization": "Bearer ${env:XDLOGIN_API_KEY}"
}
}
}
}{
"mcpServers": {
"xdlogin": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://127.0.0.1:8931",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer YOUR-API-KEY"
}
}
}
}{
"inputs": [
{
"type": "promptString",
"id": "xdlogin-key",
"description": "XDLogin API key",
"password": true
}
],
"servers": {
"xdlogin": {
"type": "http",
"url": "http://127.0.0.1:8931",
"headers": {
"Authorization": "Bearer ${input:xdlogin-key}"
}
}
}
}# ~/.codex/config.toml
[mcp_servers.xdlogin]
url = "http://127.0.0.1:8931"
bearer_token_env_var = "XDLOGIN_API_KEY"- Claude Code. Options such as
--scopego before the server name. To share the setup through a project’s.mcp.jsonwithout committing the key, write the header as"Authorization": "Bearer ${XDLOGIN_API_KEY}"; Claude Code fills it in from the environment. - Cursor. Save as
.cursor/mcp.jsonin a project, or~/.cursor/mcp.jsonfor every project.${env:XDLOGIN_API_KEY}reads the key from your environment. - Claude Desktop. Its config file only starts local commands, so the
mcp-remotenpm package (Node.js 18 or later) bridges to XDLogin. Open Settings → Developer → Edit Config, merge this in, replaceYOUR-API-KEY, then quit and reopen Claude Desktop. The key sits inenvbecause some clients, Claude Desktop on Windows among them, split arguments at spaces.mcp-remoteaccepts plainhttp://for127.0.0.1without extra flags. - VS Code. Save as
.vscode/mcp.json. VS Code prompts for the key, so the file holds no secret. - Codex CLI. Add to
~/.codex/config.toml. Codex sends the value ofXDLOGIN_API_KEYas the bearer token.
What the agent can call
Twenty-one tools on Windows (3.5.8), the same on every plan; six of them are new in 3.5.8. The MCP tools reference says what each one does, and how the Linux build (3.5.6) and the macOS beta (3.5.5) differ. Profiles are also exposed as resources (xdlogin://profiles, xdlogin://profile/{id} and xdlogin://sessions/active), so an agent can read a profile before acting on it.
- profile_list
- profile_get
- profile_create
- profile_clone
- profile_launch
- profile_stop
- profile_delete
- browser_navigate
- browser_screenshot
- browser_snapshot
- browser_click
- browser_type
- browser_press_key
- browser_scroll
- browser_get_text
- fingerprint_get
- proxy_test
- cookies_export
- cookies_import
- health
- stats
browser-use and Playwright
For an agent that reads and clicks, launch the profile through the REST API (Start API in the same settings tab), wait for GET /api/profiles/{id}/connect to stop answering 409, and pass the endpoint it returns to your framework. The endpoint’s port can change with every launch, so read it each time.
browser-use
browser-use 0.13 attaches to a running Chrome with cdp_url. It needs Python 3.11 or later and an API key for the model you pick.
# pip install browser-use requests (browser-use 0.13; set ANTHROPIC_API_KEY for the model)
import asyncio
import os
import time
import requests
from browser_use import Agent, Browser, ChatAnthropic
BASE = os.environ.get("XDLOGIN_BASE_URL", "http://127.0.0.1:31337")
KEY = os.environ.get("XDLOGIN_API_KEY")
HEADERS = {"Authorization": f"Bearer {KEY}"} if KEY else {}
def cdp_url(profile_id):
r = requests.post(f"{BASE}/api/profiles/{profile_id}/launch", headers=HEADERS, timeout=90)
if r.status_code != 200:
raise RuntimeError(f"launch failed: {r.json().get('error')}")
for _ in range(60): # /connect answers 409 until Chrome is ready
r = requests.get(f"{BASE}/api/profiles/{profile_id}/connect", headers=HEADERS, timeout=10)
if r.status_code == 200:
return r.json()["data"]["httpEndpoint"]
time.sleep(0.5)
raise RuntimeError(r.json().get("error"))
async def main():
browser = Browser(
cdp_url=cdp_url(os.environ["XDLOGIN_PROFILE_ID"]),
headless=False,
no_viewport=True, # keep the profile's own window and screen size
highlight_elements=False, # don't draw highlight boxes into the page
keep_alive=True, # leave the profile running when the agent is done
)
agent = Agent(
task="Open my account's order history and list the three most recent orders",
llm=ChatAnthropic(model="claude-sonnet-4-6"),
browser=browser,
)
await agent.run()
asyncio.run(main())no_viewport stops browser-use from resizing the page to its own viewport, which would contradict the screen the profile reports. keep_alive leaves the profile open for the next run.
Playwright
Attach with connectOverCDP and work in browser.contexts()[0]: that context is the profile, with its cookies. The Playwright guide has the full script, parallel runs and the pitfalls; there are guides for Puppeteer and Selenium too.
import { chromium } from 'playwright';
const BASE = 'http://127.0.0.1:31337';
const id = process.env.XDLOGIN_PROFILE_ID;
const key = process.env.XDLOGIN_API_KEY;
const headers = key ? { Authorization: `Bearer ${key}` } : {};
await fetch(`${BASE}/api/profiles/${id}/launch`, { method: 'POST', headers });
let info;
for (let i = 0; i < 60 && !info; i++) { // 409 until Chrome is ready
const r = await fetch(`${BASE}/api/profiles/${id}/connect`, { headers });
if (r.ok) info = (await r.json()).data;
else await new Promise((res) => setTimeout(res, 500));
}
if (!info) throw new Error('No DevTools endpoint: is the profile running in Chrome?');
const browser = await chromium.connectOverCDP(info.wsEndpoint, { noDefaults: true });
const page = await browser.contexts()[0].newPage(); // signed in, with the profile's cookies
// ...hand `page` to your agent's tools...
await page.goto('https://example.com');
await browser.close(); // disconnects; the profile keeps runningWhat to know before you rely on it
- The page tools are new in 3.5.8, and narrow. On Linux (3.5.6) and the macOS beta (3.5.5) no MCP tool reads a page’s text, clicks or types, and
browser_screenshotreturns a text description, not an image. From 3.5.8 the six page tools work on the first tab only, see nothing inside iframes, and don’t pick from select menus, upload files, hover or drag. No tool returns the DevTools endpoint. For more than that, use CDP as above. - Your agent needs your computer. The API and the MCP server run inside the XDLogin app, so an agent reaches profiles only while XDLogin is open and the machine is awake. Each running profile is a full Chrome process, so memory decides how many run at once.
- A profile doesn’t change how the agent behaves. XDLogin sets what a site can read about the device. How fast the agent clicks, what it requests and how often is up to the agent. Frameworks can also add their own marks to a page, such as browser-use’s highlight boxes, which is why the example turns those off.
- The usual fingerprint limits apply. The TLS handshake is your Chrome’s, WebGL limits and font measurements come from your hardware, and some Web Workers report this computer’s values. The test results page lists every known gap.