Skip to main content
Glama
README.md
# Open ChatGPT

Use **your own signed-in ChatGPT account** (chatgpt.com) from AI agents, scripts and the terminal — as an
**MCP server**, an **OpenAI-compatible API**, a local **REST API** and a **CLI**.

You sign in once in a normal Chrome window (like linking WhatsApp Web), and Open ChatGPT drives that tab:
it types your message, attaches files, switches thinking or web search on, waits for the answer and reads it
back. It never asks for your password and never fakes ChatGPT's anti-bot checks — the real page does all of that.

```
your agent ──MCP / OpenAI API / REST / CLI──▶ open-chatgpt (127.0.0.1:2790) ──▶ your Chrome tab on chatgpt.com
```

> **AI agents:** read [`skills/open-chatgpt/SKILL.md`](skills/open-chatgpt/SKILL.md) — it has setup, every tool and the limits.

## What it can do

| | CLI | MCP tool |
|---|---|---|
| Ask, get the full Markdown answer | `ask "…"` | `chatgpt_ask` |
| Continue a chat | `ask -c <conversation_id> "…"` | `chatgpt_ask` + `conversation_id` |
| Temporary chat (not saved) | `ask --temporary "…"` | `temporary: true` |
| Attach photos, videos, files | `ask -f a.jpg -f b.pdf "…"` | `files: [paths]` |
| Thinking effort | `--effort instant\|medium\|high\|extra_high\|pro`, `--think`, `--no-think` | `think` |
| Web search | `--web` | `web_search: true` |
| Create images (saved as PNG) | `ask --image "…"` | `create_image: true` |
| Any other + menu tool | `--tool "Deep research"` | `tool` |
| **Transcribe a recording (any length, seconds)** | `transcribe meeting.mp3` | `chatgpt_transcribe` |
| Send a recording as the message | `ask --audio voice.m4a "…"` | `audio_file` |
| Rate a reply 👍/👎 | `rate good\|bad` | `chatgpt_rate` |
| Regenerate | `regenerate --mode try_again\|thinking\|web_search` | `chatgpt_regenerate` |
| Read aloud → audio file | `read-aloud -o reply.mp3 --format mp3` | `chatgpt_read_aloud` |
| **Live progress** (step + seconds, errors with the failing step) | printed on stderr; `progress` | MCP progress notifications; `chatgpt_progress` |
| Long tasks (Deep research…) | `--timeout <sec>` | `timeout_seconds` |
| History, pinned, archived | `list [--pinned] [--archived]` | `chatgpt_list_conversations` |
| Every message of a chat, in order | `show <id>` | `chatgpt_get_conversation` |
| Search chats | `search "…"` | `chatgpt_search` |
| Pin / unpin / archive / unarchive / rename | `pin <id>` … `rename <id> "title"` | `chatgpt_manage_conversation` |
| Saved memories: list, delete | `memories`, `forget <id>` | `chatgpt_list_memories`, `chatgpt_delete_memory` |
| Profile: account, plan, custom instructions, memory use | `profile` | `chatgpt_profile` |
| Projects; new chat inside one | `projects`, `ask --project <id>` | `chatgpt_list_projects`, `project_id` |
| Signed-in account | `status` | `chatgpt_status` |

What each plan allows (Free / Go / Plus / Pro) and the known limits: [docs/plans-and-limits.md](docs/plans-and-limits.md).
How it was reverse-engineered: [docs/how-it-works.md](docs/how-it-works.md).

## Before you use it

- This automates the ChatGPT website, which OpenAI's terms do not officially allow. Use it on your own account,
  for personal use, at a human pace, and accept that the account could be limited. For production use the
  official OpenAI API.
- One Chrome tab answers one request at a time; requests queue.
- When chatgpt.com changes its page, selectors in `src/chatgpt.js` (`SEL`) may need updating.

## Setup

