Skip to main content
Glama

chatgpt-desktop-mcp

An MCP server that lets other agents work with the Codex chats in the ChatGPT desktop app on your Mac: list and read conversations, start new chats in your projects, send follow-ups, pick the model, reasoning effort, and speed, steer or interrupt running work, and answer approvals. It runs locally and can be exposed to the internet behind bearer tokens, so a remote agent can monitor and drive your Codex work.

Chats created this way show up in the app's sidebar, and messages sent to a chat that is open in the app appear live in its window.

Unofficial. Not affiliated with OpenAI. It combines the documented Codex app-server protocol with the desktop app's local IPC channel, which is internal and can change when the app updates. Run chatgpt-desktop-mcp doctor after updating the app.

How it works

flowchart LR
  Agent["Remote agent or MCP client"] -->|"HTTPS + bearer token"| Tunnel["Tunnel or reverse proxy"]
  Tunnel --> MCP["chatgpt-desktop-mcp\n127.0.0.1:8723/mcp"]
  MCP -->|"local IPC (~/.codex/ipc)"| App["ChatGPT desktop app\nchats open in a window"]
  MCP -->|"stdio JSON-RPC"| Engine["codex app-server\nbundled with ChatGPT.app"]
  App --> Store[("~/.codex\nchats, projects, config")]
  Engine --> Store

The bridge routes each request to the right place:

  • Chats open in a ChatGPT window are driven through the app itself, the same way the app's own surfaces coordinate. Messages, model changes, steering, and interrupts appear live in the window.

  • Everything else (listing, history, model catalog, new chats, chats not open in the app) goes through a private codex app-server started from the binary bundled inside ChatGPT.app. It shares ~/.codex with the app, so it sees the same chats, projects, sign-in, MCP servers, and settings.

Codex lets only one process write to a chat at a time. The process that runs bridge turns exits about two seconds after its last turn so the app can open those chats again.

Related MCP server: Mac Developer Bridge

Requirements

  • macOS with the ChatGPT desktop app and Codex. Verified with ChatGPT 26.1027.30824 (bundled Codex 0.166.0-alpha.5.1).

  • Node.js 22 or newer.

  • Optional: cloudflared for a public URL (brew install cloudflared). A fixed URL also needs a domain on Cloudflare.

Install

npm install -g https://github.com/kevinrss01/chatgpt-desktop-mcp/tarball/main
chatgpt-desktop-mcp doctor

The compiled CLI is committed in dist/, so installing from GitHub needs no build step. From a clone: npm install && npm link.

Quick start

# 1. Create a token. It is printed once; only its SHA-256 digest is stored.
chatgpt-desktop-mcp token create --name my-agent --scope control

# 2. Serve MCP on http://127.0.0.1:8723/mcp
chatgpt-desktop-mcp serve

# Or also publish it on a temporary public HTTPS URL
chatgpt-desktop-mcp serve --tunnel

Every request needs Authorization: Bearer <token>. Tokens have one of two scopes:

Scope

Tools

read

list_models, list_projects, list_chats, read_chat, get_chat_status, wait_for_chat, list_pending_requests

control

Everything in read, plus create_chat, send_message, interrupt_chat, rename_chat, archive_chat, respond_to_request

Manage tokens with chatgpt-desktop-mcp token list and chatgpt-desktop-mcp token revoke <id-or-name>.

Connect an agent

Claude Code

claude mcp add --transport http chatgpt-desktop https://YOUR-HOST/mcp --header "Authorization: Bearer $CDM_TOKEN"

Codex CLI (~/.codex/config.toml)

[mcp_servers.chatgpt_desktop]
url = "https://YOUR-HOST/mcp"
bearer_token_env_var = "CDM_TOKEN"

OpenAI Responses API

const response = await openai.responses.create({
  model: 'gpt-6.1-sol',
  input: 'Check my Codex chats and summarize what is still running.',
  tools: [
    {
      type: 'mcp',
      server_label: 'chatgpt_desktop',
      server_url: 'https://YOUR-HOST/mcp',
      headers: { Authorization: `Bearer ${process.env.CDM_TOKEN}` },
      require_approval: 'never',
    },
  ],
});

Local stdio clients (no token; the client launches the server):

{ "mcpServers": { "chatgpt-desktop": { "command": "chatgpt-desktop-mcp", "args": ["stdio"] } } }

Add "--read-only" to the args for a monitoring-only setup. ChatGPT custom connectors require OAuth, which this server does not implement yet.

To try tools from a terminal: CDM_URL=http://127.0.0.1:8723/mcp CDM_TOKEN=... node scripts/call-tool.mjs list_chats '{"limit":5}'.

Tools

Tool

What it does

list_models

Models available to the app with their reasoning efforts and speed tiers.

list_projects

Projects saved in the app, plus folders used by recent chats.

list_chats

Chats sorted by last activity; filter by project, search text, or archived state.

read_chat

The latest turns of a chat in order: messages, plus one-line summaries of commands, edits, and tool calls. Page back with olderCursor.

get_chat_status

Whether the chat is running and where (app, bridge, or stored), its model, effort, and speed, pending approvals, and the latest assistant message.

wait_for_chat

Waits until the chat stops running (up to 300 s), then returns its status.

create_chat

Starts a chat in a project (or a new folder under ~/Documents/Codex/<date>/, as the app does) with an optional first prompt, model, effort, speed, sandbox, and approval policy.

