chatgpt-mac-bridge
by mingrath
README.md
# chatgpt-mac-bridge
Give **ChatGPT Web** hands on your Mac — files, git, terminal, browser, windows — without opening a public port.
ChatGPT talks to an OpenAI-hosted tunnel. A small client on your Mac pulls that work down over outbound HTTPS and forwards it to a local MCP server on loopback. The model can then create and edit. It cannot delete until you say yes.
```
Phone or laptop browser
│
▼
ChatGPT Web ──► OpenAI Secure MCP Tunnel
▲
│ outbound HTTPS only
│ no inbound ports
tunnel-client
│
▼
http://127.0.0.1:8765/mcp
this repo (MCP server)
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
files/git shell/dev interceptor
(browser + macOS)
```
## Why this exists
ChatGPT is useful until the work leaves the chat window. Then you copy a patch, switch apps, run the test, paste the error back, and the quota on a “real” coding agent keeps moving.
A public write-up made the other shape obvious: keep using ChatGPT (the quota you already pay for), and let it drive the machine through [MCP](https://modelcontextprotocol.io) plus [OpenAI Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels). That write-up was Windows — UI Automation, Office COM, Task Scheduler. This repo is the same method on a Mac, with one extra rule the original asked for: **full access except delete**.
What that fixes:
| Pain | What this changes |
|---|---|
| ChatGPT can talk, but cannot touch the repo | It reads, searches, patches, runs tests, and starts the dev server on *this* computer |
| ngrok / Cloudflare Tunnel means a public URL | Secure MCP Tunnel is outbound-only. Nothing listens on the internet |
| A coding-agent subscription burns while you sit in ChatGPT anyway | The loop stays in ChatGPT. Local Codex is optional, not required |
| “Full access” plus `rm -rf` is how you lose a weekend | Delete, trash, `git clean`, and `git reset --hard` return `DELETE BLOCKED` until you confirm a token |
| Phone-only evenings | Type in ChatGPT on the phone. The Mac does the work, as long as it is awake and the tunnel is up |
| Design in a separate tab from code | Drive [Penpot](https://penpot.app) from the same chat, either as a second connector or through the browser tools |
This is not a hosted product. It is a local server plus a documented tunnel. If the Mac sleeps, ChatGPT’s hands disappear.
## What you get
44 MCP tools. The names follow the original write-up so a ChatGPT thread can be reused.
| You say | Tool | Backend |
|---|---|---|
| open this project | `workspace_open` / `workspace_tree` | folders under Desktop, Documents, Developer, `~/dev`, … |
| read / search / patch | `read_file` `search_text` `apply_patch` `edit_file` | filesystem + ripgrep + `git apply` |
| git | `git_status` `git_diff` `git_log` `git_run` | git |
| test / lint / build / dev | `project_*` | npm / pnpm / bun |
| run something | `shell` `process_start` `process_logs` | local spawn, logs in `~/.chatgpt-mac-bridge` |
| second implementer | `codex_run` | local `codex exec` (optional) |
| click the site | `dom_cdp` | [interceptor](https://github.com) browser |
| click the app | `accessibility` `window` `input_event` `vision` | interceptor macOS (Accessibility, not Windows UIA) |
| clipboard / notify / file picker | `clipboard` `notification` `file_dialog` | interceptor + osascript |
| record / play | `screen_record` `audio` | ffmpeg + afplay |
| cron | `scheduler` | LaunchAgents |
| fetch from *this* network | `web_fetch` | local `fetch` |
| Word / Excel-ish | `office` | `textutil` — there is no Office COM on macOS |
Policy tools, always on:
- `bridge_instructions` — standing rules. Ask ChatGPT to call this first.
- `pending_destructive` — unused delete tokens
- `confirm_destructive` — only after **you** said yes. The model cannot tick this for itself in good faith; `userSaidYes` must be true and the token expires in 10 minutes.
## Requirements
- A Mac
- Node 20+
- A ChatGPT account that can enable **Developer mode** and attach a custom MCP app
- An OpenAI Platform org that can create a [Secure MCP Tunnel](https://platform.openai.com/settings/organization/tunnels) and a runtime API key
Optional, only if you want those tools:
| Binary | Unlocks |
|---|---|
| `brew install openai/tools/tunnel-client` | the ChatGPT connection (required for the method, not for local smoke) |
| `rg` (ripgrep) | `search_text` |
| [interceptor](https://github.com) | browser + native UI + clipboard + notifications |
| `codex` | `codex_run` |
| `ffmpeg` | screen / mic capture |
**Plan caveat.** OpenAI documents full write MCP for Business / Enterprise / Edu. Plus / Pro can still attach a connector in Developer mode; some write tools may be confirmation-gated or missing. Custom apps are **web-only** — the iOS app will not grow these tools. Phone use means chatgpt.com in a browser, or the Mac doing the work while you type on the phone.
## Setup
### 1. Run the local server
```sh
git clone https://github.com/mingrath/chatgpt-mac-bridge.git
cd chatgpt-mac-bridge
npm install
npm start
```
You should see:
```
chatgpt-mac-bridge listening on http://127.0.0.1:8765/mcp
```
In another terminal:
```sh
npm run doctor
npm run smoke
```
Smoke checks two things that matter: the tool list is present, and `rm` is refused.
Keep it up across logins:
```sh
./scripts/install-launchagent.sh
```
That writes `~/Library/LaunchAgents/com.chatgpt-mac-bridge.plist` for *this* checkout and the `node` on your PATH. It does not commit a machine-specific plist.
### 2. Open the outbound tunnel
You need three values from OpenAI, none of which this repo can invent:
| Value | Where |
|---|---|
| `tunnel_…` | [Platform → Tunnels](https://platform.openai.com/settings/organization/tunnels) |
| runtime API key | [Platform → API keys](https://platform.openai.com/settings/organization/api-keys) — not an admin key |
| Tunnels **Read + Use** | org role on that Platform org |
Either walk it:
```sh
./scripts/connect-chatgpt.sh
```
or do it by hand:
```sh
brew install openai/tools/tunnel-client
export CONTROL_PLANE_API_KEY="sk-..."
tunnel-client init \
--sample sample_mcp_remote_no_auth \
--profile mac-bridge \
--tunnel-id tunnel_0123456789abcdef0123456789abcdef \
--mcp-server-url http://127.0.0.1:8765/mcp
tunnel-client doctor --profile mac-bridge --explain
tunnel-client run --profile mac-bridge
```
Leave that process running. Admin UI defaults to http://127.0.0.1:8080/ui. If it is not ready, ChatGPT will not see the tools.
### 3. Attach ChatGPT
1. ChatGPT → **Settings → Apps → Advanced Settings → Developer mode**
2. [chatgpt.com/plugins](https://chatgpt.com/plugins) → create a developer-mode app
3. **Connection = Tunnel** → pick the tunnel or paste the `tunnel_id`
4. Scan tools. You want `workspace_*`, `read_file`, `apply_patch`, `shell`, `confirm_destructive`
5. New chat → enable the app →
> Call `bridge_instructions`, then `workspace_list`. Open `~/Desktop` (or your repo) and show me the tree.
First prompt that should feel different: paste a bug and say “fix it in the repo, run the test, do not delete anything.”
### 4. Point it at your code
The server may already see `~/Desktop`, `~/Documents`, `~/Downloads`, `~/Developer`, `~/dev`, `~/src`, `~/projects`, `~/code` if those folders exist.
Anything else:
```
workspace_add_root path: /Users/you/the-repo
workspace_open path: /Users/you/the-repo
```
Or set `WORKSPACE_ROOTS` / `DEFAULT_WORKSPACE` in `.env` (see `.env.example`). Writes are limited to `$HOME` and `/tmp`. `~/.ssh`, Keychains, `/System`, and `/usr` are refused.
## How delete is blocked
A standing regex sits in front of `shell`, `git_run`, `process_start`, and `apply_patch`. It matches `rm`, `git clean`, `git reset --hard`, trash, Finder empty-trash, and patches that delete files.
Those calls do **not** run. They return `DELETE BLOCKED` plus a token. ChatGPT has to show you the preview. If you say yes, it calls `confirm_destructive` with that token and `userSaidYes: true`. Tokens die after ten minutes.
Stopping a process is allowed. Quitting an app is treated as destructive. Overwriting a file with new contents is allowed — that is an edit, not a delete.
This is a seatbelt, not a sandbox. A determined prompt can still do damage through `write_file` or a creative shell line the regex does not know yet. Do not attach this to a machine you do not sit in front of.
## Penpot
Penpot is a separate MCP server. Two clean options:
1. `npx -y @penpot/mcp@stable`, load `http://localhost:4400/manifest.json` inside Penpot, add `http://localhost:4401/mcp` as a **second** ChatGPT connector (another tunnel profile).
2. Stay on this bridge and drive https://design.penpot.app with `dom_cdp`.
`penpot_status` only prints those URLs. It does not proxy Penpot tools.
## Security, said plainly
- The MCP port binds `127.0.0.1`. Do not change `HOST` to `0.0.0.0` unless you know why.
- Optional `MCP_TOKEN` in `.env` requires `Authorization: Bearer …` on `/mcp`.
- `tunnel-client` authenticates to OpenAI. ChatGPT never learns your home IP.
- Anything ChatGPT can see through these tools is anything the local user can see. Treat the ChatGPT thread like an unlocked laptop.
- Official warning from OpenAI still applies: only connect MCP servers you trust. This one can write files.
## Limits
- The Mac must be awake. Sleep = no tools.
- Custom ChatGPT apps do not run inside the iOS app.
- `office` is a compatibility shim. There is no Microsoft Office COM here.
- `codex_run` spends *local* Codex quota, not ChatGPT’s.
- interceptor tools need interceptor installed and Accessibility / Screen Recording granted.
## License
MIT. Method inspired by a public write-up of the same ChatGPT → Secure MCP Tunnel → local tools loop, rewritten for macOS with a no-delete default.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues