Skip to main content
Glama
zhiqiang73-cpu

manus-codex-bridge

README.md
# Manus-codex-bridge

> **An independent open-source project. Not affiliated with, endorsed by, or sponsored by Manus or OpenAI.**
> "Manus" is a trademark of its respective owner. "ChatGPT", "Codex", and "OpenAI" are trademarks of OpenAI.
> This is **not** a Manus product. Despite the name, it does **not** use the OpenAI Codex backend — it uses OpenAI's official OAuth and Responses API.

Use the ChatGPT plan you already pay for — **Plus or Pro** — in your own local tools, through OpenAI's **official** sign-in and Responses API. No API key. No per-token billing.

```
Your tool  →  mcb (local)  →  auth.openai.com (OAuth)  →  api.openai.com/v1/responses
                              └─ counts against your ChatGPT plan usage
```

Three ways to use it:

| Surface | For |
| --- | --- |
| **MCP server** (`mcb mcp`) | Manus, Claude Desktop, Cursor, and other MCP clients |
| **OpenAI-compatible HTTP** (`mcb serve`) | Any SDK or tool that accepts a custom `base_url` |
| **CLI** (`mcb ask`) | Shell scripts and quick one-off prompts |

---

## Why this exists

Your ChatGPT subscription includes an allowance that participating apps can use on your behalf. OpenAI documents this as **Sign in with ChatGPT** with optional **ChatGPT plan usage**, and explicitly supports it for **open-source tools and personal projects that run locally**.

Most community bridges in this space put an OpenAI-compatible API in front of the **Codex HTTP backend** (`chatgpt.com/backend-api/codex`) — an undocumented path tied to your `~/.codex/auth.json`, which their own READMEs warn "may change without notice."

**This project deliberately does not do that.** It uses the documented, first-party path:

| | This project | Typical community bridge |
| --- | --- | --- |
| Auth | Official OAuth 2.0 + PKCE, dynamic client registration | Reads a Codex CLI credential file |
| Inference | `POST https://api.openai.com/v1/responses` | `chatgpt.com/backend-api/codex/*` |
| Model list | `GET https://api.openai.com/v1/models` | Bundled / cached catalog |
| Credentials | OS keychain, with a documented fallback | Plain file |
| Billing | ChatGPT plan usage, granted by the user | ChatGPT plan usage, implicitly |

---

## Requirements

- **Node.js 18+** — nothing else. **Zero runtime dependencies**; no `npm install` needed.
- A **ChatGPT Plus or Pro** account.
- macOS, Linux, or Windows.

---

## Install

```bash
git clone https://github.com/zhiqiang73-cpu/Manus-codex-bridge.git
cd manus-codex-bridge
node bin/mcb.js login
```

`login` opens your browser for the official OAuth flow and stores credentials in your OS keychain. Then:

```bash
node bin/mcb.js status
node bin/mcb.js models
node bin/mcb.js ask "Explain what an idempotent HTTP method is" --model <id-from-models>
```

To get a global `mcb` command:

```bash
npm link          # or: ln -s "$PWD/bin/mcb.js" /usr/local/bin/mcb
```

---

## MCP setup

`mcb mcp` speaks MCP over stdio, so any MCP client can launch it on demand — no daemon, no open port.

**Tools exposed**

| Tool | Purpose |
| --- | --- |
| `chatgpt_status` | Connection state, whether plan usage was granted, storage backend |
| `chatgpt_models` | Live model list for the signed-in account |
| `chatgpt_ask` | Run one prompt against a chosen model |

**Example client config**

```json
{
  "mcpServers": {
    "manus-codex-bridge": {
      "command": "node",
      "args": ["/absolute/path/to/manus-codex-bridge/bin/mcb.js", "mcp"]
    }
  }
}
```

---

## OpenAI-compatible HTTP

```bash
mcb serve --port 18888 --api-key <your-local-key>
```

| Endpoint | Notes |
| --- | --- |
| `GET /v1/models` | Live catalog |
| `POST /v1/responses` | Responses API shape; supports `stream: true` |
| `POST /v1/chat/completions` | Chat Completions shape; supports `stream: true` |
| `GET /` | Local console — sign in, browse models, test inference, view evidence |

Point any OpenAI client at it:

```bash
export OPENAI_BASE_URL=http://127.0.0.1:18888/v1
export OPENAI_API_KEY=<your-local-key>
```

> **Always set `--api-key`.** The server binds to `127.0.0.1`, but any local process can reach a loopback port. Without a key, any program on your machine — including a stray dependency — could spend your ChatGPT plan.

---

## Credential storage

Credentials are stored in the OS secret store, with an automatic and documented fallback:

| Platform | Backend |
| --- | --- |
| macOS | Keychain (`security`) |
| Linux | Secret Service (`secret-tool`) |
| Windows | DPAPI, current user (`powershell`) |
| Fallback | Plain `0600` file at `~/.config/manus-codex-bridge/credentials.json` |

The fallback triggers only when the platform store is unavailable — for example a headless container without libsecret, or a restricted execution session. `mcb status` always reports which backend is actually in use, and a fallback emits a warning. **Credentials are never written to logs and never printed.**

---

## What gets stored and logged

| Path | Contents |
| --- | --- |
| OS keychain | `access_token`, `refresh_token`, `id_token` |
| `state/chatgpt-host.json` | This installation's host ID — not sensitive |
| `logs/*.json` | Model catalog, results, outbound request summary — **no tokens** |

---

## How the official flow works

Documented at <https://developers.openai.com/siwc/token-sharing-open-source/sign-in> and <https://developers.openai.com/siwc/token-sharing-open-source/models-and-inference>.

**Authorize**

| Parameter | Value |
| --- | --- |
| Endpoint | `https://auth.openai.com/api/accounts/authorize` |
| `client_id` | `dynamic_agent_client` on first registration; the issued `oaiapp_*` afterwards |
| `agent_name_hint` | Your app name — **first registration only** |
| `ext_agent_host_id` | `urn:uuid:…`, unique per installation |
| `redirect_uri` | `http://127.0.0.1:<port>/auth/callback` — loopback; `localhost` is not accepted |
| `scope` | `openid profile email offline_access resource.invoke chatgpt.tokens.use.direct` |
| `resource` | `https://api.openai.com/v1` |
| Other | `state`, `nonce`, PKCE `S256` |

The callback returns the code **and the issued `client_id`**. Exchange the code at `https://auth.openai.com/api/accounts/oauth/token` with that issued client ID, the PKCE verifier, the same `redirect_uri`, and the same `resource`.

**Infer** — `store: false` and `stream: true` are required. Success means you received `response.completed`, not merely HTTP 200.

---

## Two gotchas worth knowing

1. **The streaming response has no `content-type` header.** `POST /v1/responses` returns 200 with `content-type: null` and only `transfer-encoding: chunked`. Any client that gates on `text/event-stream` will misreport a working request as a failure. Decide from the HTTP status plus the parsed events.
2. **Failures can arrive after the stream opens.** A plan-usage error comes back as `response.failed` inside an HTTP **200** stream. Only `response.completed` means success. This bridge maps such failures to a proper status code (e.g. `429`) for callers rather than passing the misleading 200 through.

---

## Compliance and scope

