SlimAtlas AI
README.md
<div align="center">
<img src="img/slim-atlas-logo.svg" alt="SlimAtlas AI Logo" width="180">
</div>
<h1 align="center">SlimAtlas AI</h1>
<p align="center">
An extremely lightweight standalone MCP server for token-efficient browser automation for AI agents, designed to run on servers.
</p>
<p align="center">
~5x less memory than Chromium. ~25x smaller snapshots than raw HTML.
</p>
<p align="center">
Works with <strong>Linux</strong> and <strong>macOS</strong>.
</p>
<p align="center">
Built on <a href="https://bun.sh/">Bun</a>, <a href="https://pptr.dev/">Puppeteer</a> and <a href="https://github.com/lightpanda-io/browser">Lightpanda</a>.
</p>
<p align="center">
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat" alt="license MIT">
<img src="https://img.shields.io/badge/Bun-1.3.11-orange?style=flat&logo=bun&logoColor=white" alt="bun 1.3.11">
<img src="https://img.shields.io/badge/TypeScript-5.9-3178C6?style=flat" alt="TypeScript 5.9">
</p>
## Features
- **Browser Automation**: Navigate, click, type, fill forms, and evaluate JavaScript
- **Lightweight by Default**: ~5x less memory than Chromium, ~25x smaller snapshots than raw HTML
- **Page Snapshots**: Get compact YAML accessibility tree snapshots with unique node IDs for precise element targeting
- **LLM-Optimized Context**: Snapshots are stripped to semantic essentials, keeping context usage tiny so you can fit more pages and longer sessions into the same window
- **Configurable Fallback Browser**: Two-level model — lightweight browser first, then escalate to **headful Chrome**, **Browserless** or **Browserbase** cloud browsers only when the default is bot-detected, crashes, or times out.
- **Rate Limiting**: Stay under the radar. Enforce a configurable minimum delay between requests to specific domains with wildcard patterns (`*`, `*.reddit.com`) plus optional jitter, so your agent paces itself instead of hammering a site and tripping its bot defenses.
- **Proxy Support**: Route every request through an HTTP proxy with a single `PROXY_SERVER` setting — applied automatically to **both** browser layers, so your real IP never touches the target. Supports inline basic auth (`http://user:pass@host:port`).
- **Robust Session Management**: Per-session serialization, optional session cap (`MAX_SESSIONS`), mid-session crash recovery with history replay, and graceful shutdown.
- **Session Management**: Reuse sessions across multiple operations with unique session IDs
- **Cross-Platform**: Works on Linux and macOS, with a configurable real-browser fallback when needed
## Benchmark
SlimAtlas was benchmarked against Playwright MCP (headless and headful Chromium) using [slim-atlas-bench](https://github.com/hasindu-madushan/slim-atlas-bench) — 25 real-world sites across 5 categories, 3 runs each (225 total), all running in Docker on localhost.
**Setup**: Each service runs as an MCP server in its own Docker container. The benchmark harness measures per-tool-call latency (navigate + snapshot) via `langchain-mcp-adapters`, and captures container RAM via the Docker stats API. Snapshots are token-estimated at ~4 chars/token (same methodology as the raw HTML baseline).
| Metric (median) | SlimAtlas | Playwright Headless | Playwright Headful |
|---|---|---|---|
| Navigate + Snapshot (ms) | 1,383 | 1,116 | 687 |
| Container RAM (MB) | 124 | 635 | 815 |
| Snapshot Tokens | 414 | 940 | 1,843 |
SlimAtlas trades navigation speed for dramatically lower resource usage. Chromium is faster on page load, but uses **5–6x more RAM** and produces **2–4x more tokens** per snapshot — the two costs that dominate at scale in agent workflows.
### Results
<table>
<tr>
<td align="center"><strong>Snapshot Tokens</strong><br><img src="img/bench-tokens.png" alt="Snapshot Tokens" width="420"></td>
<td align="center"><strong>Memory Usage</strong><br><img src="img/bench-memory.png" alt="Memory Usage" width="420"></td>
</tr>
</table>
## Installation
Requires [Bun](https://bun.sh/) ≥1.1.
```bash
# Install dependencies
bun install
```
Browser binary is downloaded automatically on first run.
**Fallback browser**: Level 1 is always the lightweight browser. Level 2 is `FALLBACK_BROWSER` — one of `headful` (headful Chrome), `browserbase` (cloud), `browserless` (cloud), or `none` (default, no fallback). When the default crashes, times out, or is bot-detected, the session switches once to the configured fallback. Chrome is bundled by Puppeteer; Browserbase/Browserless require API credentials (see below).
## Usage
### Run the MCP Server
```bash
# Standalone HTTP server (default)
bun run src/index.ts --port=8080
# Remote, authenticated
MCP_AUTH_TOKEN=s3cret bun run src/index.ts --host=0.0.0.0 --port=8080
```
SlimAtlas exposes a single **Streamable HTTP** `/mcp` endpoint. Each client gets its own session via the `mcp-session-id` header; `MAX_SESSIONS` bounds concurrency. Point your MCP client at it:
```json
{
"mcpServers": {
"slimatlas": {
"url": "http://localhost:8080/mcp",
"transport": "http"
}
}
}
```
With authentication:
```json
{
"mcpServers": {
"slimatlas": {
"url": "http://your-host:8080/mcp",
"transport": "http",
"headers": { "Authorization": "Bearer s3cret" }
}
}
}
```
**Tip**: Use `--host=0.0.0.0` to expose remotely, but always set `MCP_AUTH_TOKEN` when doing so. The server speaks plain HTTP only — terminate TLS in front (Caddy/nginx) or the Bearer token travels the network in cleartext.
### Docker
The image bundles the Lightpanda binary at build time, so the container starts self-contained and never re-downloads:
```bash
docker build -t slimatlas .
docker run -p 8080:8080 -e MCP_AUTH_TOKEN=s3cret slimatlas
# -> http://localhost:8080/mcp
```
Pin a Lightpanda release for reproducible builds (any `lightpanda-io/browser` tag — default `nightly`):
```bash
docker build --build-arg LIGHTPANDA_VERSION=0.3.3 -t slimatlas:0.3.3 .
```
Multi-arch is handled automatically — the build detects the container's arch via `uname -m` (`x86_64` → `lightpanda-x86_64-linux`, `aarch64` → `lightpanda-aarch64-linux`), so the binary always matches the platform being built.
> **Apple Silicon (M-series Macs):** pass `--platform linux/arm64` to build/run natively. Without it, Docker Desktop may default to `amd64` and run the container under Rosetta, which fails to launch the Lightpanda binary (`rosetta error: failed to open elf …`).
> ```bash
> docker build --platform linux/arm64 --build-arg FALLBACK_BROWSER=headful -t slimatlas .
> docker run --platform linux/arm64 --name slimatlas -p 8080:8080 -e MCP_AUTH_TOKEN=s3cret -e LIGHTPANDA_POOL_SIZE=10 -e CHROME_POOL_SIZE=10 slimatlas
> ```
**Headful fallback variant.** The default image runs Lightpanda only (`FALLBACK_BROWSER=none`). To enable the headful Chrome fallback (needed only if you set `FALLBACK_BROWSER=headful` at runtime), build the headful variant — it adds the Chrome runtime libraries + Xvfb (~150MB) and presets `FALLBACK_BROWSER=headful`:
```bash
docker build --build-arg LIGHTPANDA_VERSION=0.3.3 FALLBACK_BROWSER=headful -t slimatlas:headful .
docker run -p 8080:8080 -e MCP_AUTH_TOKEN=s3cret slimatlas:headful
```
Xvfb is started lazily inside the container on the first session that escalates to headful Chrome — no entrypoint or manual `xvfb-run` needed.
### Stdio Mode
For MCP clients that spawn the server as a subprocess (local, single-client), use stdio transport:
```bash
bun run src/index.ts
```
```json
{
"mcpServers": {
"slimatlas": {
"command": "bun",
"args": ["run", "path/to/mcp/src/index.ts"]
}
}
}
```
### Configuration
Add to your MCP client configuration:
```json
{
"mcpServers": {
"slimatlas": {
"command": "bun",
"args": ["run", "path/to/mcp/src/index.ts"]
}
}
}
```
**Tip**: Set `FALLBACK_BROWSER=none` to use the lightweight browser only and propagate errors honestly. Use `headful` or `browserbase` to enable a real-browser fallback.
See [docs/configs.md](docs/configs.md) for all environment variables and CLI flags.
### Fallback browser
| `FALLBACK_BROWSER` | Level 2 browser | Notes |
|---|---|---|
| `none` (default) | — | Lightweight browser only; errors propagate honestly |
| `headful` | Headful Chrome | Real window on macOS; needs `xvfb` on headless Linux |
| `browserbase` | Browserbase cloud | Requires `BROWSERBASE_API_KEY` + `BROWSERBASE_PROJECT_ID` |
**Skip the default browser for known-hard domains** with `SKIP_LIGHTPANDA_DOMAINS` (comma-separated, subdomain-aware). Matched hosts start directly on the fallback browser. Requires `FALLBACK_BROWSER != none` (otherwise the list is ignored with a warning).
### Rate limiting
Polite, anti-detection pacing for your agent. `RateLimiter` enforces a server-wide minimum delay between `browser_navigate` calls to the domains you list, with optional random jitter so the cadence isn't a fixed, fingerprintable interval. Buckets are keyed per host across **all** sessions (because the target site sees your IP, not your sessions), and patterns support wildcards:
| Pattern | Matches | Bucket |
|---|---|---|
| `*` | Every host | Each host throttled independently |
| `*.reddit.com` | Subdomains only (`www.reddit.com`, `old.reddit.com`) — **not** `reddit.com` | All matching subdomains share one bucket |
| `g2.com` | `g2.com` + its subdomains | Apex + subdomains share one bucket |
Disabled by default. Enable with a non-empty domain list **and** a non-zero delay:
```bash
# Via CLI flags
bun run src/index.ts --rate-limit-domains=*.reddit.com,g2.com --rate-limit-min-delay-ms=2000 --rate-limit-jitter-ms=1500
# Or via environment / .env
RATE_LIMIT_DOMAINS=*.reddit.com,g2.com
RATE_LIMIT_MIN_DELAY_MS=2000
RATE_LIMIT_JITTER_MS=1500
```
### Proxy
Keep your real IP off the target. Set a single `PROXY_SERVER` and SlimAtlas routes **all** HTTP traffic from **both** browser layers through it — no per-browser wiring needed:
- **Lightweight browser (level 1)** — forwarded via its native `--http-proxy` flag.
- **Chrome fallback (level 2)** — applied via `--proxy-server` at launch.
```bash
# Via CLI flag
bun run src/index.ts --proxy-server=http://host:8080
# Or via environment / .env
PROXY_SERVER=http://user:pass@host:8080
```
Inline basic auth (`http://user:pass@host:port`) is supported on the lightweight browser layer. For IP-allowlisted proxies (no credentials) it just works on both layers. *(Per-page Chrome authentication via `page.authenticate` is on the roadmap.)*
### CLI Flags
Every environment variable can also be passed as a lower-case CLI flag in `--flag=value` form. CLI flags override environment variables. Unknown flags cause the server to exit at startup. See [docs/configs.md](docs/configs.md) for the full list.
```bash
bun run src/index.ts --fallback-browser=headful --lightpanda-pool-size=3 --skip-lightpanda-domains=g2.com --navigate-timeout=60000
```
To use flags from an MCP client, append them to the `args` array:
```json
{
"mcpServers": {
"slimatlas": {
"command": "bun",
"args": [
"run",
"path/to/mcp/src/index.ts",
"--fallback-browser=headful",
"--skip-lightpanda-domains=g2.com,linkedin.com",
"--chrome-pool-size=3"
]
}
}
}
```
### Usage Workflow
```python
# Example: Navigate, snapshot, and interact with a page
# 1. Navigate to a URL (creates a new session automatically)
result = mcp.call("browser_navigate", {"url": "https://example.com"})
# Returns: session_id: abc1, result: Navigated to https://example.com. Title: Example Domain
# 2. Take a snapshot to see the page structure
snapshot = mcp.call("browser_snapshot", {"session_id": "abc1"})
# Returns YAML with node IDs like: 0: {type: div, children: ...}
# 3. Click a node by ID (from the snapshot)
mcp.call("browser_click", {"session_id": "abc1", "nodeId": 2})
# 4. Type into a search box
mcp.call("browser_type", {"session_id": "abc1", "nodeId": 5, "text": "search query"})
# 5. Close the session when done
mcp.call("browser_close", {"session_id": "abc1"})
```
## Available Tools
| Tool | Description | Value |
|------|-------------|-------|
| `browser_navigate` | Navigate to a URL with configurable wait strategy | Entry point for all web interactions. Supports `load`, `domcontentloaded`, `networkidle0`, `networkidle2` |
| `browser_snapshot` | Get YAML accessibility tree with unique node IDs | Structured page representation ideal for LLM understanding. Node IDs enable precise targeting for clicks/types |
| `browser_view_node` | View specific node content by ID (text or image) | Inspect individual elements without full page re-read. Returns images as base64 for visual verification |
| `browser_click` | Click element by node ID or CSS selector | Node ID (from snapshot) is recommended over CSS selectors for reliability and simplicity |
| `browser_type` | Type text into element with optional keystroke delay | Simulates human typing. Use for search boxes, forms, and text inputs |
| `browser_fill` | Fill input element with a value instantly | Faster than `browser_type` for form fields. Clears existing value before filling |
| `browser_go_back` | Navigate back in browser history | Essential for multi-step workflows and correcting navigation mistakes |
| `browser_go_forward` | Navigate forward in browser history | Complements `browser_go_back` for bidirectional navigation |
| `browser_reload` | Reload the current page | Refresh dynamic content or recover from stale page state |
| `browser_get_page_info` | Get current page URL and title | Quick way to verify navigation success and current context |
| `browser_close` | Close browser session and free resources | Important for cleanup. Sessions auto-close on timeout, but explicit closing is recommended |
## Running Tests
```bash
# Run all tests
bun test
# Run tests in watch mode
bun test --watch
```
## Requirements
- [Bun](https://bun.sh/) ≥1.1
- Linux or macOS (downloads automatically) or Chrome/Chromium (fallback)
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues