diff --git a/.changeset/config.json b/.changeset/config.json
index 917f42b..00e3c98 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -4,7 +4,7 @@
"commit": false,
"fixed": [],
"linked": [
- ["@browseragentprotocol/protocol", "@browseragentprotocol/logger", "@browseragentprotocol/client", "@browseragentprotocol/server-playwright", "@browseragentprotocol/mcp"]
+ ["@browseragentprotocol/protocol", "@browseragentprotocol/logger", "@browseragentprotocol/client", "@browseragentprotocol/server-playwright", "@browseragentprotocol/mcp", "@browseragentprotocol/cli"]
],
"access": "public",
"baseBranch": "main",
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 95fbd5d..5d7b016 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,6 +6,10 @@ on:
pull_request:
branches: [main]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
build:
runs-on: ubuntu-latest
@@ -35,5 +39,66 @@ jobs:
- name: Lint
run: pnpm lint
+ test:
+ runs-on: ubuntu-latest
+ needs: build
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: ["20", "22"]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 9.15.0
+
+ - name: Setup Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: "pnpm"
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build
+ run: pnpm build
+
- name: Test
run: pnpm test
+
+ coverage:
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 9.15.0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "22"
+ cache: "pnpm"
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build
+ run: pnpm build
+
+ - name: Test with coverage
+ run: pnpm test:coverage
+
+ - name: Upload coverage
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-report
+ path: coverage/
+ retention-days: 14
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..db0dcff
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,128 @@
+# Contributing to Browser Agent Protocol
+
+Thank you for your interest in contributing to BAP. This document covers the process for contributing to this project.
+
+## Getting Started
+
+### Prerequisites
+
+- Node.js >= 20.0.0
+- pnpm 9.x (`corepack enable && corepack prepare pnpm@9.15.0 --activate`)
+- Git
+
+### Setup
+
+```bash
+git clone https://github.com/browseragentprotocol/bap.git
+cd bap
+pnpm install
+pnpm build
+```
+
+### Verify your setup
+
+```bash
+pnpm typecheck # Type checking across all packages
+pnpm lint # ESLint
+pnpm test # Vitest test suites
+```
+
+## Repository Structure
+
+```
+packages/
+ protocol/ # Core types, schemas, and shared utilities (Zod)
+ logger/ # Structured logging
+ client/ # TypeScript WebSocket client SDK
+ server-playwright/ # Playwright-based BAP server
+ mcp/ # MCP (Model Context Protocol) bridge
+ cli/ # Shell CLI for browser automation
+ python-sdk/ # Python client SDK
+```
+
+**Dependency order**: `protocol` -> `logger` -> `client` / `server-playwright` -> `mcp` / `cli`
+
+All packages are built with `tsup` and managed with `turborepo`.
+
+## Development Workflow
+
+### 1. Create a branch
+
+```bash
+git checkout -b feature/your-feature-name
+```
+
+### 2. Make changes
+
+- Protocol changes go in `packages/protocol/src/types/`
+- Server handler changes go in `packages/server-playwright/src/server.ts`
+- Client SDK changes go in `packages/client/src/index.ts`
+- CLI commands go in `packages/cli/src/commands/`
+
+### 3. Build and test
+
+```bash
+pnpm build # Build all packages (respects dependency order)
+pnpm typecheck # Must pass with zero errors
+pnpm lint # Must pass with zero errors (warnings are acceptable)
+pnpm test # All tests must pass
+```
+
+### 4. Submit a pull request
+
+- Keep PRs focused on a single change
+- Include tests for new functionality
+- Update relevant README files if adding user-facing features
+- Reference any related issues
+
+## Code Style
+
+- TypeScript strict mode is enabled across all packages
+- ESLint with `typescript-eslint` rules
+- Prettier for formatting (`pnpm format`)
+- Use Zod schemas for all protocol types (no raw `interface` for wire types)
+- All new protocol fields must be optional for backward compatibility
+
+## Testing
+
+Tests use [Vitest](https://vitest.dev/). Each package has its own test configuration.
+
+```bash
+# Run all tests
+pnpm test
+
+# Run tests for a specific package
+pnpm --filter @browseragentprotocol/protocol test
+
+# Run with coverage
+pnpm test:coverage
+```
+
+### Test guidelines
+
+- Schema validation tests go in `packages/protocol/src/__tests__/`
+- CLI flag/command tests go in `packages/cli/__tests__/`
+- MCP tool tests go in `packages/mcp/src/__tests__/`
+- Integration tests that require a browser go in `packages/server-playwright/src/__tests__/`
+
+## Protocol Changes
+
+BAP uses JSON-RPC 2.0 over WebSocket. If you are changing the protocol:
+
+1. Update Zod schemas in `packages/protocol/src/types/`
+2. Export new types from `packages/protocol/src/types/index.ts`
+3. Implement server handling in `packages/server-playwright/src/server.ts`
+4. Add client passthrough in `packages/client/src/index.ts`
+5. Add schema validation tests
+6. All new fields must be **optional** to maintain backward compatibility
+
+## Reporting Issues
+
+- Use [GitHub Issues](https://github.com/browseragentprotocol/bap/issues)
+- Include BAP version, Node.js version, and browser type
+- For bugs: include steps to reproduce, expected vs actual behavior
+- For feature requests: describe the use case and proposed API
+
+## License
+
+By contributing, you agree that your contributions will be licensed under the [Apache License 2.0](LICENSE).
diff --git a/README.md b/README.md
index d880a86..e8ff8d9 100644
--- a/README.md
+++ b/README.md
@@ -1,58 +1,93 @@
# Browser Agent Protocol (BAP)
+[](https://www.npmjs.com/package/@browseragentprotocol/cli)
[](https://www.npmjs.com/package/@browseragentprotocol/mcp)
[](https://opensource.org/licenses/Apache-2.0)
-An open standard for AI agents to interact with web browsers.
+An open standard for AI agents to interact with web browsers. Two interfaces: **CLI** for shell-based agents, **MCP** for protocol-native agents.
-> **v0.2.0:** Renamed MCP tools, auto-reconnect, multi-context support, streaming, and more. APIs may evolve based on feedback.
-
-## Overview
+```bash
+# CLI — any agent that can run shell commands
+npx @browseragentprotocol/cli open https://example.com
+npx @browseragentprotocol/cli act 'click:text:"More information..."' snapshot
-BAP (Browser Agent Protocol) provides a standardized way for AI agents to control web browsers. It uses JSON-RPC 2.0 over WebSocket for communication and includes semantic selectors designed for AI comprehension.
+# MCP — agents with native Model Context Protocol support
+npx @browseragentprotocol/mcp
+```
-### Key Features
+## Why BAP?
-- **Semantic Selectors**: Use accessibility roles, text content, and labels instead of brittle CSS selectors
-- **Accessibility-First**: Built-in support for accessibility tree inspection
-- **AI-Optimized**: Designed for LLM-based agents with token-efficient observations
-- **MCP Integration**: Works seamlessly with [Model Context Protocol](https://modelcontextprotocol.io)
-- **Composite Actions**: Execute multi-step action sequences in a single round-trip (`agent/act`, `agent/observe`, `agent/extract`)
-- **Element References**: Stable element refs (`@submitBtn`, `@e7f3a2`) that persist across observations
-- **Screenshot Annotation**: Set-of-Marks style overlays with numbered badges for vision models
-- **Multi-Context Support**: Parallel isolated browser sessions with `context/create`, `context/list`, `context/destroy`
-- **Human-in-the-Loop Approval**: Enterprise workflow for human oversight of sensitive actions
-- **Frame Support**: Explicit frame switching for iframes with `frame/list`, `frame/switch`, `frame/main`
-- **Streaming Responses**: Chunked transfers for large observations with checksum verification
+- **Composite Actions**: Execute multi-step flows in one command — 40x fewer tokens than one-action-at-a-time
+- **Fused Operations**: Combine navigate+observe, act+observe into single server calls — 50-85% fewer roundtrips
+- **Semantic Selectors**: Target elements by purpose (`role:button:"Submit"`) not position — survives redesigns
+- **Structured Extraction**: Extract validated JSON from any page with a schema
+- **Two Interfaces**: CLI (`bap act`) for shell-based agents, MCP tools for protocol-native agents
+- **Accessibility-First**: Built on accessibility tree inspection, designed for AI comprehension
+- **Element References**: Stable refs (`@e1`, `e15`) that persist across observations
+- **Screenshot Annotation**: Set-of-Marks overlays with numbered badges for vision models
-## Packages
+## Quick Start
-### TypeScript
+### CLI — For AI Agents That Run Shell Commands
-| Package | Description | npm |
-|---------|-------------|-----|
-| [`@browseragentprotocol/protocol`](./packages/protocol) | Protocol types, schemas, and utilities | [](https://www.npmjs.com/package/@browseragentprotocol/protocol) |
-| [`@browseragentprotocol/logger`](./packages/logger) | Pretty logging utilities with colors and icons | [](https://www.npmjs.com/package/@browseragentprotocol/logger) |
-| [`@browseragentprotocol/client`](./packages/client) | TypeScript client SDK | [](https://www.npmjs.com/package/@browseragentprotocol/client) |
-| [`@browseragentprotocol/server-playwright`](./packages/server-playwright) | Server implementation using Playwright | [](https://www.npmjs.com/package/@browseragentprotocol/server-playwright) |
-| [`@browseragentprotocol/mcp`](./packages/mcp) | Model Context Protocol integration | [](https://www.npmjs.com/package/@browseragentprotocol/mcp) |
+```bash
+# Open a page and observe interactive elements
+bap open https://example.com
+bap observe --max=20
-### Python
+# Login flow in ONE command (vs 3+ separate commands)
+bap act fill:role:textbox:"Email"="user@example.com" \
+ fill:role:textbox:"Password"="secret" \
+ click:role:button:"Sign in"
-| Package | Description | PyPI |
-|---------|-------------|------|
-| [`browser-agent-protocol`](./packages/python-sdk) | Python SDK with async/sync APIs | [](https://pypi.org/project/browser-agent-protocol/) |
+# Extract structured data
+bap extract --fields="title,price,rating"
-## Quick Start
+# Use semantic selectors
+bap click role:button:"Get Started"
+bap fill label:"Email" "user@example.com"
+```
-BAP works with any MCP-compatible client. The server auto-starts — no separate setup needed.
+Install globally or use via npx:
+
+```bash
+npm i -g @browseragentprotocol/cli
+# or
+npx @browseragentprotocol/cli
+```
+
+See the full [CLI documentation](./packages/cli) for all 26 commands, selector reference, and recipes.
+
+### MCP — For Protocol-Native Agents
+
+```
+navigate({ url: "https://example.com/login" })
+observe({ includeScreenshot: true })
+act({
+ steps: [
+ { action: "action/fill", selector: "@e1", value: "user@example.com" },
+ { action: "action/fill", selector: "@e2", value: "password123" },
+ { action: "action/click", selector: "role:button:Sign in" }
+ ]
+})
+```
+
+See the [MCP documentation](./packages/mcp) for tool reference and configuration.
+
+## Integrations
### Claude Code
+**CLI** (install skill for optimal usage):
+```bash
+npm i -g @browseragentprotocol/cli
+bap install-skill
+```
+
**MCP server** (one command):
```bash
claude mcp add --transport stdio bap-browser -- npx -y @browseragentprotocol/mcp
@@ -92,6 +127,13 @@ Restart Claude Desktop after saving.
### Codex CLI
+**CLI**:
+```bash
+npm i -g @browseragentprotocol/cli
+bap install-skill
+```
+
+**MCP**:
```bash
codex mcp add bap-browser -- npx -y @browseragentprotocol/mcp
```
@@ -124,147 +166,126 @@ args = ["-y", "@browseragentprotocol/mcp"]
Codex Desktop browsing Hacker News with BAP
-### Browser Selection
-
-By default, BAP uses your locally installed Chrome. You can choose a different browser with the `--browser` flag:
+### Gemini CLI
+**CLI**:
```bash
-npx @browseragentprotocol/mcp --browser firefox
+npm i -g @browseragentprotocol/cli
+bap install-skill
```
-| Value | Browser | Notes |
-|---|---|---|
-| `chrome` (default) | Local Chrome | Falls back to bundled Chromium if not installed |
-| `chromium` | Bundled Chromium | Playwright's built-in Chromium |
-| `firefox` | Firefox | Requires local Firefox |
-| `webkit` | WebKit | Playwright's WebKit engine |
-| `edge` | Microsoft Edge | Requires local Edge |
+**MCP** — add to `~/.gemini/settings.json`:
-In a JSON MCP config, pass the flag via args:
```json
{
"mcpServers": {
"bap-browser": {
"command": "npx",
- "args": ["-y", "@browseragentprotocol/mcp", "--browser", "firefox"]
+ "args": ["-y", "@browseragentprotocol/mcp"]
}
}
}
```
-### Using the TypeScript SDK
-
-#### Start the Server
-
-```bash
-npx @browseragentprotocol/server-playwright
-```
-
-#### Connect from TypeScript
-
-```typescript
-import { BAPClient, role } from "@browseragentprotocol/client";
+### Manus
-const client = new BAPClient("ws://localhost:9222");
-await client.connect();
+Manus supports MCP servers via its web UI (HTTP transport only):
-// Launch browser and navigate
-await client.launch({ browser: "chromium", headless: false });
-await client.createPage({ url: "https://example.com" });
+1. Go to **Settings > Integrations > Custom MCP Servers**
+2. Click **Add Server**
+3. Set transport to **HTTP** and provide your hosted BAP MCP endpoint URL
+4. Save and verify connection
-// Use semantic selectors (AI-friendly)
-await client.click(role("button", "Submit"));
-await client.fill(role("textbox", "Email"), "user@example.com");
+> **Note:** Manus requires HTTP/SSE transport. To use BAP with Manus, deploy the MCP server as an HTTP endpoint using a stdio-to-HTTP bridge like [mcp-remote](https://www.npmjs.com/package/mcp-remote), then register the URL in the Manus UI.
-// Get accessibility tree for AI reasoning
-const { tree } = await client.accessibility();
+### Other Agents
-await client.close();
-```
-
-### Using the Python SDK
+BAP CLI includes a built-in skill installer that supports 13 AI coding agent platforms:
```bash
-pip install browser-agent-protocol
+bap install-skill # Auto-detect and install to all agents
+bap install-skill --dry-run # Preview what would be installed
```
-```python
-import asyncio
-from browseragentprotocol import BAPClient, role, label
+Supported: Claude Code, Codex CLI, Gemini CLI, Cursor, GitHub Copilot, Windsurf, Roo Code, Amp, Deep Agents, OpenCode, and more.
-async def main():
- async with BAPClient("ws://localhost:9222") as client:
- # Launch browser and navigate
- await client.launch(browser="chromium", headless=False)
- await client.create_page(url="https://example.com")
+### Browser Selection
- # Use semantic selectors (AI-friendly)
- await client.click(role("button", "Submit"))
- await client.fill(label("Email"), "user@example.com")
+By default, BAP uses your locally installed Chrome. Switch browsers with:
- # Get accessibility tree for AI reasoning
- tree = await client.accessibility()
+```bash
+# CLI
+bap config browser firefox
-asyncio.run(main())
+# MCP — pass via args
+npx @browseragentprotocol/mcp --browser firefox
```
-For synchronous usage (scripts, notebooks):
+| Value | Browser | Notes |
+|---|---|---|
+| `chrome` (default) | Local Chrome | Falls back to bundled Chromium if not installed |
+| `chromium` | Bundled Chromium | Playwright's built-in Chromium |
+| `firefox` | Firefox | Requires local Firefox |
+| `webkit` | WebKit | Playwright's WebKit engine |
+| `edge` | Microsoft Edge | Requires local Edge |
-```python
-from browseragentprotocol import BAPClientSync, role
+## Packages
-with BAPClientSync("ws://localhost:9222") as client:
- client.launch(browser="chromium", headless=True)
- client.create_page(url="https://example.com")
- client.click(role("button", "Submit"))
-```
+### TypeScript
-### Semantic Selectors
+| Package | Description | npm |
+|---------|-------------|-----|
+| [`@browseragentprotocol/cli`](./packages/cli) | CLI for shell-based AI agents | [](https://www.npmjs.com/package/@browseragentprotocol/cli) |
+| [`@browseragentprotocol/mcp`](./packages/mcp) | MCP integration for protocol-native agents | [](https://www.npmjs.com/package/@browseragentprotocol/mcp) |
+| [`@browseragentprotocol/client`](./packages/client) | TypeScript client SDK | [](https://www.npmjs.com/package/@browseragentprotocol/client) |
+| [`@browseragentprotocol/server-playwright`](./packages/server-playwright) | Server implementation using Playwright | [](https://www.npmjs.com/package/@browseragentprotocol/server-playwright) |
+| [`@browseragentprotocol/protocol`](./packages/protocol) | Protocol types, schemas, and utilities | [](https://www.npmjs.com/package/@browseragentprotocol/protocol) |
+| [`@browseragentprotocol/logger`](./packages/logger) | Pretty logging utilities | [](https://www.npmjs.com/package/@browseragentprotocol/logger) |
-BAP uses semantic selectors that are more stable and readable than CSS selectors:
+### Python
-```typescript
-import { role, text, label, testId, ref } from "@browseragentprotocol/client";
+| Package | Description | PyPI |
+|---------|-------------|------|
+| [`browser-agent-protocol`](./packages/python-sdk) | Python SDK with async/sync APIs | [](https://pypi.org/project/browser-agent-protocol/) |
-// By accessibility role and name
-await client.click(role("button", "Submit"));
-await client.fill(role("textbox", "Search"));
+## Architecture
-// By visible text content
-await client.click(text("Sign in"));
+```
+AI Agent (shell) AI Agent (MCP-native)
+ │ │
+ ▼ ▼
+@browseragentprotocol/cli @browseragentprotocol/mcp
+ │ │
+ ▼ ▼
+@browseragentprotocol/client ───────┘
+ │
+ ▼ WebSocket (JSON-RPC 2.0)
+@browseragentprotocol/server-playwright
+ │
+ ▼ Playwright
+Browser (Chromium / Firefox / WebKit)
+```
-// By associated label
-await client.fill(label("Email address"), "user@example.com");
+The CLI spawns the server as a background daemon that persists across commands. The MCP bridge runs as a stdio process managed by the host agent.
-// By test ID (for automation)
-await client.click(testId("submit-button"));
+## Using the SDKs
-// By stable element reference (from agent/observe)
-await client.click(ref("@submitBtn"));
-```
+### TypeScript
-### AI Agent Methods
+```typescript
+import { BAPClient, role } from "@browseragentprotocol/client";
-BAP provides composite methods optimized for AI agents:
+const client = new BAPClient("ws://localhost:9222");
+await client.connect();
-```typescript
-// agent/observe - Get AI-optimized page snapshot
-const observation = await client.observe({
- includeAccessibility: true,
- includeInteractiveElements: true,
- includeScreenshot: true,
- maxElements: 50,
- annotateScreenshot: true, // Set-of-Marks style
-});
+await client.launch({ browser: "chromium", headless: false });
+await client.createPage({ url: "https://example.com" });
-// Interactive elements with stable refs
-for (const el of observation.interactiveElements) {
- console.log(`${el.ref}: ${el.role} - ${el.name}`);
- // @e1: button - Submit
- // @e2: textbox - Email
-}
+// Semantic selectors
+await client.click(role("button", "Submit"));
+await client.fill(role("textbox", "Email"), "user@example.com");
-// agent/act - Execute multi-step sequences atomically
+// Composite actions
const result = await client.act({
steps: [
{ action: "action/fill", params: { selector: label("Email"), value: "user@example.com" } },
@@ -272,87 +293,55 @@ const result = await client.act({
{ action: "action/click", params: { selector: role("button", "Sign In") } },
],
});
-console.log(`Completed ${result.completed}/${result.total} steps`);
-
-// agent/extract - Extract structured data
-const data = await client.extract({
- instruction: "Extract all product names and prices",
- schema: {
- type: "array",
- items: {
- type: "object",
- properties: {
- name: { type: "string" },
- price: { type: "number" },
- },
- },
- },
-});
-```
-> **Note:** `agent/extract` (and `extract` in MCP) uses heuristic-based extraction (CSS patterns). For complex pages, consider using `content` to get page content as markdown and extract data yourself.
+await client.close();
+```
-## Server Options
+### Python
```bash
-npx @browseragentprotocol/server-playwright [options]
-
-Options:
- -p, --port WebSocket port (default: 9222)
- -h, --host Host to bind to (default: localhost)
- -b, --browser Browser: chromium, firefox, webkit (default: chromium)
- --headless Run in headless mode (default: true)
- --no-headless Run with visible browser window
- -t, --timeout Default timeout in milliseconds (default: 30000)
- -d, --debug Enable debug logging
- --token Authentication token for client connections
- --help Show help
- -v, --version Show version
+pip install browser-agent-protocol
```
-## CLI Tools
+```python
+import asyncio
+from browseragentprotocol import BAPClient, role, label
-### Python CLI
+async def main():
+ async with BAPClient("ws://localhost:9222") as client:
+ await client.launch(browser="chromium", headless=False)
+ await client.create_page(url="https://example.com")
-```bash
-# Test connection to a BAP server
-bap connect ws://localhost:9222
+ await client.click(role("button", "Submit"))
+ await client.fill(label("Email"), "user@example.com")
-# Get server info
-bap info ws://localhost:9222 --json
+asyncio.run(main())
```
-### TypeScript Server CLI
+## Selectors
-```bash
-# Start the server
-npx @browseragentprotocol/server-playwright --port 9222 --no-headless
+BAP uses semantic selectors that survive DOM changes:
-# Start with debug logging
-npx @browseragentprotocol/server-playwright --debug
-```
+| Selector | Example | Priority |
+|----------|---------|----------|
+| `role::""` | `role:button:"Submit"` | Best — ARIA role + accessible name |
+| `text:""` | `text:"Sign in"` | Visible text content |
+| `label:""` | `label:"Email"` | Form label association |
+| `placeholder:""` | `placeholder:"Search..."` | Input placeholder |
+| `testid:""` | `testid:"submit-btn"` | data-testid attribute |
+| `e` / `@ref` | `e15`, `@e1` | From snapshot/observe (positional) |
+| `css:` | `css:.btn-primary` | Last resort — fragile |
## Development
```bash
-# Clone the repository
git clone https://github.com/browseragentprotocol/bap.git
cd bap
-
-# Install dependencies
pnpm install
-
-# Build all packages
pnpm build
-
-# Run type checking
pnpm typecheck
-
-# Run linting
pnpm lint
-
-# Install Python SDK in development mode
-cd packages/python-sdk && pip install -e .
+pnpm test
```
## Contributing
@@ -361,7 +350,7 @@ We welcome contributions! Please open an issue or submit a pull request on GitHu
## License
-This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
+Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
## Links
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..218c66a
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,79 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| 0.2.x | Yes |
+| < 0.2 | No |
+
+## Reporting a Vulnerability
+
+If you discover a security vulnerability in BAP, please report it responsibly.
+
+**Do not open a public GitHub issue for security vulnerabilities.**
+
+Instead, please send a report to the maintainers via one of these channels:
+
+1. **GitHub Security Advisories**: Use the [private vulnerability reporting](https://github.com/browseragentprotocol/bap/security/advisories/new) feature on GitHub
+2. **Email**: Send details to the repository maintainers listed in the GitHub organization
+
+### What to include
+
+- Description of the vulnerability
+- Steps to reproduce
+- Affected versions and packages
+- Potential impact
+- Suggested fix (if any)
+
+### Response timeline
+
+- **Acknowledgment**: Within 48 hours
+- **Initial assessment**: Within 1 week
+- **Fix or mitigation**: Depends on severity, targeting:
+ - Critical: 48 hours
+ - High: 1 week
+ - Medium: 2 weeks
+ - Low: Next release cycle
+
+## Security Considerations
+
+BAP controls web browsers on behalf of AI agents. Operators and users should be aware of these security boundaries:
+
+### Authentication
+
+- The `--token` flag enables token-based authentication for WebSocket connections
+- **Always use authentication in production** — without it, any process on the network can control the browser
+- Tokens are compared using constant-time equality to prevent timing attacks
+
+### Network exposure
+
+- By default, the server binds to `localhost` only
+- Do not expose BAP servers to the public internet without authentication and TLS
+- Use `--host` with caution; binding to `0.0.0.0` exposes the server to all network interfaces
+
+### Domain restrictions
+
+- The `--allowed-domains` flag restricts which domains the browser can navigate to
+- Use this in production to prevent navigation to unintended sites
+
+### Browser sandbox
+
+- BAP inherits Playwright's browser sandbox settings
+- Chromium runs with sandbox enabled by default
+- Do not disable the browser sandbox in production
+
+### Data handling
+
+- Screenshots and page content may contain sensitive data
+- Storage state export (`getStorageState`) includes cookies and local storage
+- Treat all browser data as potentially sensitive
+
+## Dependencies
+
+BAP depends on:
+- **Playwright** for browser control
+- **ws** for WebSocket transport
+- **Zod** for schema validation
+
+We monitor dependencies for known vulnerabilities and update promptly.
diff --git a/SKILL.md b/SKILL.md
new file mode 100644
index 0000000..8577ed5
--- /dev/null
+++ b/SKILL.md
@@ -0,0 +1,198 @@
+---
+name: bap-browser
+description: "Browser automation CLI with composite actions and semantic selectors. Use when the user needs to visit websites, fill forms, extract data, take screenshots, or automate multi-step browser workflows like login, checkout, or search."
+license: Apache-2.0
+---
+
+# BAP Browser CLI
+
+AI-first browser automation. Like playwright-cli but with composite actions,
+semantic selectors, and structured extraction.
+
+## Quick Start
+
+```bash
+bap open https://example.com
+bap observe # compact interactive elements
+bap click role:button:"Get Started" # semantic selector
+bap close
+```
+
+## Composite Actions
+
+Execute multiple browser steps in ONE command instead of one-at-a-time:
+
+```bash
+# Login flow — ONE command instead of 3+ separate calls
+bap act fill:role:textbox:"Email"="user@example.com" \
+ fill:role:textbox:"Password"="secret" \
+ click:role:button:"Sign in"
+```
+
+Each step uses the syntax `action:selector=value` or `action:selector`.
+
+## Fused Operations
+
+Fused operations combine multiple server calls into one, cutting roundtrips by 50-85%.
+
+```bash
+# Navigate + observe in 1 call (instead of bap goto + bap observe)
+bap goto https://example.com --observe
+
+# Act + post-observe in 1 call (get updated page state after actions)
+bap act click:role:button:"Submit" --observe
+
+# Control response size with --tier
+bap goto https://example.com --observe --tier=minimal # refs + names only
+bap goto https://example.com --observe --tier=interactive # elements + roles (default)
+bap observe --tier=full # everything + metadata
+```
+
+**Always prefer fused calls** — `bap goto --observe` is 1 roundtrip vs 2 for `bap goto` then `bap observe`.
+
+## Common Patterns
+
+```bash
+# Accept cookies + navigate
+bap act click:text:"Accept" goto:https://example.com/app
+
+# Fill and submit a search
+bap act fill:role:searchbox:"Search"="query here" press:Enter
+
+# Checkout form
+bap act fill:label:"Card number"="4111111111111111" \
+ fill:label:"Expiry"="12/28" \
+ fill:label:"CVV"="123" \
+ click:role:button:"Pay now"
+
+# Login with fused observe (2 calls total)
+bap goto https://app.example.com/login --observe
+bap act fill:label:"Email"="user@example.com" \
+ fill:label:"Password"="secret" \
+ click:role:button:"Sign in" --observe
+```
+
+## Selectors
+
+BAP supports both positional refs (from snapshots) and semantic selectors:
+
+| Selector | Example | When to use |
+|----------|---------|-------------|
+| `e` | `e15` | From snapshot refs (playwright-cli compatible) |
+| `role::""` | `role:button:"Submit"` | When you know the element's purpose |
+| `text:""` | `text:"Sign in"` | By visible text |
+| `label:""` | `label:"Email"` | Form fields by label |
+| `placeholder:""` | `placeholder:"Search..."` | By placeholder |
+| `testid:""` | `testid:"submit-btn"` | By data-testid |
+
+Prefer semantic selectors (`role:`, `label:`, `text:`) — they survive page layout changes. Use `e` refs from `bap observe` or `bap snapshot` when semantic selectors are unclear.
+
+For the full selector reference, see [references/SELECTORS.md](references/SELECTORS.md).
+
+## Commands
+
+### Navigation
+
+```bash
+bap open [url] # Open browser
+bap goto # Navigate
+bap goto --observe # Fused navigate+observe (1 call instead of 2)
+bap goto --observe --tier=interactive # Fused with response tier
+bap back / bap forward # History navigation
+bap reload # Reload page
+```
+
+### Interaction
+
+```bash
+bap click # Click element
+bap fill # Fill input field (clears first)
+bap type # Type into focused element (keystroke-by-keystroke)
+bap press # Press keyboard key (Enter, Tab, Escape, etc.)
+bap select # Select dropdown option
+bap check # Check checkbox
+bap uncheck # Uncheck checkbox
+bap hover # Hover over element
+```
+
+### Observation
+
+```bash
+bap observe # Compact interactive elements (default max 50)
+bap observe --full # Full accessibility tree
+bap observe --forms # Form fields only
+bap observe --max=20 # Limit number of elements returned
+bap observe --tier=interactive # Response tier: full, interactive, minimal
+bap snapshot # Full YAML snapshot (playwright-cli compatible)
+bap screenshot [--file=F] # Save screenshot to .bap/ directory
+```
+
+### Structured Extraction
+
+```bash
+bap extract --fields="title,price" # Quick field extraction
+bap extract --schema=schema.json # JSON Schema-based extraction
+bap extract --list="product" # Extract list of items
+```
+
+### Sessions and Tabs
+
+```bash
+bap -s= # Run command in named session
+bap sessions # List active sessions
+bap tabs # List open tabs
+bap tab-new [url] # Open new tab
+bap tab-select # Switch to tab
+```
+
+### Recipes
+
+```bash
+bap recipe login --user= --pass=