Skip to main content
Glama
mingrath

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.