Skip to main content
Glama
README.md
# webbridge

Two surfaces over free web-chat providers, plus a metered path for a paid
account.

- **`/v1/messages`**: Anthropic Messages API, for a harness that points
  `ANTHROPIC_BASE_URL` at a local port.
- **`webbridge mcp`**: an MCP server on stdio, for text tasks: ask, rewrite,
  summarize, translate, classify.

Both sit on the same drivers, so a provider added once works on both.

## Status

Working: the wire contract, the Codex driver, the generic browser driver, the
g4f driver, and the pacing and budget controls. 53 tests pass.

Measured 2026-10-24: five providers answer anonymously, which are ChatGPT,
Gemini, GLM, Cloudflare and Yqcloud. Two consecutive turns return real text. Tool calls are
reliable only on the Codex driver, because the free providers decline the tool
framing on some calls.

## Day to day

Free path, the common one: single-shot text work through MCP or through a
script that calls `/v1/messages` with no tools. That path returns text every
time. It cannot carry an agent loop, because ChatGPT's web UI refuses tool
framing on some calls and the anonymous account has a message limit.

Paid path: the Codex driver speaks a real tool API, so it can carry Claude Code
on a metered account.

## Layout

```
webbridge/
  config.py        settings, pacing, daily budget, kill switch
  anthropic.py     Anthropic request/response/SSE types
  translate.py     Anthropic <-> neutral turns, tool-call protocol
  mcp_server.py    MCP text tools over the same drivers
  drivers/
    base.py        Driver protocol
    codex.py       sanctioned OAuth -> Codex Responses API
    browser.py     generic DOM driver, one YAML profile per site
    g4f_client.py  anonymous providers through the g4f library
  sites/           one YAML profile per web chat provider
  server.py        FastAPI app
docs/
  design.md        intent, goals, anti-goals, wire contract
  live-probes.md   measured per-site results for the browser driver
  g4f-driver.md    measured providers, limits, timeouts
tests/             35 tests
tools/             probe and end-to-end scripts
```

## Run against a free provider (MCP)

```
webbridge providers                       # what answers anonymously
webbridge mcp --site Gemini               # stdio server for an MCP client
```

A client config looks like this:

```json
{
  "mcpServers": {
    "webbridge": {
      "command": "/path/to/venv/bin/python",
      "args": ["-m", "webbridge.cli", "mcp", "--site", "Gemini"],
      "env": { "PYTHONPATH": "/path/to/webbridge" }
    }
  }
}
```

The tools are `ask`, `rewrite`, `summarize`, `translate` and `classify`.

## Run against a free provider (HTTP)

```
webbridge serve --driver g4f --site ChatGPT --port 8787
```

Then point a script at it:

```
ANTHROPIC_BASE_URL=http://127.0.0.1:8787 \
ANTHROPIC_API_KEY=webbridge-local \
  your-script.sh
```

## Run against a primary account (sanctioned)

Log in with the Codex CLI once. The driver reuses `~/.codex/auth.json` and
refreshes it in place.

```
codex login
webbridge serve --driver codex --port 8787
```

Point the real Claude Code binary at it. The `claude` on PATH is a launcher that rewrites `ANTHROPIC_BASE_URL`, so exec
the versioned binary.

```
ANTHROPIC_BASE_URL=http://127.0.0.1:8787 \
ANTHROPIC_API_KEY=webbridge-local \
  "$(ls -d ~/.local/share/claude/versions/* | tail -1)"
```

## Run against a web chat UI (browser)

```
webbridge sites
webbridge serve --driver browser --site zai --headless
webbridge login --site deepseek          # saved login, headed
```

A profile is one YAML file:

```yaml
name: mysite
url: https://example.com/chat
composer: "textarea"
response: "[class*='markdown']"
submit_key: Enter
requires_login: false
headless_ok: false
gates:
  - "button:has-text('Continue')"
```

The login is saved under `~/.webbridge/profiles/<site>/saved-login` and copied
into each run, so two servers never fight over one Chromium profile.

## Safety

The pacer runs before any upstream call: one concurrent request, 20 per minute,
a 400-request daily budget, a one-second minimum gap, and up to 750 ms of
jitter. An exhausted budget returns `429 rate_limit_error` and never reaches the
network. Creating `~/.webbridge/KILL` returns `503`.

Run one bridge at a time. g4f adopts any running CDP browser it finds,
including one it did not start.

## Tests

```
python -m pytest tests -q
```

Live probes need network and are not part of the suite:

```
python tools/two_turn.py --driver g4f --provider ChatGPT
```