Needs Node.js 20+ and Google Chrome (or Chromium; set `CHROME_PATH` if it isn't in the usual place).

```bash
git clone https://github.com/mkshaonexe/open-chatgpt.git
cd open-chatgpt
npm install
npm link        # optional: puts `open-chatgpt` (alias `chatgpt-bridge`) on your PATH
```

**1. Sign in once** — a Chrome window opens on chatgpt.com; sign in the normal way (Google, email…):

```bash
open-chatgpt login
```

The session is kept in `~/.chatgpt-bridge/chrome-profile`, a separate Chrome profile used only by Open ChatGPT.

**2. Start the server** and keep its Chrome window open (minimise it if you like):

```bash
open-chatgpt serve             # visible window: most reliable
open-chatgpt serve --headless  # no window; ChatGPT's bot checks may block this
```

It prints the URL and your local API key (`open-chatgpt key` prints it again).

## CLI

```bash
open-chatgpt ask "Explain CAP theorem in 3 bullets"
open-chatgpt ask -c <conversation_id> "Now give an example"
open-chatgpt ask --temporary --effort medium "Plan a 3-day trip to Sylhet"
open-chatgpt ask -f photo.jpg -f report.pdf "What's in these?"
open-chatgpt ask --web "Latest Node.js LTS?"
open-chatgpt ask --image "A flat icon of a green leaf"          # prints the saved PNG path

open-chatgpt transcribe meeting.mp3 > meeting.txt               # 5 min ≈ 8 s, 12 min ≈ 20 s
open-chatgpt ask --audio voice.m4a "Answer in Bangla."           # transcript + your text, sent as one message

open-chatgpt rate good                                          # acts on the last reply of the open chat
open-chatgpt regenerate --mode thinking
open-chatgpt read-aloud -o reply.mp3 --format mp3 --voice juniper
open-chatgpt ask --tool "Deep research" --timeout 3600 "…"     # long task, progress lines on stderr

open-chatgpt list --limit 10            # also --pinned, --archived
open-chatgpt show <conversation_id>     # every message, numbered, in order
open-chatgpt search "invoice"
open-chatgpt pin <id>                   # unpin, archive, unarchive
open-chatgpt rename <id> "New title"
open-chatgpt memories                   # forget <memory_id> deletes one
open-chatgpt profile
open-chatgpt projects                   # ask --project <id> "…" starts a chat inside one
open-chatgpt progress                   # what the running request is doing
open-chatgpt status
```

While a request runs, the CLI prints its steps on stderr (`--quiet` hides them):

```
… opening chat (0s)
… turning on Create image (1s)
… creating image (3s)
… saving 1 image (52s)
```

Replies print `conversation_id` and `message_id` on stderr; `--json` prints the whole result.
`rate`, `regenerate` and `read-aloud` take `-c <conversation_id>` and `--message <id>`; without them they act on
the last reply of the chat the bridge has open.

## MCP

Streamable HTTP (with `serve` running):

```bash
claude mcp add --transport http chatgpt http://127.0.0.1:2790/mcp \
  --header "Authorization: Bearer $(open-chatgpt key)"
```

stdio, for clients that only speak stdio (forwards to the running server):

```json
{ "mcpServers": { "chatgpt": { "command": "node", "args": ["/path/to/open-chatgpt/bin/cli.js", "mcp"] } } }
```

(after `npm link` you can use `"command": "open-chatgpt", "args": ["mcp"]`)

| Tool | Arguments |
|---|---|
| `chatgpt_ask` | `prompt`, `conversation_id`, `project_id`, `model`, `temporary`, `files`, `think`, `web_search`, `create_image`, `tool`, `audio_file`, `timeout_seconds` |
| `chatgpt_progress` | — (state, step, elapsedSeconds, error of the running/last request) |
| `chatgpt_transcribe` | `audio_file` |
| `chatgpt_rate` | `rating` (`good`/`bad`), `conversation_id`, `message_id` |
| `chatgpt_regenerate` | `mode` (`try_again`/`thinking`/`web_search`), `conversation_id`, `message_id` |
| `chatgpt_read_aloud` | `voice`, `format` (`aac`/`mp3`/`opus`), `out_file`, `conversation_id`, `message_id` |
| `chatgpt_list_conversations` | `limit`, `offset`, `pinned`, `archived` |
| `chatgpt_get_conversation` | `conversation_id` |
| `chatgpt_search` | `query`, `limit` |
| `chatgpt_manage_conversation` | `conversation_id`, `action` (`pin`/`unpin`/`archive`/`unarchive`/`rename`), `title` |
| `chatgpt_list_memories` | — |
| `chatgpt_delete_memory` | `memory_id` |
| `chatgpt_profile` | — |
| `chatgpt_list_projects` | — |
| `chatgpt_status` | — |

Long tools (`ask`, `transcribe`, `regenerate`, `read_aloud`) send **MCP progress notifications** when the client
passes a `progressToken`, e.g. `creating image (41s)`. Failures come back as error results that name the step,
e.g. `Failed while setting thinking effort: thinking level "pro" is not offered on this account (available: instant, medium, high)`.

File arguments are absolute paths on the machine running the server (it only listens on 127.0.0.1).

## OpenAI-compatible API

```python
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:2790/v1", api_key="<open-chatgpt key>")
r = client.chat.completions.create(model="chatgpt", messages=[{"role": "user", "content": "Hello"}])
print(r.choices[0].message.content)
```

- `model`: `chatgpt` / `default` = your account's default; anything else is passed to chatgpt.com as the model slug.
- `stream: true` works. `usage` is always 0 (the website doesn't report tokens).
- Each call starts a new chat; system messages and earlier turns are folded into one prompt. Send
  `"conversation_id"` (or an `x-conversation-id` header) to keep using one chat; then only the newest user
  message is sent. Every response includes `conversation_id`. Extra body fields: `temporary`.

## REST

Every route except `/health` needs `Authorization: Bearer <key>`. File fields are absolute local paths.

| Method | Path | Body / query → result |
|---|---|---|
| `POST` | `/api/ask` | `{prompt, conversationId?, projectId?, model?, temporary?, files?, think?, webSearch?, image?, tool?, audioFile?, timeoutSeconds?}` → `{text, conversationId, messageId, model, temporary, images[], transcript, elapsedSeconds, url}` |
| `GET` | `/api/progress` | `{action, state, step, elapsedSeconds, error?, failedAt?}` |
| `POST` | `/api/transcribe` | `{audioFile}` → `{text, seconds, chunks}` |
| `POST` | `/api/rate` | `{rating, conversationId?, messageId?}` |
| `POST` | `/api/regenerate` | `{mode?, conversationId?, messageId?}` → like `/api/ask` |
| `POST` | `/api/read-aloud` | `{conversationId?, messageId?, voice?, format?, outFile?}` → `{path, mimeType, bytes, …}` |
| `GET` | `/api/status` | signed-in account and plan |
| `GET` | `/api/conversations` | `?limit=20&offset=0&pinned=true&archived=true` |
| `GET` | `/api/conversations/:id` | title, flags and every message in order |
| `POST` | `/api/conversations/:id/pin` | also `unpin`, `archive`, `unarchive`, `rename` (`{title}`) |
| `GET` | `/api/search` | `?q=…&limit=10` |
| `GET` | `/api/memories` | saved memories; `DELETE /api/memories/:id` deletes one |
| `GET` | `/api/profile` | account, plan, custom instructions, memory usage |
| `GET` | `/api/projects` | projects and their latest chats |
| `POST` | `/v1/chat/completions` | OpenAI format |
| `GET` | `/v1/models` | |
| `POST` | `/mcp` | MCP streamable HTTP (stateless) |

## Files and settings

Everything lives in `~/.chatgpt-bridge/` (override with `CHATGPT_BRIDGE_HOME`):

| Path | |
|---|---|
| `chrome-profile/` | the signed-in Chrome profile |
| `config.json` | API key and settings (below) |
| `images/<conversation_id>/` | generated images |
| `audio/` | read-aloud files saved without `-o` |

| `config.json` key | Default | |
|---|---|---|
| `port` | `2790` | API port (env `CHATGPT_BRIDGE_PORT`) |
| `cdpPort` | `9339` | Chrome debugging port, bound to 127.0.0.1 |
| `chromePath` | per OS | env `CHROME_PATH` |
| `temporaryByDefault` | `false` | `true` keeps bridge chats out of your history |
| `answerTimeoutMs` | `300000` | how long to wait for one answer |

## License

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues