TinyBrowse
A browser built from scratch in Rust — zero dependencies, complete control.
Based on one-agent-one-browser.
Features
- Custom HTML/CSS/JS rendering — no WebKit, no Chromium
- Zero third-party Rust dependencies — pure Rust
- Cross-platform — macOS, Linux/X11, Windows
- ES6 JavaScript — arrow functions, template literals, spread operator
- Pure headless mode — Cairo memory rendering, no X11 required
- WebSocket automation API — full browser control via JSON-RPC
- MCP server — Claude Code integration with LLM-powered research
- Autonomous research — Cerebras LLM driver for web research tasks
- WASI support — WebAssembly build for portable deployment
Quick Start
# Build everything
cargo build --release
# Run with GUI
./target/release/tinybrowse https://example.com
# Headless screenshot
./target/release/tinybrowse --headless --screenshot out.png https://example.com
# Start automation server (WebSocket on port 9222)
./target/release/tinybrowse --headless --port 9222
MCP Server
TinyBrowse includes a standalone MCP server (tinybrowse-mcp) for AI agent integration.
Build
# Standard build (stdio mode for Claude Code)
cargo build --release -p tinybrowse --bin tinybrowse-mcp
# With HTTP server support (for cloud deployment)
cargo build --release -p tinybrowse --bin tinybrowse-mcp --features http-server
Server Modes
| Mode | Usage | Best For |
|---|---|---|
| stdio (default) | tinybrowse-mcp | Claude Code, local MCP |
| HTTP | tinybrowse-mcp --http --port 8080 | Docker, VPS, cloud |
HTTP Mode Example:
# Start HTTP server
./target/release/tinybrowse-mcp --http --port 8080
# Test with curl
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Configure Claude Code
Add to ~/.mcp.json:
{
"mcpServers": {
"tinybrowse": {
"command": "/path/to/tinybrowse/rust-engine/target/release/tinybrowse-mcp",
"args": [],
"env": {
"TINYFISH_WS_URL": "ws://localhost:9222",
"TINYBROWSE_API_KEY": "your-server-api-key",
"CEREBRAS_API_KEY": "your-cerebras-api-key",
"MIXEDBREAD_API_KEY": "your-mixedbread-api-key"
},
"description": "TinyBrowse - Pure Rust MCP with LLM research capabilities"
}
}
}
Environment Variables
| Variable | Description | Required |
|---|---|---|
TINYBROWSE_API_KEY | Server authentication key (prefix: tb_) | Optional |
CEREBRAS_API_KEY | Cerebras API key for LLM research (prefix: csk-) | For research |
MIXEDBREAD_API_KEY | MixedBread API for semantic search | Optional |
TINYFISH_WS_URL | WebSocket URL for browser | Default: ws://localhost:9222 |
Generate API Keys
# Create .env.local with new keys
cat > .env.local << 'EOF'
TINYBROWSE_API_KEY=tb_$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9')
CEREBRAS_API_KEY=csk-your-key-from-cerebras
EOF
MCP Tools
| Tool | Description |
|---|---|
search_only | Search web, return URLs only |
search | Search and browse top result |
browse | Navigate to URL, get page content |
action | Click, type, scroll in browser |
page | Get current page state |
proxy | Configure proxy settings |
sessions | List/manage browser sessions |
research | LLM-powered autonomous research |
Research Tool
The research tool uses Cerebras LLM (GLM 4.7, ~200 tok/s) to autonomously search and browse the web.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "research",
"arguments": {
"task": "What is the population of Tokyo?",
"max_turns": 8,
"verbose": true,
"proxy": "http://user:pass@proxy:8080"
},
"api_key": "your-tinybrowse-api-key"
}
}
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
task | string | required | Research question or task |
max_turns | int | 100 | Maximum LLM turns |
verbose | bool | false | Enable detailed console logging |
proxy | string | null | Proxy URL for all requests |
Verbose Mode
Enable verbose: true to see detailed console output:
╔══════════════════════════════════════════════════════════════╗
║ TINYBROWSE RESEARCH - VERBOSE MODE ║
╚══════════════════════════════════════════════════════════════╝
[CONFIG] max_turns=8, proxy=None
[TASK] What is the population of Tokyo?
────────────────────────────────────────────────────────────
[TOOL CALL] search → population of Tokyo
[RESULT 1] Tokyo Population 2026 - https://worldpopulationreview.com/...
[RESULT 2] Tokyo - Wikipedia - https://en.wikipedia.org/wiki/Tokyo
[TOOL RESULT] 5 results returned
[LLM] model=zai-glm-4.7 tokens=733 time=322ms speed=189 tok/s
────────────────────────────────────────────────────────────
╔══════════════════════════════════════════════════════════════╗
║ RESEARCH COMPLETE ║
╚══════════════════════════════════════════════════════════════╝
[STATS] time=9918ms
[STATS] total_tokens=6532
[STATS] tool_calls=7
[STATS] pages_browsed=3
Proxy Support
All browser tools support proxy configuration:
{
"name": "browse",
"arguments": {
"url": "https://example.com",
"proxy": "socks5://user:pass@proxy:1080"
}
}
Supported formats:
http://host:porthttp://user:pass@host:portsocks5://host:port
Testing
# Load environment
source .env.local
# Test tools list
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | \
./target/release/tinybrowse-mcp
# Test search
printf '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_only","arguments":{"query":"Rust WebAssembly"},"api_key":"%s"}}' "$TINYBROWSE_API_KEY" | \
CEREBRAS_API_KEY="$CEREBRAS_API_KEY" ./target/release/tinybrowse-mcp
# Test research with verbose
printf '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"research","arguments":{"task":"What is the capital of France?","max_turns":5,"verbose":true},"api_key":"%s"}}' "$TINYBROWSE_API_KEY" | \
CEREBRAS_API_KEY="$CEREBRAS_API_KEY" ./target/release/tinybrowse-mcp
WASI Build
Build for WebAssembly (WASI) for portable deployment:
# Install target
rustup target add wasm32-wasip1
# Build (use rustup's cargo, not Homebrew)
rustup run stable cargo build --release \
--target wasm32-wasip1 \
--no-default-features \
--features wasi \
-p tinybrowse \
--bin tinybrowse-mcp
# Run locally with Wasmer
wasmer run ./target/wasm32-wasip1/release/tinybrowse-mcp.wasm
Note: WASI build works for MCP protocol but HTTP networking requires native build or Wasmer Edge with networking enabled.
Wasmer Deployment
# Login to Wasmer
wasmer login <your-token>
# Create app.yaml
cat > app.yaml << EOF
kind: wasmer.io/App.v0
name: tinybrowse-mcp
owner: your-username
package: .
env:
CEREBRAS_API_KEY: "your-key"
TINYBROWSE_API_KEY: "your-key"
EOF
# Deploy
mkdir -p data tmp # Required directories
wasmer deploy --publish-package
Docker (Headless)
Run in Docker without any X11 requirements:
# Build
docker build -t tinybrowse .
# Run headless with WebSocket API
docker run -d -p 9222:9222 tinybrowse --headless --port 9222
# Run MCP server with environment
docker run -i \
-e CEREBRAS_API_KEY=your-key \
-e TINYBROWSE_API_KEY=your-key \
tinybrowse-mcp
Docker Compose
docker compose up -d
docker compose ps # Should show "Up", not "Restarting"
WebSocket Automation API
When running with --port 9222, TinyBrowse exposes a WebSocket automation API.
Connect
const ws = new WebSocket('ws://localhost:9222');
Commands
Navigate:
{"id": 1, "method": "navigate", "params": {"url": "https://example.com"}}
Get page title:
{"id": 2, "method": "getTitle"}
Take screenshot:
{"id": 3, "method": "screenshot", "params": {"path": "/tmp/screenshot.png"}}
Get elements:
{"id": 4, "method": "getElements"}
Click element:
{"id": 5, "method": "click", "params": {"x": 100, "y": 200}}
Type text:
{"id": 6, "method": "type", "params": {"text": "hello world"}}
Set proxy:
{"id": 7, "method": "setProxy", "params": {"url": "http://proxy:8080"}}
Example with websocat
# Install websocat
brew install websocat # macOS
# or: cargo install websocat
# Connect and send commands
echo '{"id":1,"method":"navigate","params":{"url":"https://example.com"}}' | websocat ws://localhost:9222
# Interactive mode
websocat ws://localhost:9222
{"id":1,"method":"navigate","params":{"url":"https://example.com"}}
{"id":2,"method":"getTitle"}
{"id":3,"method":"screenshot","params":{"path":"/tmp/test.png"}}
Building
Release Build
cargo build --release
Individual Binaries
# Browser GUI
cargo build --release -p tinybrowse --bin tinybrowse
# MCP server
cargo build --release -p tinybrowse --bin tinybrowse-mcp
# WASI build
rustup run stable cargo build --release \
--target wasm32-wasip1 \
--no-default-features --features wasi \
-p tinybrowse --bin tinybrowse-mcp
System Dependencies
macOS
System frameworks only (CoreGraphics/CoreText/ImageIO) — no extra installs.
Linux/X11
For GUI mode:
# Ubuntu/Debian
sudo apt-get install -y libx11-dev libxft-dev libcairo2-dev librsvg2-dev \
libcurl4-openssl-dev libpng-dev libjpeg-turbo8-dev libwebp-dev
# Arch
sudo pacman -S --needed libx11 libxft cairo librsvg curl libpng libjpeg-turbo libwebp
For headless mode: Only Cairo is required — no X11 needed:
# Ubuntu/Debian
sudo apt-get install -y libcairo2-dev librsvg2-dev libpng-dev
# Arch
sudo pacman -S --needed cairo librsvg libpng
Windows
WinHTTP, WIC, Direct2D/DirectWrite — built into Windows 10/11.
Command Line Arguments
| Argument | Description |
|---|---|
<target> | Path to HTML file or http(s):// URL |
--screenshot <path> | Write PNG screenshot and exit |
--headless | Don't create a window (pure Cairo rendering on Linux) |
--port <port> | Start WebSocket automation server |
--width <px> | Viewport width (default: 1024) |
--height <px> | Viewport height (default: 768) |
Environment:
OAB_SCALE— Override DPI scale factor
Tests
cargo test
# Linux headless (for GUI tests)
xvfb-run -a cargo test
Architecture
tinybrowse/
├── crates/
│ └── tinybrowse/
│ ├── bin/
│ │ └── tinybrowse-mcp.rs # MCP server with LLM research
│ ├── platform/
│ │ ├── macos/ # CoreGraphics rendering
│ │ ├── x11/ # Cairo/X11 rendering + pure headless
│ │ └── windows/ # Direct2D rendering
│ ├── html/ # HTML parser
│ ├── css/ # CSS parser & cascade
│ ├── js/ # ES6 JavaScript engine
│ ├── render/ # Layout & paint
│ ├── llm.rs # Cerebras LLM driver
│ ├── http_client.rs # Cross-platform HTTP (native + WASI)
│ └── search.rs # Web search (MixedBread + DuckDuckGo)
├── wasmer.toml # WASI deployment config
├── app.yaml # Wasmer Edge config (gitignored)
└── docker-compose.yml # Container orchestration
Security
- API Key Validation: Server validates key format on startup
- Client Authentication: Requests require
api_keyin params whenTINYBROWSE_API_KEYis set - Key Prefixes:
tb_for TinyBrowse,csk-for Cerebras
License
MIT