send_message

Sends a follow-up. If the chat is busy, whenBusy chooses to reject, steer the running turn, or interrupt it first.

interrupt_chat

Stops the running turn.

rename_chat, archive_chat

Retitle, archive, or restore a chat.

list_pending_requests, respond_to_request

See and answer command, file, question, and MCP approval requests from chats the bridge runs.

Model, effort, and speed. list_models returns valid values. speed accepts standard, a tier id such as priority or ultrafast, or a tier name such as Fast. For a chat open in the app, a model or effort change is applied for the next turn, as the app's model picker does.

A typical monitoring loop: list_chats to see what changed, get_chat_status for live state, wait_for_chat until a run ends, then read_chat for the transcript.

Exposing it safely

A control token lets the holder make Codex run commands on your Mac with the permissions in your Codex configuration. Treat it like SSH access.

  • The server listens on 127.0.0.1 by default. Publish it through a TLS tunnel or reverse proxy; do not bind it to a public interface.

  • serve --tunnel starts a Cloudflare quick tunnel. Its URL is random and changes on every start, which suits testing. For a fixed address, use service install --hostname below, Tailscale Funnel, or your own proxy pointing at http://127.0.0.1:8723.

  • Give monitoring agents read tokens. Revoke tokens you no longer use.

  • Requests carrying a browser Origin header are rejected unless allowed with --allow-origin, which also enables CORS for that origin. Repeated failed authentication from one client returns 429.

  • To restrict chats the bridge starts or resumes, run with --sandbox workspace-write --approval-policy on-request and answer approvals with respond_to_request. Callers cannot loosen these settings through tool arguments. Chats open in the app keep the app's own settings.

Keep it running with a fixed URL

service install sets up macOS LaunchAgents so the server and a named Cloudflare tunnel start at login and restart when they exit. The public URL stays the same across restarts, network changes, and reinstalls.

cloudflared tunnel login        # once, pick the zone that will hold the hostname
chatgpt-desktop-mcp service install --hostname mcp.example.com
chatgpt-desktop-mcp token create --name my-agent
chatgpt-desktop-mcp service status

Agents then connect to https://mcp.example.com/mcp. Install creates the tunnel (default name chatgpt-desktop-mcp) and a DNS record for the hostname, and refuses to replace an existing record unless you pass --overwrite-dns. The tunnel forwards only that hostname to http://127.0.0.1:8723.

It loads these agents in ~/Library/LaunchAgents, with logs in ~/Library/Logs/chatgpt-desktop-mcp:

Agent

What it does

server

Runs serve on 127.0.0.1 and restarts it if it exits.

tunnel

Runs the named tunnel. cloudflared reconnects by itself after network drops.

watchdog

Every 2 minutes, restarts the server if /healthz stops answering, and restarts the tunnel if the public URL fails twice in a row while the Mac is online.

keepawake

Runs caffeinate -s, which stops the Mac from sleeping while it is on AC power. Skip with --no-keep-awake.

openapp

Opens the ChatGPT app in the background at login so open chats can be controlled live. Skip with --no-open-app.

Run service install again after upgrading Node or this package. service uninstall removes the agents and keeps the tunnel and DNS record; delete those with cloudflared tunnel delete <name> and the Cloudflare dashboard.

What this covers and what it cannot:

  • Network drops and Wi‑Fi changes: covered. The tunnel reconnects and the URL does not change.

  • Crashes: covered. launchd restarts the server and tunnel within about 10 seconds, and the watchdog catches a process that hangs.

  • Restart or shutdown: everything starts again once you log in. With FileVault on, macOS waits for your password at boot, so the URL is down until then. To power back on after a power cut, run sudo pmset -a autorestart 1.

  • Sleep: a sleeping Mac cannot answer. keepawake prevents idle sleep on AC power only.

  • Closing the lid: the Mac sleeps unless it is in clamshell mode (power plus an external display). sudo pmset -a disablesleep 1 keeps it awake with the lid closed; undo with sudo pmset -a disablesleep 0. A closed, awake laptop runs warm, so do not put it in a bag.

  • The ChatGPT app: listing, reading, and new chats work without it. Live control of chats open in a window needs the app running.

For an address that stays up even when the Mac is off, run the bridge on an always-on Mac (a Mac mini, for example) and connect the other agents there.

Limitations

  • macOS only. Live control of open chats relies on the app's internal IPC messages and may need an update after a ChatGPT release; the background engine keeps working in that case.

  • When a chat is not open in the app, the app's router needs about 10 seconds to confirm that before the bridge runs the message in its background engine. Messages to open chats are delivered immediately.

  • While the bridge is running a turn in a chat that is not open in the app, the app can show that chat but cannot continue it until the turn ends.

  • Renaming or archiving a chat that the app currently has loaded is refused; do it in the app.

  • Approvals for runs inside the app are answered in the app; get_chat_status shows that one is waiting.

  • Messages are text only. Regular ChatGPT conversations outside Codex are stored in your ChatGPT account, not in ~/.codex, and are not covered.

Development

npm install
npm test          # unit and protocol tests (no app needed)
npm run build     # rebuild dist/ and commit it with your change
node dist/cli.js serve --verbose

The tests cover the IPC framing and a fake desktop router, token storage, model option resolution, history formatting, live-state parsing, and HTTP authentication and scopes through a real MCP client.

License

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers