Content
# Browser Session Toolkit
Toolkit for managed browser automation on Playwright. The project is needed so that an agent or script can work with a regular Chromium as with a sustainable session: open a site, go through the UI, click the download button, read the page, take a screenshot, check the login and reuse cookies between steps.
The main idea: do not write raw Playwright code every time, but use a set of ready-made operations with normal element search, persistent profile, download handling and MCP interface for Codex.
## What it can do
- Launches persistent Chromium sessions with a separate profile and download folder.
- Reuses an active session through `get_or_start_session`.
- Navigates, clicks, enters text, presses keys and scrolls the page.
- Searches for elements by visible text, CSS, XPath, ARIA/role and RegExp.
- Works with modals and portal-heavy UI through `scope` and `scopeSelector`.
- Makes introspection of the page: visible elements, fields, buttons/links, dialogs, active scope, description of a specific target.
- Safely handles downloads through browser download event.
- Reads the contents of the page as markdown/readable/raw.
- Takes screenshots.
- Makes direct HTTP requests through cookies of the active browser session.
- Checks cookies and approximate signed-in status.
- Publishes as stdio MCP server for Codex and other MCP clients.
## Installation
```bash
npm install
npx playwright install chromium
```
`node_modules/` is not stored in git. If the folder does not exist after a clean clone, this is normal.
## Quick start via JS
```js
import {
get_or_start_session,
navigate,
click,
type,
get_content,
screenshot,
close_session
} from "./src/browser-tools.mjs";
const session = await get_or_start_session({
headless: false,
userDataDir: "%USERPROFILE%/.rootlord/browser/sessions/default",
downloadDir: "%USERPROFILE%/.rootlord/browser/downloads/default"
});
await navigate("https://example.com", { sessionId: session.sessionId });
await click("More information", { sessionId: session.sessionId });
const page = await get_content({
sessionId: session.sessionId,
format: "markdown"
});
const image = await screenshot({
sessionId: session.sessionId,
full_page: true
});
await close_session({ sessionId: session.sessionId });
```
In the examples below, JS calls are shown. When working through MCP, call the same tools with the same arguments, and for RegExp use `targetPattern` and `targetFlags`.
## Quick start via MCP
Launching MCP server:
```bash
npm run mcp
```
The same directly:
```bash
node ./src/browser-mcp-server.mjs
```
Example of connection in `%USERPROFILE%\.codex\config.toml`:
```toml
[mcp_servers.browser_toolkit]
command = "node"
args = ["C:/path/to/rootlord/browser/src/browser-mcp-server.mjs"]
```
After changing the MCP config, restart Codex, otherwise new native tools may not appear. An empty `resources/list` for this server is normal: the server publishes tools, not resources.
## Basic workflow
1. Create or reuse a session:
```js
const session = await get_or_start_session({
headless: false,
userDataDir: "%USERPROFILE%/.rootlord/browser/sessions/work",
downloadDir: "%USERPROFILE%/.rootlord/browser/downloads/work"
});
```
2. Open a page:
```js
await navigate("https://www.investing.com/", {
sessionId: session.sessionId
});
```
3. If the UI is unclear, first look at what is on the page:
```js
await list_buttons_links({ sessionId: session.sessionId, limit: 30 });
await list_inputs({ sessionId: session.sessionId, includeHidden: true });
await get_dialogs({ sessionId: session.sessionId });
await describe_target("Download", { sessionId: session.sessionId });
```
4. Perform an action:
```js
await click("Download", {
sessionId: session.sessionId,
expectDownload: true
});
```
5. Close the session at the end of the task:
```js
await close_session({ sessionId: session.sessionId });
```
## Session management
`start_session(options)` always starts a session if the `sessionId` is not yet occupied. `get_or_start_session(options)` first searches for an active session and then starts a new one. For agent flows, it is usually better to use `get_or_start_session`.
Main options:
- `headless`: `false` if you need to see the browser; `true` for background mode.
- `sessionId`: your session name/ID.
- `userDataDir`: folder of the persistent Chromium profile. By default, outside the project: `%USERPROFILE%/.rootlord/browser/sessions/<sessionId>`.
- `downloadDir`: folder for downloaded files. By default, outside the project: `%USERPROFILE%/.rootlord/browser/downloads/<sessionId>`.
- `reuseExisting` or `reuse_existing`: reuse an active session.
- `stealth`: enabled by default; helps on sites with anti-bot checks.
- `proxy`: string or Playwright proxy object.
- `locale`, `timezoneId`, `userAgent`, `viewport`: browser context parameters.
- `timeoutMs`: basic timeout. Too large values are limited by the toolkit according to the operation type.
View active sessions:
```js
await list_sessions();
```
Close one session:
```js
await close_session({ sessionId: "work" });
```
Close the active session:
```js
await close_session();
```
## Element search
`click`, `type`, `exists`, `wait_for`, `describe_target` understand different target formats:
- Visible text: `"Download"`, `"Sign in"`.
- RegExp in JS API: `/download/i`.
- RegExp in MCP: `targetPattern: "download", targetFlags: "i"`.
- CSS selector: `"css=input[name='email']"` or regular CSS if the string looks like a selector.
- XPath: `"xpath=//button[contains(., 'Download')]"`.
- Index with multiple matches: `index` or `matchIndex`.
- Hidden fields: `allowHidden: true`.
Example for hidden date inputs:
```js
await type("2025-04-21", "input[type='date']", {
sessionId: session.sessionId,
allowHidden: true,
index: 0
});
await type("2026-04-21", "input[type='date']", {
sessionId: session.sessionId,
allowHidden: true,
index: 1
});
```
## Modals and scope
If there are modals, drawers, dropdown portals or overlays on the page, first check the active scope:
```js
await get_active_scope({ sessionId: session.sessionId });
await get_dialogs({ sessionId: session.sessionId });
```
Action within an active modal:
```js
await click("Sign in with Email", {
sessionId: session.sessionId,
scope: "active_dialog"
});
```
Action within a specific container:
```js
await type("user@example.com", "css=input[name='email']", {
sessionId: session.sessionId,
scopeSelector: "css=[data-floating-ui-portal]"
});
```
## Downloads
For buttons like `Download`, `Export`, `CSV`, `Excel`, `PDF`, use `click` instead of `page.evaluate(() => element.click())`. The toolkit subscribes to the browser download event in advance and saves the file.
```js
const result = await click("Download", {
sessionId: session.sessionId,
expectDownload: true,
downloadDir: "%USERPROFILE%/.rootlord/browser/downloads/manual"
});
console.log(result.download);
```
Useful options:
- `expectDownload`: explicitly wait for the download event.
- `downloadTimeoutMs`: download wait timeout.
- `downloadDir`: save folder.
- `downloadPath` or `saveDownloadAs`: full path or file name.
- `copyDownloadTo`: additional copy to a folder or full path.
If you need to download a URL without opening a page:
```js
await download_url({
url: "https://example.com/file.csv",
saveDir: "%USERPROFILE%/.rootlord/browser/downloads/api",
useSessionCookies: true,
sessionId: session.sessionId
});
```
`useSessionCookies: true` allows you to use cookies of the active browser session for private files/APIs.
## Reading pages and HTTP
`get_content` reads the current page:
```js
await get_content({
sessionId: session.sessionId,
format: "markdown"
});
```
Formats:
- `markdown`: HTML is cleaned through Turndown.
- `readable`: the main article is first extracted through Mozilla Readability, then markdown.
- `raw`: the original body is returned.
`fetch_url` makes an HTTP request through the request context:
```js
await fetch_url({
url: "https://example.com/api/data",
method: "GET",
useSessionCookies: true,
sessionId: session.sessionId,
extract: "markdown"
});
```
For JSON, the toolkit tries to fill `json`. For binary responses, it returns `base64`.
## Expectations and Composite Flows
Ordinary wait:
```js
await wait_for({
sessionId: session.sessionId,
waitTextIncludes: "Done",
waitTimeoutMs: 10000
});
```
Click with subsequent wait:
```js
await click_and_wait_for("Learn more", {
sessionId: session.sessionId,
waitUrlIncludes: "iana.org"
});
```
Input with subsequent wait:
```js
await type_and_wait_for("hello", "css=input[name='q']", {
sessionId: session.sessionId,
waitTextIncludes: "Search"
});
```
For long scenarios, it's better to use `run_steps`: fewer MCP round-trips, one session context, and a clear list of actions.
```js
await run_steps({
steps: [
{
action: "get_or_start_session",
headless: false,
userDataDir: "%USERPROFILE%/.rootlord/browser/sessions/investing",
downloadDir: "%USERPROFILE%/.rootlord/browser/downloads/investing"
},
{
action: "navigate",
url: "https://www.investing.com/indices/mcx-historical-data"
},
{
action: "click",
target: "Download",
expectDownload: true
}
]
});
```
## MCP Tool List
- `list_sessions`: list of active sessions.
- `start_session`: launch persistent Chromium.
- `get_or_start_session`: reuse an active session or create a new one.
- `navigate`: open URL.
- `click`: click target, including download buttons.
- `type`: enter text in target or active field.
- `press_key`: press a key.
- `scroll`: scroll the page.
- `exists`: cheap check for target presence.
- `click_and_wait_for`: click and wait for result.
- `type_and_wait_for`: input and wait for result.
- `run_steps`: perform multiple actions in one MCP call.
- `get_content`: get page content.
- `screenshot`: take a PNG screenshot.
- `wait_for`: wait for target/URL/title/text.
- `list_visible_elements`: list of visible elements.
- `list_inputs`: list of input/select/textarea, including hidden ones.
- `list_buttons_links`: list of buttons and links.
- `get_dialogs`: list of dialog/modal-like elements.
- `get_active_scope`: description of active element/scope.
- `describe_target`: diagnostics for a specific target.
- `fetch_url`: HTTP request, optionally with session cookies.
- `download_url`: download URL to a file.
- `get_cookies`: get cookies of the current context.
- `check_signed_in`: rough check for signs of an authorized session.
- `close_session`: close a session.
## Security and Git
Do not commit:
- `.cache/`
- `node_modules/`
- `.env`, `.env.*`
- `*.pem`, `*.key`, `*.p12`, `*.pfx`
- downloaded files with private data
Browser profiles contain cookies, localStorage, sessionStorage, IndexedDB, and browsing history. This can be equivalent to a login leak.
By default, the toolkit stores such data outside the project:
- `%USERPROFILE%/.rootlord/browser/sessions/...`
- `%USERPROFILE%/.rootlord/browser/downloads/...`
If `userDataDir`, `downloadDir`, `savePath`, `saveDir`, or `copyDownloadTo` point inside the project folder, the toolkit will throw an error and not write browser state/download data there.
The state folder can be overridden with the `ROOTLORD_BROWSER_STATE_DIR` environment variable, but it must also point outside the project.
`get_cookies` returns sensitive values. Do not paste its full output into issues, README, prompts, logs, or commits.
If you need to completely reset local browser sessions, close the browser/session and delete `%USERPROFILE%/.rootlord/browser/sessions`. If such a folder contained real logins and was exposed, log out of active sessions on websites and reissue tokens.
## Development
Install dependencies:
```bash
npm install
```
Start MCP server:
```bash
npm run mcp
```
Verify that profiles and dependencies are not in git:
```bash
git ls-files | rg "^(.cache|node_modules)/"
```
The command should not output anything.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.