| Use | Allowed? |
| --- | --- |
| Open source, run locally, free | ✅ Explicitly supported |
| Each user signs in with their own account | ✅ This is the intended design |
| Charging for it, or hosting it as a service for others | ❌ Requires an [OpenAI partner request](https://openai.com/form/sign-in-with-chatgpt-interest/) |
| Copying OpenAI's DevKit code | ❌ That code is under a noncommercial licence |

This project is an **independent implementation written from OpenAI's public documentation**. It does not include or derive from the DevKit source.

---

## Usage limits

Plan usage is a shared allowance, not an unlimited pool:

- You can set a **per-app weekly limit** in ChatGPT → **Settings → Usage**. That limit is a **cap, not a separate pool** — the app can hit its cap while your plan still has usage left.
- `subscription_sharing_usage_limit_exceeded` can mean *either* an app-level cap *or* an overall plan limit. You cannot tell which from the code alone, and you cannot infer a reset time from it.
- Signing in again or retrying does **not** restore usage.
- After included usage runs out, apps can use credits only if the user has opted in **and** the app's limit is set to 100%.

This project surfaces these errors verbatim, with a link to <https://chatgpt.com/settings/usage>, rather than guessing.

---

## Mobile and tablets

OpenAI's flow has one hard constraint: the callback must be an **HTTP loopback address on `127.0.0.1`** — `localhost` is not accepted, and only the port may vary. Custom URL schemes (`myapp://`) are not accepted either. That shapes what is possible on mobile.

| Platform | How | Status |
| --- | --- | --- |
| **Android** | [Termux](https://termux.dev) → `pkg install nodejs` → clone → `node bin/mcb.js login` | Works today, no code changes needed |
| **iPad / iPhone** | Run the bridge on a Mac/PC, use it from the tablet over your LAN | Works today (see below) |
| **iPad / iPhone** | Native Swift app (`NWListener` loopback + Keychain) | Not built — a separate app project |
| **iPad / iPhone** | [iSH](https://ish.app) (Alpine emulation) + Node | Experimental — slow, port binding unreliable |

**Why there is no "just install it" iOS app yet:** Node.js does not run on iOS/iPadOS, and the loopback-only callback rule rules out the usual mobile OAuth pattern. A native app is feasible — an iOS app can serve `127.0.0.1` while the system browser is in the foreground, which is the standard RFC 8252 loopback pattern — but that is a separate Swift project, not a port of this one.

### Using it from a phone or tablet

Run the bridge on a computer and reach it from the tablet over your local network:

```bash
mcb serve --host 0.0.0.0 --api-key <random-string>
```

- `--host 0.0.0.0` binds all interfaces. **An API key is mandatory** — the command refuses to start without one.
- The console is mobile-responsive, so `http://<computer-ip>:18888/` works in a phone browser.
- Point a mobile OpenAI client at `http://<computer-ip>:18888/v1` with the same key.

**Sign-in still happens on the computer.** The OAuth callback must land on the bridge machine's `127.0.0.1`, so the login cannot be completed from the phone. Sign in on the computer first, then use it from the tablet.

> Only do this on a network you trust. Anyone on the same Wi-Fi can reach the port; the API key is what stops them from spending your plan.

### Manus on mobile

The MCP surface needs a local process, so a mobile Manus client cannot launch `mcb mcp`. Use the HTTP surface from mobile instead.

---

## Limitations

- **Single account.** Multi-account / multi-workspace switching is not implemented yet.
- **Text only.** Tools, images, and structured outputs are not passed through yet.
- **No background service.** `serve` runs in the foreground; it does not register a LaunchAgent or systemd unit. MCP clients launch `mcb mcp` on demand instead.
- macOS is the primary test target.

---

## Documentation

| Document | Contents |
| --- | --- |
| [RESULT.md](RESULT.md) | Verification report: the full official chain, with evidence |
| [docs/deployment-and-open-source.md](docs/deployment-and-open-source.md) | Running on other machines; release checklist |
| [docs/product-and-residency-plan.md](docs/product-and-residency-plan.md) | Product positioning, constraints, and roadmap |
| [SECURITY.md](SECURITY.md) | Credential handling and disclosure policy |

---

## License

[Apache-2.0](LICENSE) — includes an explicit patent grant.

OpenAI-authored DevKit code and assets are **not** used by this project and remain under their own noncommercial licence. OpenAI trademarks remain subject to the [OpenAI brand guidelines](https://openai.com/brand/).