Skip to content
Developers

Automate your browser from TypeScript.

@sessionat/sdk is the typed client for the browser's built-in MCP server. Read your local visit analytics, manage workspaces, and drive any page from Node. Zero dependencies, connected over local loopback, MIT licensed.

npm i @sessionat/sdk

Requires the free Sessionat browser, then enable the MCP server in chrome://sessionat-mcp/.

Quickstart

Connect in one line. Read data, then drive the page.

connect() finds your running browser automatically by reading the local discovery file it writes (loopback port plus a bearer token), then health-probes each candidate to pick the live one.

quickstart.ts
import { Sessionat } from "@sessionat/sdk";

// Zero-config: auto-discovers your running browser over local loopback.
const browser = await Sessionat.connect();

// Your private, local analytics (read-only, no approval needed):
const top = await browser.analytics.topSites({ range: "7d", orderBy: "active_seconds" });
console.log(top.top_sites); // [{ host, visit_count, active_seconds, category }]

// Drive the live browser (one-time approval in chrome://sessionat-mcp/):
await browser.tabs.open("https://news.ycombinator.com");
await browser.page.waitFor({ text: "Hacker News" });
const shot = await browser.page.screenshot(); // { data, mimeType }
What you can build

Three surfaces, one typed client.

Query your own data

Top sites, focus categories, time-of-day buckets, and full visit history. Everything is stored locally and nothing is uploaded, so analytics reads need no approval.

analytics · visits
Manage workspaces

List, inspect, and create Arc-style workspaces, and read the tabs inside each one. Wire your own scripts into the same workspace model the browser uses.

workspaces
Automate the live browser

Open, focus, and close tabs, navigate, read page text, click, type, scroll, wait, and screenshot. It pierces shadow DOM, cross-origin iframes, and canvas apps.

tabs · page
Connection

Auto-discovery, or be explicit.

In most cases Sessionat.connect() just works. For CI, multiple profiles, or remote setups you can pass an endpoint and token directly, or point at a profile or discovery file. You can also configure it entirely from the environment.

SESSIONAT_MCP_URL + SESSIONAT_MCP_TOKENUse this exact endpoint and skip discovery.
SESSIONAT_MCP_DISCOVERYPath to an mcp.json discovery file.
SESSIONAT_PROFILE_DIRA profile dir; reads <dir>/mcp.json.
connect.ts
// Explicit endpoint (copy from chrome://sessionat-mcp/, "Other" tab):
await Sessionat.connect({ url: "http://127.0.0.1:54321/mcp", token: "..." });

// Or point at a specific profile / discovery file:
await Sessionat.connect({ profileDir: "/path/to/Sessionat/Default" });
await Sessionat.connect({ discoveryFile: "/path/to/mcp.json" });

Local-only by design

The connection is loopback-only (127.0.0.1) and bearer-token authenticated. Your visit data never leaves your device, and the SDK makes no outbound calls of its own beyond the local browser.

Permissions

Reads are open. Writes need one-time approval.

Reading analytics, visits, and workspaces is open. Every write or automation tool (opening tabs, clicking, typing, even listing open tabs) is gated behind a one-time, per-client approval. It is a deliberate guard against prompt-injection. The first such call throws, you approve the client once, and retry.

approval.ts
import { SessionatWriteApprovalError } from "@sessionat/sdk";

try {
  await browser.tabs.open("https://example.com");
} catch (err) {
  if (err instanceof SessionatWriteApprovalError) {
    // Open chrome://sessionat-mcp/, enable write tools for this client, retry.
    console.error(err.message);
  }
}
API reference

Five namespaces on the connected client.

Sessionat.connect() returns a typed browser with the namespaces below. There is also a low-level escape hatch: browser.listTools(), browser.callTool(name, args), and browser.request(method) for raw JSON-RPC.

browser.analyticsread-only

Your 100%-local browsing analytics. Read-only, no approval needed.

topSites({ range?, orderBy?, limit?, workspaceId? })Most-visited hosts with visit counts, active seconds, and category.
categories({ range?, workspaceId? })Time split across content categories (work, social, reference, ...).
buckets({ bucket, range?, workspaceId? })Activity grouped by hour, day of week, or day.
browser.visitsread-only

Search and read your local visit history. Read-only.

list({ range?, limit?, workspaceId? })Recent visits, newest first.
search(query, { range?, limit?, workspaceId? })Full-text search across visited pages.
forHost(host, { range?, limit?, workspaceId? })Every visit to a single host.
browser.workspacesread + write

Read your Arc-style workspaces. Creating one is write-gated.

list()All workspaces plus the active id.
active()The active workspace and its open tabs.
get(workspaceId)One workspace by id.
create({ name, color?, icon? }) *Create a new workspace.
browser.tabswrite-gated

Open, focus, and close tabs in the live browser. Write-gated.

list() *Open tabs across the browser.
active() *The currently focused tab.
open(url, { background? }) *Open a URL in a new tab.
focus(index) *Switch to a tab by index.
close(index) *Close a tab by index.
browser.pagewrite-gated

Drive the active page. Pierces shadow DOM and cross-origin iframes. Write-gated.

navigate(url) *Load a URL in the active tab.
text({ maxChars?, rootSelector?, frameUrlMatch? }) *Read the visible page text.
click({ selector?, text?, textExact?, frameUrlMatch? }) *Click by selector or visible text.
type({ selector?, fieldLabel?, text, submit?, frameUrlMatch? }) *Type into a field, optionally submit.
waitFor({ selector?, text?, textExact?, timeoutMs? }) *Wait for an element or text to appear.
scroll({ selector?, dx?, dy?, position? }) *Scroll the page or an element.
outline({ limit?, rootSelector?, frameUrlMatch? }) *Structured outline of interactive elements.
screenshot({ maxWidth? }) *Capture the viewport. Returns { data, mimeType }.
pressKey(key, { modifiers? }) *Send a real, trusted key event.
typeKeys(text) *Type text as trusted key events (canvas apps like Docs, Figma).

* write-gated: needs one-time per-client approval. Ranges are today | 7d | 30d | all.

Package

Tiny, typed, and open.

0 deps
Uses built-in fetch
ESM + CJS
Full TypeScript types
Node 18+
macOS browser required
MIT
Open source

Also published as the unscoped alias npm i sessionat-sdk.

Get the browser, then start scripting.

The SDK talks to the free Sessionat browser on your Mac. Download it, enable the MCP server, and run the quickstart.