# ChatGPT Web2API

> OpenAI-compatible API proxy for ChatGPT web — expose ChatGPT as an MCP server, REST API, and OpenAI SDK endpoint via CDP-driven browser automation

- **Type:** MCP server
- **Install:** `agentstack add mcp-octo-lex-chatgpt-web2api`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Octo-Lex](https://agentstack.voostack.com/s/octo-lex)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Octo-Lex](https://github.com/Octo-Lex)
- **Source:** https://github.com/Octo-Lex/ChatGPT-Web2API

## Install

```sh
agentstack add mcp-octo-lex-chatgpt-web2api
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# ChatGPT-Web2API

**Turn ChatGPT into an API. No API key. No token extraction. No sentinel solving.**

One command starts a Chrome browser, logs into ChatGPT, and exposes an OpenAI-compatible API + MCP server.

[](https://github.com/Elephant-Rock-Lab/ChatGPT-Web2API/actions/workflows/ci.yml)
[](https://github.com/Elephant-Rock-Lab/ChatGPT-Web2API/releases)
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://github.com/Elephant-Rock-Lab/ChatGPT-Web2API/actions)

---

## Why This Exists

You have a **ChatGPT Plus subscription** but want to use it programmatically:

- **Build AI agents** that chat with ChatGPT via MCP (Model Context Protocol)
- **Use the OpenAI Python SDK** against your ChatGPT account — no separate API key
- **Access ChatGPT Projects** for persistent memory and custom instructions
- **Manage memories, conversations, and projects** from code

Other reverse proxies require token extraction, sentinel challenge solving, or cookie management. This one **drives a real Chrome browser** — anti-bot challenges (Turnstile, PoW) are handled automatically.

## Demo

```bash
$ pip install chatgpt-web2api
$ chatgpt-web2api
✓ Chrome launched on port 9222
✓ Navigating to chatgpt.com...
✓ Ready on http://localhost:8080

$ curl -s http://localhost:8080/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{"model":"auto","messages":[{"role":"user","content":"What is 8+7?"}]}'
{"choices":[{"message":{"content":"8 + 7 = 15"},"finish_reason":"stop"}]}
```

```python
# OpenAI Python SDK — drop-in replacement
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")
print(client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "What is 8+7?"}]
).choices[0].message.content)
# → "8 + 7 = 15"
```

## How It Works

```
┌──────────────┐   OpenAI API   ┌──────────────┐   CDP    ┌──────────────┐
│  Your code   │ ──────────────► │  API Server  │ ────────► │    Chrome     │
│  SDK / curl  │ ◄────────────── │  or MCP      │ ◄──────── │  chatgpt.com  │
│  MCP client  │   JSON / SSE    │  Server      │  events   │  (logged in)  │
└──────────────┘                 └──────────────┘           └──────────────┘
```

The proxy types messages, clicks send, and reads responses — exactly like a human. The browser handles all anti-bot challenges transparently.

## Quick Start

```bash
pip install chatgpt-web2api

# Start — opens Chrome, waits for login on first run
chatgpt-web2api

# Then use it:
curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"auto","messages":[{"role":"user","content":"What is 2+2?"}]}'
# → {"choices":[{"message":{"content":"4"}}]}
```

That's it. **One install, one command, one endpoint.**

## What You Get

### 🌐 OpenAI-Compatible REST API

Drop-in replacement for `api.openai.com`:

```python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")

response = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
)
for chunk in response:
    print(chunk.choices[0].delta.content or "", end="")
```

### ⏱️ Rate Limits & Agent Retry

ChatGPT occasionally shows a "Too many requests" pop-up when conversations are
accessed rapidly. ChatGPT-Web2API turns this from a silent failure into a
**practical, agent-friendly signal** — so autonomous workflows back off and
recover instead of breaking.

**Transparent by default.** When the pop-up appears, the server dismisses it
(clicks "Got it") and retries your request with exponential backoff — up to
3 attempts. A *transient* limit is invisible: your agent gets a normal
successful response and never knows it happened.

**A clean pause signal when it persists.** If the limit survives all retries,
the server surfaces a **standard OpenAI `429`** that every major agent framework
already knows how to handle — with **zero client-side integration**:

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{"error": {"type": "rate_limit_exceeded", "code": "rate_limit_exceeded", ...}}
```

The OpenAI SDK, LangChain, LlamaIndex, and similar frameworks auto-retry on
`429` + `Retry-After` by default. The wait (`Retry-After`) is parsed from the
pop-up text when ChatGPT gives an exact number, otherwise a conservative 60s.

**MCP clients** (Claude Desktop, Cursor) get the same signal semantically: a
`CallToolResult` with `isError=True` and a machine-readable `structuredContent`
payload an agent can read to decide "pause, then retry this tool":

```json
{"rate_limited": true, "retry_after": 60, "error": "rate_limit_exceeded"}
```

> **Streaming caveat:** a 429 can be sent *before* streaming begins (a pre-flight
> check), but a limit that appears *mid-stream* (after the 200 is committed) is
> surfaced as an inline `[Error: rate_limit_exceeded — retry in Ns]` chunk,
> since the HTTP status can no longer change. Non-streaming requests are
> unaffected.

### 🩺 Troubleshooting API/UI Drift

ChatGPT changes its web API over time, which can silently break driver functions
(the unit-test mocks can't detect this). ChatGPT-Web2API ships a **reactive
diagnostic** that captures evidence at the moment a function breaks, plus a
`doctor` command that auto-discovers the breakage and prints the evidence for
fast repair.

**Enable capture** (off by default) so breakage in the wild is recorded:

```bash
W2A_DIAGNOSE=1 chatgpt-web2api start
```

When a function returns a broken shape, a redacted artifact is written to
`~/.chatgpt-web2api/diagnostics/--.json` with the
exact request, live response, expected-vs-actual mismatch (secrets stripped).

**Auto-discover + diagnose:**

```bash
chatgpt-web2api doctor               # which functions have captured breakage?
chatgpt-web2api doctor create_project# print the captured evidence for one
chatgpt-web2api doctor --verify get_models   # re-run a read function live to test a fix
```

`doctor` (no args) **auto-discovers** which functions are broken — no human
needs to name them. It then prints the evidence an AI coding agent (or you)
reads to propose the corrected payload/selector/parse. `--verify` confirms a
read-path fix against the live account before you commit it. No AI is bundled —
the project captures deterministic evidence; the fix is human-applied.

### 🤖 MCP Server (16 Tools for AI Agents)

Expose ChatGPT to Claude Desktop, Cursor, Craft Agent, or any MCP client. Two
transports are supported — **SSE is recommended** for persistent clients like
ZCode; stdio is kept as a compatibility/dev mode.

#### SSE transport (persistent server; single active session until B1)

SSE is useful for one long-lived MCP server, but **current master does not yet
provide per-client tab/session isolation inside one SSE process**. Multiple MCP
clients may attach to the same SSE endpoint, but they share one MCP server, one
`CDPDriver`, one Chrome tab, and one browser conversation surface. Running
parallel agent sessions through the same SSE server can cause cross-session
interference: one session may navigate, continue, or mutate the browser state
expected by another.

Until the B1 MCP session-affine tab pool is implemented and enabled, use SSE
for a **single active agent session at a time**. For parallel agent sessions
today, use separate MCP processes with `parallel_tabs=true` and
`tab_mode=owned`, and ensure the Chrome-owning `chatgpt-web2api` REST process
is started with the same parallel config or is not serving REST traffic on
that CDP port. Otherwise, wait for B1.

Start it (in a dedicated terminal — Chrome must already be running with an
authenticated session, i.e. `chatgpt-web2api` started first):

```bash
chatgpt-web2api-mcp --transport sse --port 8090
```

Then add the client config:

```json
{
  "mcpServers": {
    "chatgpt-web2api-sse": {
      "type": "sse",
      "url": "http://localhost:8090/sse"
    }
  }
}
```

Every client session that attaches to that URL currently shares the single MCP
server and Chrome connection. This avoids per-session process growth, but it is
**not safe for parallel independent agent sessions** until B1's session-affine
driver/tab pool is available and explicitly enabled.

> **Multi-session warning:** A2 prevents stale-return by anchoring responses to
> the submitted turn, but it does not isolate multiple MCP SSE clients from each
> other. Shared SSE remains a single browser-control surface today.

#### Alternative: stdio transport (per-session, dev/debug)

Stdio spawns one MCP child process per client session, so it is not
recommended for many concurrent ZCode sessions. It is useful for single-client
setups, local development, and debugging (logs go straight to stderr):

```json
{
  "mcpServers": {
    "chatgpt": {
      "command": "chatgpt-web2api-mcp",
      "args": ["--cdp-port", "9222"]
    }
  }
}
```

**16 tools** give the agent full control:

| Tool | What it does |
|------|-------------|
| `chat_completion` | Send message, get response (multi-turn, streaming) |
| `list_models` | 17 live model slugs (GPT-5.5, 5.4 Thinking, etc.) |
| `list_projects` | ChatGPT projects with persistent memory |
| `create_project` | Create isolated workspace with dedicated memory |
| `delete_project` | Permanently remove a project (gated: `W2A_ENABLE_DESTRUCTIVE`) |
| `list_conversations` | Recent chats with pagination |
| `get_conversation` | Full message history |
| `archive_conversation` | Archive/unarchive (reversible) |
| `delete_conversation` | Delete permanently |
| `list_memories` | Facts ChatGPT remembers (41 found in testing) |
| `create_memory` | Tell ChatGPT to remember something |
| `delete_memory` | Remove a memory |
| `list_gpts` | Discover Custom GPTs |
| `chat_with_gpt` | Chat with a specific Custom GPT |
| `update_project_instructions` | Change project system prompt |
| `list_project_files` | Files in a project's knowledge base |

Every tool includes rich descriptions with domain knowledge, Pydantic-validated input, structured output schemas, and proper `ToolAnnotations` — agents understand *how* and *when* to use each one without prompting.

### 🔒 MCP Tool Access Control

The MCP server has **no authentication of its own** — any client that can reach it can call its tools. Since several tools mutate or delete data on your *real* ChatGPT account, tools are gated by risk tier so the dangerous ones are never exposed by accident:

| Tier | Tools | Exposed when |
|------|-------|--------------|
| **Safe** (always visible) | `chat_completion`, `chat_with_gpt`, `list_models`, `list_projects`, `list_conversations`, `get_conversation`, `list_memories`, `list_gpts`, `list_project_files` | Out of the box |
| **Write** (account mutation) | `create_project`, `update_project_instructions`, `create_memory`, `archive_conversation` | `W2A_ENABLE_WRITE=1` |
| **Destructive** (irreversible) | `delete_conversation`, `delete_memory` | `W2A_ENABLE_DESTRUCTIVE=1` |

```bash
# Default: safe reads + core chat only — nothing can mutate your account
chatgpt-web2api-mcp

# Enable project/memory/archive creation
W2A_ENABLE_WRITE=1 chatgpt-web2api-mcp

# Full surface (also exposes deletes)
W2A_ENABLE_WRITE=1 W2A_ENABLE_DESTRUCTIVE=1 chatgpt-web2api-mcp
```

Gated tools are **hidden** from `list_tools` *and* **refused at call time** — a client that calls a gated tool by name gets an error result rather than silent execution. Binding the SSE transport to a non-loopback address with no `api_keys` configured logs a prominent warning.

### 🔌 ZCode Hook: One-Line Bootstrap with `ensure`

Instead of manually starting REST and SSE in separate terminals, run a single
command that reconciles both and exits — ideal for a ZCode session-start hook:

```bash
chatgpt-web2api ensure
```

`ensure` is **point-in-time reconciliation**, not a supervisor: it checks REST
(`/health`) and SSE, starts whichever is missing, verifies SSE with a real MCP
handshake, and exits `0` when both are ready (nonzero with a diagnostic if not).
If a service dies later, re-run `ensure` from the next hook/session.

```bash
# Reconcile REST on 8080 + SSE on 8090 (defaults)
chatgpt-web2api ensure

# Custom ports / config (propagated to the child processes)
chatgpt-web2api ensure --rest-port 8081 --mcp-sse-port 8091 --config ~/.my-config.json
```

Degraded-REST policy: `ensure` does **not** restart REST immediately on
`degraded` (Chrome alive, driver disconnected) — it polls for up to 20s first,
since that may be a transient CDP reconnect. Only `missing` or `broken` trigger
an immediate (re)start. This avoids a destructive Chrome bounce on transients.

### 🔧 Three Interfaces, One Chrome Session

```bash
# Terminal 1: Start Chrome + API
chatgpt-web2api

# Terminal 2: MCP server (SSE recommended for ZCode; stdio also supported)
chatgpt-web2api-mcp --transport sse --port 8090
# or: chatgpt-web2api-mcp             # stdio (one MCP child per client)

# Terminal 3: Any OpenAI SDK, curl, or HTTP client
curl http://localhost:8080/v1/chat/completions ...
```

## What Makes This Different

| | ChatGPT-Web2API | chat2api | ChatGPTReversed | Official API |
|---|---|---|---|---|
| **Anti-bot handling** | ✅ Automatic (CDP) | ❌ Manual (PoW/Turnstile) | ❌ Manual | N/A |
| **Token extraction** | ❌ None needed | ✅ Required | ✅ Required | N/A |
| **Project memory** | ✅ Full CRUD | ❌ | ❌ | ❌ |
| **MCP server** | ✅ 16 tools | ❌ | ❌ | ❌ |
| **ChatGPT memories** | ✅ List/create/delete | ❌ | ❌ | ❌ |
| **Custom GPTs** | ✅ Chat with any GPT | ❌ | ❌ | ❌ |
| **OpenAI SDK compat** | ✅ Drop-in | ✅ | ❌ | ✅ (native) |
| **Streaming** | ✅ SSE | ✅ | ❌ | ✅ |
| **Multi-turn** | ✅ Auto-continue | ✅ | ❌ | ✅ |
| **Installation** | `pip install` | Docker | npm | `pip install openai` |
| **Cost** | Free (uses your subscription) | Free | Free | Pay-per-token |

**Key insight**: Other proxies fight the anti-bot system. This one *uses the browser as the solution* — Chrome handles Turnstile, PoW, and session management automatically.

## MCP Tools in Action

An AI agent connected via MCP can:

```python
# Create an isolated project with dedicated memory
create_project(name="Python Async Specialist", memory_scope="project_v2")

# Chat within the project (persistent memory across conversations)
chat_completion(message="What are Python coroutines?", project_id="g-p-abc123")

# Manage memories
list_memories()  # → 41 facts ChatGPT remembers
create_memory(content="Always use type hints in Python code")
delete_memory(memory_id="abc-123")

# List and manage conversations
list_conversations(limit=10)
archive_conversation(conversation_id="xyz", archive=True)

# Chat with a Custom GPT
chat_with_gpt(gpt_id="g-hkJGhxxx", message="Analyze this data")
```

## Configuration

### CLI Flags

| Flag | Default | Description |
|------|---------|-------------|
| `--port` | 8080 | API server port |
| `--cdp-port` | 9222 | Chrome debugging port |
| `--headless` | false | Run Chrome headless (may trigger detection) |
| `--log-level` | INFO | DEBUG, INFO, WARNING, ERROR |

### Config File (`config.json`)

```json
{
  "port": 8080,
  "host": "127.0.0.1",
  "cdp_port": 9222,
  "headless": false,
  "default_model": "auto",
  "default_project_id": null,
  "tab_mode": "owned",
  "parallel_tabs": false,
  "api_keys": [],
  "request_timeout": 120
}
```

See [`config.example.json`](config.example.json) for all available keys.

### MCP Session Pool (B1 — experimental, default off)

When `mcp_session_pool_enabled=true`, the MCP SSE server does **not** connect to
Chrome at startup. Instead, each MCP client session gets its own owned
`CDPDriver`/Chrome tab on first request (lazy materialization). Different
sessions get different tabs, capped by pool size. This enables parallel
multi-session use of one shared SSE endpoint.

Requires `parallel_tabs=true` + `tab_mode=owned` (validated at load).

```json
{
  "parallel_tabs": true,
  "tab_mode": "owned",
  "mcp_session_pool_enabled": true,
  "mcp_session_pool_siz

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Octo-Lex](https://github.com/Octo-Lex)
- **Source:** [Octo-Lex/ChatGPT-Web2API](https://github.com/Octo-Lex/ChatGPT-Web2API)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-octo-lex-chatgpt-web2api
- Seller: https://agentstack.voostack.com/s/octo-lex
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
