by alexanderradahl (community) macOS, Claude Desktop, Claude Code, OpenCode
Даёт ChatGPT настоящий терминал на вашем Mac. Open-source MCP-мост для shell, файлов, PTY-сессий, задач и истории Codex.
Мощный MCP-сервер для управления рабочим столом: выполнение команд, работа с файлами, управление процессами и редактирование кода. …
MCP-сервер для macOS: AI-агент делает скриншоты любых окон и управляет GUI — кликает, печатает, скроллит, перетаскивает …
MCP-сервер для запуска Apple Shortcuts из LLM. Позволяет вызывать любые автоматизации macOS/iOS через Claude, интегрируя AI …
MCP-сервер для управления Homebrew на macOS через естественный язык. Позволяет Claude устанавливать, обновлять и искать пакеты, …
git clone https://github.com/alexanderradahl/mac-developer-bridge.git cd mac-developer-bridge ./menubar/build.sh open /Applications/MacDevBridge.app
Mac Developer Bridge превращает диалог с ChatGPT в слой рассуждений для вашего настоящего Mac. Он может выполнять команды оболочки, редактировать файлы, запускать интерактивные терминальные сессии, управлять длительными фоновыми задачами, читать сохранённые Codex-треды без запуска ещё одного витка модели Codex и, по желанию, управлять вашими реальными вкладками Chrome, в которых вы вошли в систему, в фоновом режиме, не перехватывая фокус.

Пример: «Найди сессию Codex, над которой я работал вчера, проверь живой репозиторий, исправь CI, запушь результат и расскажи, что изменилось».
Именно для таких рабочих процессов создан этот проект.
[!WARNING] Mac Developer Bridge намеренно предоставляет MCP-клиенту действующие разрешения вашего пользователя macOS. Он не изолирован и не имеет списка разрешённых команд или путей. Прочитайте SECURITY.md, прежде чем включать его.
У ChatGPT есть способность рассуждать. У вашего Mac есть исходный код, терминал, учётные данные, инструменты сборки, локальные сервисы и незавершённая работа. Mac Developer Bridge соединяет их через MCP, не добавляя в середину ещё одну модель или цикл агента.
flowchart LR
A[ChatGPT] -->|MCP| B[Mac Developer Bridge]
B --> C[Shell, Git and local CLIs]
B --> D[Filesystem]
B --> E[Real PTY sessions]
B --> F[Background jobs]
B --> G[Stored Codex history]
B --> H[Audit log and kill switch]
Сам мост не выполняет вызовов моделей OpenAI. Он предоставляет детерминированные локальные инструменты; рассуждения обеспечивает ChatGPT. Инструменты для работы с историей Codex используют read-only методы codex app-server и никогда не вызывают turn/start.
Это намеренно отличается от локального кодинг-агента. Здесь нет второго цикла рассуждений. Агентом остаётся ChatGPT; Mac — это среда выполнения.
Для личного аккаунта ChatGPT проще всего использовать приложение в строке меню. Вам понадобятся macOS, Node.js 18+, cloudflared, имя хоста/туннель и режим разработчика ChatGPT.
git clone https://github.com/alexanderradahl/mac-developer-bridge.git
cd mac-developer-bridge
./menubar/build.sh
open /Applications/MacDevBridge.app
Нажмите Start, затем Copy ChatGPT Setup в приложении в строке меню. Подробная настройка OAuth и Cloudflare описана в разделах Подключение к ChatGPT и DEPLOY.md.
Пользователи рабочих пространств, у которых есть доступ к OpenAI Secure MCP Tunnel, могут вместо этого использовать install.sh. См. Транспорты.
Хотите посмотреть, что можно попросить его сделать? Начните с готовых рабочих процессов.
Если проект оказался полезным, поставьте звезду репозиторию, чтобы его могли найти другие разработчики. Если вы создали с его помощью что-то интересное, поделитесь точным рабочим процессом в обсуждении What are you making ChatGPT do on your Mac?.
Это независимый проект с открытым исходным кодом и не является официальным продуктом OpenAI или Cloudflare. OpenAI, ChatGPT, Codex и Cloudflare являются товарными знаками соответствующих владельцев.
Mac Developer Bridge распространяется под лицензией MIT. Приветствуются отчёты об ошибках и целевые pull request; см. CONTRIBUTING.md. Отчёты, связанные с безопасностью, следует направлять в соответствии с рекомендациями из SECURITY.md, а не публиковать открыто.
/bin/zsh -lc от имени вошедшего пользователя macOSgit applybridge.mjs перечитывает файл разблокировки перед каждым вызовом инструмента, поэтому его удаление отклоняет следующий вызов и завершает работу — если процесс не унаследовал MAC_DEV_BRIDGE_FULL_ACCESS_ACK, который обходит файл полностьюscripts/disable.sh), который останавливает фронтенд, мост, опциональный нативный хост фонового Chrome, откреплённые группы заданий shell_start, интерактивные pty-сессии и федеративные дочерние MCP-серверы, проверяя те же цели, на которые он отправлял сигналыGit, менеджеры пакетов, Vercel CLI, CLI баз данных, AppleScript, браузерные CLI, инструменты сборки и другие установленные программы остаются доступными через shell_exec; мост намеренно не поддерживает список разрешённых команд.
| Инструмент | Назначение |
|---|---|
bridge_status |
Идентификация времени выполнения, пути, контекст разрешений, оболочка, режим аудита, бинарный файл Codex, политика фокусировки и статус фонового Chrome |
chrome_workspace_status |
Проверка принадлежащей расширению группы Chrome MDB, активности аренды и пула повторно используемых фоновых вкладок; без необходимости предоставления доступа к веб-сайтам |
chatgpt_extension_status |
Проверка установленного расширения ChatGPT для Chrome, регистрации нативного хоста OpenAI и статуса живого моста страницы только для чтения без изменения расширения OpenAI |
chrome_workspace_setup |
Создание или расширение пула MDB, пока Chrome уже находится на переднем плане; по умолчанию целевое количество — восемь повторно используемых вкладок |
chrome_tabs |
Список вкладок в реальном подписанном профиле Chrome без активации Chrome; доступно только при включённом режиме "Строгие подтверждения" |
chrome_open |
Аренда бездействующей вкладки из постоянной группы MDB и открытие URL без создания новой вкладки |
chrome_navigate |
Переход по утверждённой вкладке без её выбора |
chrome_snapshot |
Чтение видимого текста и интерактивных элементов с утверждённой вкладки |
chrome_click |
Клик по элементу в утверждённой вкладке без вывода Chrome на передний план |
chrome_fill |
Заполнение полей ввода, текстовых областей, выпадающих списков или contenteditable-полей в фоне |
chrome_close |
Возврат вкладки рабочей области MDB в пул бездействующих или закрытие нерабочей фоновой вкладки |
shell_exec |
Выполнение любой команды оболочки на переднем плане, опционально с рабочей директорией, переменными окружения, stdin, тайм-аутом и ограничением вывода |
shell_start |
Запуск откреплённого долго выполняющегося процесса |
shell_job_status |
Проверка состояния выполнения и хвостов журналов |
shell_job_list |
Список метаданных постоянных заданий |
shell_job_kill |
Отправка сигнала фоновой группе процессов |
fs_read |
Чтение текста или base64 с постраничным смещением |
fs_write |
Атомарная замена, создание, добавление или запись в двоичном формате |
fs_list |
Рекурсивный или нерекурсивный список содержимого каталога |
fs_stat |
Метаданные lstat и цель символической ссылки |
fs_manage |
mkdir, удаление, перемещение, копирование, chmod или symlink |
apply_patch |
Применение или проверка unified-диффа с помощью git apply |
codex_thread_read |
Чтение сохранённого треда Codex без его возобновления |
codex_thread_list |
Поиск и постраничный просмотр сохранённых тредов Codex |
codex_thread_turns_list |
Постраничный просмотр сохранённых витков с полными, краткими или опущенными элементами |
audit_tail |
Чтение хвоста локального аудита моста |
На macOS опциональная интеграция фонового браузера работает с тем же подписанным профилем Chrome, который вы уже используете, поэтому существующие сеансы на веб-сайтах работают, но рутинная автоматизация выполняется через небольшое локальное расширение вместо автоматизации пользовательского интерфейса AppleScript или выбора страницы через Chrome DevTools Protocol. Нативный хост при установке привязывается к выбранному профилю/учётной записи Chrome и отказывается работать с профилем, вышедшим из системы или несоответствующим.
Это намеренно реализовано по желанию, поскольку управление аутентифицированным браузером является мощной функцией. Установите нативный хост один раз, затем загрузите распакованное расширение один раз в Chrome:
./scripts/install-background-chrome.sh
Затем в Chrome откройте chrome://extensions, включите Режим разработчика, выберите Загрузить распакованное и укажите каталог chrome-extension/ этого репозитория. Ожидаемый идентификатор расширения — pcebfblnmcappinbenkmddjdapaoajgm.
Расширение поддерживает встроенную группу вкладок Chrome с именем MDB. По умолчанию оно использует восемь бездействующих вкладок, принадлежащих расширению. Они создаются только тогда, когда Chrome уже находится на переднем плане, а затем арендуются и повторно используются для повседневной работы. Группа свернута в состоянии бездействия и разворачивается, когда одна или несколько вкладок арендованы. Это повторяет подход с управляемой группой, используемый расширениями-браузерными агентами, и позволяет избежать особенности macOS/Chrome, обнаруженной в этом проекте: даже chrome.tabs.create({ active:false }) может вывести Chrome на передний план.
Пул теперь самовосстанавливается и саморасширяется. Если Chrome или расширение перезапускается, или все еще присутствует старый пул из четырех вкладок, расширение увеличивает управляемый пул до стандартных восьми вкладок при следующем естественном фокусировании на Chrome. Оно никогда не активирует Chrome только ради восстановления или расширения. Вы также можете принудительно выполнить настройку, пока Chrome уже находится на переднем плане, вызвав chrome_workspace_setup (размер пула по умолчанию: 8).
chrome_workspace_status не требует разрешения, поскольку он только читает локальное состояние рабочего пространства, принадлежащего расширению. Теперь он включает метаданные о возрасте аренды/бездействии, 10-минутный тайм-аут автоматического возврата бездействующих аренд и 20-секундный бюджет ожидания аренды. chrome_workspace_setup также не требует разрешения, поскольку создает только бездействующие страницы, принадлежащие расширению; он отказывается создавать или расширять пул, если Chrome не находится в фокусе, а не перехватывает фокус самостоятельно. Устаревшие/внутренние вызовы tabs.open направляются по тому же пути аренды workspace.open, поэтому они не могут создавать свободные вкладки вне MDB. Когда все вкладки заняты, chrome_open кратко ожидает освобождения вместо немедленного сбоя; заброшенные аренды возвращаются через 10 минут без активности браузера, при этом каждая навигация/снимок/клик/заполнение продлевает активную аренду.
Ослабленный доступ используется по умолчанию. Обычная работа с HTTP/HTTPS через вошедший в систему профиль Chrome MDB не требует команды одобрения в терминале или списка разрешений для каждого сайта. Это сделано намеренно: Mac Developer Bridge уже предоставляет неограниченные полномочия shell/файлов от имени вошедшего пользователя macOS, и полезное поведение по умолчанию — чтобы выполнение в браузере соответствовало выбранному оператором уровню доверия, оставаясь при этом фоново-ориентированным.
Ослабленное одобрение не ослабляет маршрутизацию Chrome. Прямое управление Chrome через shell_exec/shell_start — AppleScript, JXA, прямой запуск исполняемого файла Chrome или shell-команда open HTTP/HTTPS URL (включая open -g) — всегда отклоняется с ошибкой CHROME_BACKGROUND_REQUIRED как в режиме Relaxed, так и в режиме Strict. Работа с браузером должна выполняться с помощью инструментов chrome_* и управляемой группы MDB. Это делает поведение без перехвата фокуса структурным, а не зависящим от выбранного режима одобрения.
Если вы хотите более строгий рабочий процесс браузера/приложения, включите Строгие одобрения в приложении Mac Developer Bridge в строке меню. Переключатель работает без перезапуска. В строгом режиме одобрения chrome-background суммируются и используются совместно во всех сеансах ChatGPT, подключенных к мосту, пока не истечет срок каждого разрешения:
./scripts/approve-personal-browser.sh \
--provider chrome-background \
--url-pattern 'https://www.producthunt.com/*' \
--url-pattern 'https://www.reddit.com/*' \
--ttl 900
Обычный рабочий процесс:
chrome_open открывает одобренный URL-адрес в бездействующей вкладке, арендованной из группы MDB.chrome_snapshot для чтения страницы и получения достаточно стабильных селекторов для видимых элементов управления.chrome_fill / chrome_click / chrome_navigate по мере необходимости.chrome_close для возврата рабочей вкладки на бездействующую страницу расширения и освобождения аренды. Освобождение рабочего пространства является локальной/не требующей разрешения очисткой, поэтому выданные в строгом режиме разрешения URL не могут заблокировать завершенную аренду.Привязка к профилю всегда соблюдается. В ослабленном режиме расширение разрешает обычные сайты HTTP/HTTPS без отдельного разрешения для каждого сайта. В строгом режиме каждое одобрение chrome-background сохраняется в отдельном файле с режимом доступа 0600 в каталоге $DATA_DIR/chrome-background-grants/, истекает не позднее чем через 15 минут и объединяется с другими действующими разрешениями. Просроченные файлы автоматически удаляются, а шаблоны URL-адресов применяются внутри Chrome. Федеративные провайдеры личных браузеров сохраняют свое отдельное поведение однократного использования.
chatgpt_extension_status специально доступен только для чтения. Он сообщает об установленной версии расширения ChatGPT Chrome, локальной регистрации нативного хоста com.openai.codexextension и — когда уже открыта вкладка chatgpt.com — о текущем статусе, возвращаемом собственным мостом страниц OpenAI. MDB не модифицирует расширение OpenAI, не добавляет себя в список разрешённых нативных хостов OpenAI, не раскрывает произвольные приватные RPC-вызовы OpenAI и не открывает боковую панель ChatGPT программным образом. Текущее расширение ChatGPT не объявляет externally_connectable; путь открытия его боковой панели также требует доверенного жеста пользователя.
Что фоновый режим не гарантирует: CAPTCHA, собственные диалоги разрешений браузера/ОС, выбор файлов, загрузки, требующие доверенного жеста пользователя, passkeys и другие элементы защитного интерфейса браузера могут потребовать выполнения на переднем плане/вручную. Мост сообщает об этом ограничении, а не молча активирует Chrome. Это также сознательно уже, чем произвольный перехват JavaScript страницы или сетевых заголовков; см. SECURITY.md.
Чтобы удалить интеграцию:
./scripts/uninstall-background-chrome.sh
Для нативных приложений macOS MDB по-прежнему предпочитает API, способные работать в фоне, или веб-пути, потому что автоматизация таких приложений, как Slack, через Accessibility/AppleScript может потребовать, чтобы целевое приложение стало активным. В режиме по умолчанию (мягком) контроль нативных приложений, не относящихся к Chrome, разрешён без отдельного подтверждения в терминале, поэтому MDB может выполнить задачу, когда взаимодействие с приложением на переднем плане действительно необходимо. Chrome — исключение: поскольку у MDB есть специальное фоновое расширение с выполненным входом, прямая автоматизация GUI Chrome всегда принудительно направляется обратно на путь браузера MDB, а не получает разрешение на перехват фокуса.
Предпочтение в порядке убывания:
MDB с выполненным входом;Когда включён режим Строгие разрешения, управление нативным приложением на переднем плане блокируется, если оператор не создаст одноразовое разрешение, ограниченное конкретным приложением:
./scripts/approve-foreground-gui.sh --app Slack --ttl 60
Строгий режим необязателен и выключен по умолчанию. Флажок в строке меню меняет его на лету.
Настоящий pty, выделяемый lib/ptyhelper.pl (ядро Perl, без добавленных зависимостей). Рекламируется только тогда, когда помощник запущен на этом хосте; в противном случае шесть инструментов отсутствуют, а не работают со сбоями.
| Инструмент | Назначение |
|---|---|
pty_start |
Запускает программу на реальном терминале и возвращает идентификатор сессии |
pty_read |
Читает транскрипт с байтового курсора, при необходимости с длительным опросом |
pty_write |
Отправляет нажатия клавиш, включая управляющие символы |
pty_resize |
Изменяет размер окна, подтверждается чтением из ядра |
pty_signal |
Отправляет сигнал группе процессов сессии |
pty_close |
Завершает сессию и освобождает её |
Ограничения, которые будут заметны при обычном использовании:
pty_write отказывает в такой записи с PTY_WRITE_CANON_LIMIT, а не сообщает байты, которые программа никогда не увидит. Байты накапливаются между вызовами до появления \r или \n, поэтому разбиение на части не обходит это. Отправляйте строки длиной не более 1023 байт. Сессия, переведшая свой терминал в raw-режим, проверяется и допускается.pty_start не могут превысить его.MAC_DEV_BRIDGE_PTY_RING_BYTES вывода в фиксированном кольце; pty_read сообщает lostBytes, когда курсор отстаёт.pty_close сообщает leaderGroupGone, ttyProcessesKilled и uncontainedPids отдельно, и containmentVerified равно true только когда ничего не выжило.Если сконфигурирован реестр провайдеров, инструменты каждого провайдера анонсируются с префиксом key__tool и проксируются. Встроенного провайдера нет: реестр предоставляется оператором. Режим личного браузерного профиля требует одноразового разрешения оператора — см. SECURITY.md.
Эти переменные читаются в bridge.mjs по обоим транспортам.
| Переменная | По умолчанию | Назначение |
|---|---|---|
MAC_DEV_BRIDGE_DATA_DIR |
~/Library/Application Support/MacDeveloperBridge |
Состояние, метаданные заданий, корни федерации. |
MAC_DEV_BRIDGE_LOG_DIR |
~/Library/Logs/MacDeveloperBridge |
Каталог журналов. |
MAC_DEV_BRIDGE_AUDIT_LOG |
$LOG_DIR/audit.jsonl |
Путь к аудиту JSONL. |
MAC_DEV_BRIDGE_AUDIT_MODE |
metadata |
off, metadata или full. full записывает аргументы инструментов; см. предостережение в SECURITY.md. |
MAC_DEV_BRIDGE_UNLOCK_FILE |
$DATA_DIR/FULL_ACCESS_ENABLED |
Отзываемый latch разблокировки. Повторно считывается перед каждым вызовом инструмента. |
MAC_DEV_BRIDGE_UNLOCK_RECHECK_MS |
3000 |
Как часто latch повторно считывается, пока существует pty-сессия или федеративный дочерний процесс и клиент молчит. Ограничивает время, на которое они могут пережить удаление файла разблокировки. |
MAC_DEV_BRIDGE_SHELL |
login shell | Оболочка, используемая для shell_exec/shell_start. |
MAC_DEV_BRIDGE_DEFAULT_OUTPUT_BYTES |
1000000 |
Лимит вывода по умолчанию на вызов. |
MAC_DEV_BRIDGE_MAX_OUTPUT_BYTES |
8000000 |
Потолок, который может запросить вызов. |
MAC_DEV_BRIDGE_PTY_PERL |
/usr/bin/perl |
Интерпретатор для pty-помощника. |
MAC_DEV_BRIDGE_PTY_HELPER |
lib/ptyhelper.pl рядом с bridge.mjs |
Путь к вспомогательному скрипту. |
MAC_DEV_BRIDGE_PTY_MAX_SESSIONS |
8 (1–64) |
Лимит одновременных сессий. kern.tty.ptmx_max равен 511 на всю систему, так что это защищает собственный Terminal.app оператора, а не только этот процесс. |
MAC_DEV_BRIDGE_PTY_RING_BYTES |
262144 (4 КиБ–4 МиБ) |
Объём сохраняемого вывода на сессию. Общий объём — это значение, умноженное на лимит сессий. |
MAC_DEV_BRIDGE_PTY_IDLE_TIMEOUT_MS |
900000 (1 с–1 ч) |
Окно возврата простаивающей сессии и потолок: pty_start может запросить меньшее, но не большее. Фактическое значение активной сессии указано в bridge_status. |
MAC_DEV_BRIDGE_PTY_MAX_LIFETIME_MS |
28800000 (5 с–24 ч) |
Жёсткий потолок, применяется даже к активно используемой сессии. |
MAC_DEV_BRIDGE_PTY_START_TIMEOUT_MS |
5000 |
Сколько времени pty_start ждёт, пока помощник сообщит о реальном pty. |
MAC_DEV_BRIDGE_MCP_SERVERS |
— | Путь к JSON-файлу реестра дочерних MCP-провайдеров. |
MAC_DEV_BRIDGE_MCP_SERVERS_JSON |
— | Тот же реестр, но встроенный. Имеет приоритет. |
MAC_DEV_BRIDGE_MCP_START_DEADLINE_MS |
15000 (1 с–120 с) |
Потолок реального времени на полный запуск одного провайдера — рукопожатие, проверка разрешений и каждая страница tools/list. Провайдер, превысивший его, отбрасывается, а не задерживает поверхность инструментов. |
MAC_DEV_BRIDGE_MCP_PING_IDLE_MS |
30000 |
Интервал простоя, после которого федеративный дочерний процесс получает ping; дочерний процесс, не ответивший на ping, считается зависшим и перезапускается. |
MAC_DEV_BRIDGE_PERSONAL_APPROVAL_FILE |
$DATA_DIR/PERSONAL_BROWSER_APPROVED |
Устаревший/федеративный путь одноразового разрешения для личного браузера. Устаревшее разрешение chrome-background здесь импортируется в общий пул для обратной совместимости. |
MAC_DEV_BRIDGE_BACKGROUND_CHROME_GRANT_DIR |
$DATA_DIR/chrome-background-grants |
Каталог аддитивных, истекающих разрешений на фоновые URL Chrome, общих для всех сессий и перезагружаемых после перезапуска моста. |
MAC_DEV_BRIDGE_SETTINGS_FILE |
$DATA_DIR/settings.json |
Настройки оператора. strictApprovals по умолчанию равен false, если файл/ключ отсутствует. Приложение в строке меню управляет им. |
MAC_DEV_BRIDGE_FOREGROUND_GUI_APPROVAL_FILE |
$DATA_DIR/FOREGROUND_GUI_APPROVED |
Строгий режим: одноразовое, ограниченное приложением разрешение на передний план GUI. |
MAC_DEV_BRIDGE_CHROME_SOCKET |
$DATA_DIR/chrome-background.sock |
Unix-сокет между bridge.mjs и необязательным хостом Chrome native-messaging. Режим 0600 внутри каталога данных с режимом 0700. |
MAC_DEV_BRIDGE_CHROME_NATIVE_PID_FILE |
$DATA_DIR/chrome-native-host.pid |
Запись PID, используемая аварийным выключателем для необязательного нативного хоста Chrome. |
MAC_DEV_BRIDGE_FULL_ACCESS_ACK |
— | Форма подтверждения через окружение. Не отзывается; см. ниже. |
MCP-сервер работает с эффективными правами учётной записи macOS, которая его запускает. У него нет списка разрешённых путей, списка разрешённых команд, песочницы или внутреннего шлюза одобрения команд.
macOS по-прежнему применяет контроль конфиденциальности TCC, полный доступ к диску, ACL, SIP, контроль доступа к связке ключей и аутентификацию sudo. Неинтерактивные вызовы MCP-оболочки не волшебным образом предоставляют пароль sudo или терминальный интерфейс. Настраивайте sudo без пароля только когда вы осознанно хотите такого отдельного повышения прав.
Мост отказывается запускаться, пока не существует осознанного подтверждения, и перепроверяет его перед каждым вызовом инструмента — поэтому удаление файла подтверждения предотвращает будущие запуски и останавливает работающий мост при следующем вызове.
Форма окружения (MAC_DEV_BRIDGE_FULL_ACCESS_ACK) намеренно не отзывается таким способом: мост, который её унаследовал, никогда не читает файл, поэтому удаление файла не останавливает его. Шаги установки ниже экспортируют эту переменную, поэтому мост, запущенный из такой оболочки, можно остановить только остановив процесс. Приложение в строке меню удаляет её из своих дочерних процессов именно по этой причине.
Разрешения действий ChatGPT и поведение подтверждения — отдельные вещи. MCP-сервер честно сообщает о правах на запись и деструктивных операциях и не может обойти ограничения, налагаемые продуктом ChatGPT или рабочим пространством.
Мост говорит на MCP через stdio. Два транспорта могут доставить его до ChatGPT.
OpenAI Secure MCP Tunnel (install.sh, описан ниже) — только исходящий
и не требует публичной конечной точки. Он требует тип подключения Tunnel в
диалоге плагина ChatGPT, который недоступен для личных аккаунтов — вариант
отображается, но отключён.
Cloudflare Tunnel + Server URL (mcp-http.mjs) — запасной вариант, когда Tunnel
недоступен. mcp-http.mjs предоставляет мост через Streamable HTTP на
127.0.0.1:8787 за OAuth 2.1 (и статическим Bearer-токеном для других клиентов),
а cloudflared публикует его:
export MAC_DEV_BRIDGE_HTTP_TOKEN="$(openssl rand -hex 32)"
node mcp-http.mjs
В диалоге плагина ChatGPT предлагается аутентификация: OAuth, No Auth или Mixed — поля для API-ключа/Bearer нет. Поэтому
mcp-http.mjsтакже реализует сервер авторизации OAuth 2.1, и именно так вы подключаете ChatGPT. См. Подключение к ChatGPT ниже. Статический Bearer-токен по-прежнему работает для любого клиента, который может отправить заголовокAuthorization: Bearer.
Хост привязан к loopback, а путь — к /mcp, намеренно: единственный предполагаемый
пир — это cloudflared на той же машине.
Окружение:
| Переменная | По умолчанию | Назначение |
|---|---|---|
MAC_DEV_BRIDGE_HTTP_TOKEN |
— | Bearer-токен. Минимум 24 байта, печатный ASCII. Отказывается запускаться без него. |
MAC_DEV_BRIDGE_HTTP_TOKEN_FILE |
— | Читать токен из файла с режимом 0600, чтобы не показывать его в ps eww. Имеет приоритет. |
MAC_DEV_BRIDGE_HTTP_PORT |
8787 |
Порт loopback. |
MAC_DEV_BRIDGE_HTTP_TIMEOUT_MS |
600000 |
Верхний предел на запрос, для длинных вызовов shell_exec. |
MAC_DEV_BRIDGE_ENTRY |
bridge.mjs рядом с mcp-http.mjs |
Тестовый шов для подстановки заглушки моста. Изменение означает, что scripts/disable.sh не распознает дочерний процесс. |
MAC_DEV_BRIDGE_PUBLIC_URL |
выводится из Host |
Задаёт издателя OAuth. Зафиксируйте её: Host управляется клиентом, а издатель должен совпадать с тем, что обнаружил клиент. |
MAC_DEV_BRIDGE_OAUTH_CLIENT_ID |
генерируется | Идентификатор клиента, вставляемый в ChatGPT. Стабилен между перезапусками. |
MAC_DEV_BRIDGE_OAUTH_REDIRECT_URIS |
— | Дополнительные обратные вызовы точного совпадения, через запятую. Добавляются к встроенным. |
MAC_DEV_BRIDGE_OAUTH_CLIENT_SECRET |
— | Необязательный второй фактор при /token, применяется через client_secret_post или client_secret_basic. Поместите то же значение в поле OAuth Client Secret в ChatGPT. Удаляется из окружения дочерних процессов. |
MAC_DEV_BRIDGE_BODY_IDLE_TIMEOUT_MS |
30000 |
Прерывает запрос, если его тело зависает на это время. Это idle-таймаут, а не общий, поэтому медленная, но прогрессирующая загрузка не обрезается. |
MAC_DEV_BRIDGE_MAX_BUFFERED_BYTES |
100663296 (96 МиБ) |
Общий бюджет для буферизованных тел запросов. Превышение сбрасывает нагрузку с повторяемой ошибкой 503. |
Поймите разницу в экспозиции, прежде чем выбирать этот вариант. Транспорт Tunnel устанавливает только исходящие соединения. Этот публикует HTTPS-конечную точку, которая предоставляет неограниченный доступ к оболочке, при этом единственным барьером является один Bearer-токен. Ротируйте токен, если он когда-либо был раскрыт, и рассмотрите Cloudflare Access перед ним как второй фактор.
Для этого транспорта ещё не автоматизировано: install.sh требует tunnel-client и
отклоняет отсутствующий идентификатор tunnel_..., поэтому он не может установить HTTP-путь,
и нет LaunchAgent — ничто не перезапускает mcp-http.mjs или cloudflared после
перезагрузки или сбоя. scripts/doctor.sh покрывает этот транспорт.
uninstall.sh удаляет файлы, но не останавливает работающий фронтенд.
Диалог плагина ChatGPT предлагает три варианта аутентификации — OAuth, No
Auth, Mixed — и не имеет поля API-ключа/Bearer, поэтому статический Bearer-токен
негде ввести. Поэтому mcp-http.mjs реализует сервер авторизации OAuth 2.1,
и именно так ChatGPT подключается.
Заполните диалог следующим образом:
| Поле | Значение |
|---|---|
| Подключение | URL сервера |
| Адрес сервера | https://<hostname>/mcp |
| Аутентификация | OAuth |
| Способ регистрации | Пользовательский OAuth-клиент |
| OAuth Client ID | логируется при запуске, либо задайте MAC_DEV_BRIDGE_OAUTH_CLIENT_ID |
| OAuth Client Secret | оставьте пустым |
| Метод аутентификации на endpoint токена | none |
| Области по умолчанию | mcp |
| OIDC включен | снимите галочку |
В приложении в строке меню пункт Copy ChatGPT Setup заполняет этот список заранее.
Снимите галочку OIDC, потому что /.well-known/openid-configuration обслуживается только как псевдоним метаданных OAuth и намеренно опускает все поля подписи и субъекта. ID-токен не выпускается, поэтому строгий OIDC-клиент должен прервать работу, а не требовать его.
Затем ChatGPT открывает страницу согласия, обслуживаемую вашей собственной машиной. Она называет точный обратный вызов, на который будет перенаправлен, и запрашивает токен моста, что позволяет ей понять, что одобрение пришло от вас. Прочитайте строку "Will redirect to" перед подтверждением — любой путь /connector/oauth/<token> является допустимым коннектором ChatGPT, включая тот, который создал кто-то другой.
Используйте именованный туннель Cloudflare. Имя хоста быстрого туннеля меняется при каждом запуске, и это имя хоста является OAuth-издателем — поэтому перезапуск между обнаружением и обратным вызовом приводит к тому, что издатель перестаёт соответствовать записанному ChatGPT, и строгий клиент молча отбрасывает обратный вызов. Именованный туннель также означает, что коннектор создаётся один раз, а не при каждом запуске.
Обслуживаемые конечные точки: /.well-known/oauth-protected-resource,
/.well-known/oauth-authorization-server, /.well-known/openid-configuration
плюс /.well-known/oauth-protected-resource/mcp,
/.well-known/oauth-authorization-server/mcp, /.well-known/openid-configuration/mcp
и /mcp/.well-known/openid-configuration — всего семь путей, поскольку форма с префиксом /mcp/ существует только для openid-configuration. Затем /authorize,
/token, /revoke, /revoke-all и /healthz. Ответ 401
от /mcp содержит WWW-Authenticate: Bearer resource_metadata="…", что позволяет клиенту обнаружить остальное.
menubar/ собирает небольшое AppKit-приложение для строки меню, которое управляет двумя процессами, необходимыми этому транспорту, и отображает три вещи, которые вы реально используете: публичный URL, bearer-токен и отвечает ли конечная точка.
./menubar/build.sh # also installs a copy to /Applications
open /Applications/MacDevBridge.app
Сборка устанавливается в /Applications (с запасным вариантом ~/Applications), потому что Launchpad и Spotlight не отображают приложения, находящиеся в ~/Downloads. Пакет находит mcp-http.mjs через MAC_DEV_BRIDGE_HOME, затем пакет рядом с собой, затем путь, зашитый в Info.plist во время сборки — поэтому установленная копия по-прежнему находит пакет.
Меню предоставляет: текущий статус, режим туннеля, Copy Server URL, Copy OAuth Client ID, Copy ChatGPT Setup (весь диалог, заполненный по порядку), Copy Bearer Token, Start/Stop, живой флажок Strict approvals (по умолчанию выключен), Rotate Token, Open Logs и Quit.
Оно предпочитает именованный туннель Cloudflare, когда ~/.cloudflared/config.yml объявляет его, что даёт стабильный URL — в противном случае быстрый туннель, чьё имя хоста меняется при каждом запуске и вынуждает пересоздавать коннектор ChatGPT каждый раз. Меню показывает, какой режим активен.
Почему его стоит использовать вместо сырых команд:
mcp-http.mjs и cloudflared; Stop и Quit останавливают только то, что было запущено, а не обнаруживают процессы по имени.bridge.mjs, а не просто убийством процесса.MAC_DEV_BRIDGE_HTTP_TOKEN_FILE, что не позволяет увидеть его в ps eww./healthz и по живучести дочерних процессов, поэтому смерть дочернего процесса сообщается, а не предполагается, что всё в порядке.applicationWillTerminate не выполняется при принудительном завершении, сбое или жёсткой перезагрузке, поэтому предыдущий запуск мог оставить файл разблокировки взведённым, фронтенд работающим, а cloudflared всё ещё публиковать публичное имя хоста. Запуск снимает защёлку и останавливает всё, что записано в mcp-http.pid и cloudflared.pid, каждый из которых сначала проверяется на идентичность, потому что PID переиспользуются, а эти файлы переживают SIGKILL и перезагрузку. Ранее возвращение только фронтенда оставляло публичный вход, который ни один последующий запуск не мог закрыть, и который следующий Start снова взвёл бы вместе со вторым туннелем.MAC_DEV_BRIDGE_FULL_ACCESS_ACK своим дочерним процессам. Эта переменная является
standing unlock in bridge.mjs, поэтому наследование этого не позволило бы Stop отозвать что-либо — а документация по установке говорит экспортировать его. cloudflared продолжал публиковать с взведённой защёлкой, а в меню отображалось «не запущено». PATH login shell, поэтому shell_exec ведёт себя так же, как в терминале (иначе у приложения, запущенного из GUI, нет nvm или Homebrew).Приложение подписано ad-hoc и не нотаризовано. Оно находит mcp-http.mjs через MAC_DEV_BRIDGE_HOME, затем пакет рядом с бандлом, затем путь, встроенный в Info.plist во время сборки — так что копия в /Applications работает с пакетом, оставленным на месте. Пересоберите после перемещения пакета, чтобы встроенный путь оставался правильным.
MAC_DEV_BRIDGE_HOME.
Он не заменяет scripts/disable.sh: отсоединённые задания shell_start по замыслу переживают фронтенд, и только этот скрипт забирает их из реестра заданий.
Оба транспорта:
codex только для трёх инструментов истории Codex. Доступ к оболочке и файловой системе не зависит от Codex.Безопасный MCP-туннель OpenAI дополнительно требует:
tunnel-client, загруженный из OpenAI Platform Tunnels или официального релиза OpenAI на GitHub, исполняемый и доступный в PATH или в ~/.local/bin/tunnel-client.Cloudflare Tunnel + Server URL дополнительно требует:
cloudflared, аутентифицированный для учётной записи Cloudflare.openssl для генерации токена-носителя (bearer token)./mcp жёстко задан без переопределения, и mcp-http.mjs отказывается запускаться без токена.Доступность режима разработчика контролируется развёртыванием учётной записи и политикой рабочего пространства. Если переключатель режима разработчика отсутствует, этот пакет не может обойти это ограничение на стороне продукта. Параметр Tunnel конкретно недоступен на личных аккаунтах — он отображается, но отключён — поэтому существует HTTP-транспорт.
Клонируйте репозиторий (или загрузите релиз/архив) и откройте Терминал в его папке:
git clone https://github.com/alexanderradahl/mac-developer-bridge.git
cd mac-developer-bridge
Затем установите:
chmod +x install.sh uninstall.sh bridge.mjs scripts/*.sh
export MAC_DEV_BRIDGE_FULL_ACCESS_ACK='I_UNDERSTAND_THIS_GRANTS_FULL_ACCESS'
export CONTROL_PLANE_TUNNEL_ID='tunnel_0123456789abcdef0123456789abcdef'
# Hidden input; the key is not placed in shell history.
read -r -s -p 'Tunnel runtime API key: ' CONTROL_PLANE_API_KEY; printf '\n'
export CONTROL_PLANE_API_KEY
./install.sh
unset CONTROL_PLANE_API_KEY MAC_DEV_BRIDGE_FULL_ACCESS_ACK
Установщик также может запросить идентификатор туннеля и ключ времени выполнения при интерактивном запуске. Ключ времени выполнения хранится в связке ключей входа macOS и не записывается в пакет, профиль туннеля или plist LaunchAgent.
Установщик:
~/.local/share/mac-developer-bridge.~/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED с правами 0600.tunnel-client и запускает tunnel-client doctor.Поддерживаются пользовательские расположения с помощью переменных окружения MAC_DEV_BRIDGE_INSTALL_DIR, MAC_DEV_BRIDGE_BIN_DIR, MAC_DEV_BRIDGE_PLIST_DIR, MAC_DEV_BRIDGE_DATA_DIR и MAC_DEV_BRIDGE_LOG_DIR.
https://<hostname>/mcp. Аутентификация предлагает только OAuth, No Auth или Mixed — см. Connecting to ChatGPT; в этом диалоге нет поля для bearer-токена.bridge_status.Предлагаемый первый запрос:
Use only the Mac Developer Bridge app for local-machine operations.
First call bridge_status and report the effective user, home directory, shell, Codex binary, audit mode, and whether the tunnel runtime key was scrubbed from child command environments.
Then call codex_thread_read with:
{"thread_id":"019fa926-dbbd-7d72-aa0c-8edd41bd585c","include_turns":true}
If the result is too large, call codex_thread_turns_list in ascending order with items_view="full" and continue through nextCursor until the complete persisted history is recovered.
Do not invoke codex, codex exec, codex-reply, turn/start, or any OpenAI API from shell commands. The Chat conversation is the reasoning agent. Inspect the repository and branch referenced by the thread, report the current state, and continue the unfinished work.
Ask before production deployments, destructive database operations, credential changes, force pushes, or deleting user data.
codex_thread_read и codex_thread_turns_list используют локальные API чтения codex app-server. Мост не предоставляет ни одного метода Codex, который запускает ход модели.
Проверьте текущее состояние, прежде чем гадать:
scripts/tcc-doctor.sh # add --open to jump to the settings pane
Он проверяет защищённый TCC путь как node и как $MAC_DEV_BRIDGE_SHELL — только эти два — и сообщает, какие из них имеют разрешение. Полный доступ к диску не может быть предоставлен из скрипта — базы данных TCC защищены SIP, поэтому они недоступны для записи даже от root, а tccutil может только сбрасывать записи. Человек должен добавить бинарный файл в Системных настройках, или MDM должен отправить профиль PPPC.
Если чтение завершается с ошибкой EPERM или «Operation not permitted», предоставьте Полный доступ к диску фактическим исполняемым файлам в цепочке выполнения:
node, показанный bridge_status/bin/zshtunnel-client, только для транспорта Tunnelcloudflared не нуждается в этом: он только пересылает HTTP на loopback и никогда не касается файловой системы от имени инструмента.
LaunchAgent может не наследовать разрешения конфиденциальности, ранее предоставленные Терминалу или другой установке Node. Полный доступ к диску отделён от обычных разрешений POSIX.
Пути ~/.local/share/mac-developer-bridge ниже существуют только если был запущен install.sh, который требует tunnel-client — поэтому при HTTP-транспорте этот каталог не существует, и вы запускаете скрипты из извлечённого каталога пакета.
Оба транспорта:
# Full diagnostic report (includes the Full Disk Access check)
./scripts/doctor.sh # or ~/.local/share/mac-developer-bridge/scripts/doctor.sh
# Kill switch. Read its output; a non-zero exit means NOT contained.
./scripts/disable.sh
# Audit log
tail -f "$HOME/Library/Logs/MacDeveloperBridge/audit.jsonl"
HTTP-транспорт:
# Logs (only populated if you redirected them, as DEPLOY.md step 2 does)
tail -f "$HOME/Library/Logs/MacDeveloperBridge/http.stderr.log"
# Restart: there is no LaunchAgent, so stop and re-run it.
# disable.sh REMOVES the unlock file, so it must be recreated — without this the
# front end starts and /healthz answers 200 while every tool call fails 503,
# because /healthz never spawns the bridge.
./scripts/disable.sh
printf 'I_UNDERSTAND_THIS_GRANTS_FULL_ACCESS\n' \
> "$HOME/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED"
chmod 600 "$HOME/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED"
export MAC_DEV_BRIDGE_HTTP_TOKEN='<the same token the plugin uses>'
node mcp-http.mjs >>"$HOME/Library/Logs/MacDeveloperBridge/http.stderr.log" 2>&1 &
Туннельный транспорт:
launchctl print "gui/$(id -u)/com.openai.mac-developer-bridge-tunnel"
launchctl kickstart -k "gui/$(id -u)/com.openai.mac-developer-bridge-tunnel"
# Re-enable after an explicit acknowledgement (requires the LaunchAgent plist)
export MAC_DEV_BRIDGE_FULL_ACCESS_ACK='I_UNDERSTAND_THIS_GRANTS_FULL_ACCESS'
~/.local/share/mac-developer-bridge/scripts/enable.sh
unset MAC_DEV_BRIDGE_FULL_ACCESS_ACK
# Rotate the tunnel runtime key with hidden input
~/.local/share/mac-developer-bridge/scripts/rotate-tunnel-key.sh
tail -f "$HOME/Library/Logs/MacDeveloperBridge/tunnel.stderr.log"
enable.sh в настоящее время требует plist LaunchAgent, поэтому он не работает на HTTP-транспорте. Чтобы снова включить его там, воссоздайте файл разблокировки и перезапустите фронтенд, как в DEPLOY.md, вариант B.
tunnel-client обычно предоставляет loopback-эндпоинты для проверки состояния и операторский интерфейс по адресу http://127.0.0.1:8080/healthz, /readyz, /metrics и /ui во время работы.
Режим по умолчанию — metadata. Он записывает:
При HTTP-транспорте установка отсутствует, а приложение в строке меню, запущенное через GUI, не имеет наследуемого окружения оболочки — поэтому единственные способы изменить режим аудита там: экспортировать его в оболочке и запустить mcp-http.mjs из этой оболочки, или запустить приложение с помощью open -a MacDevBridge --env MAC_DEV_BRIDGE_AUDIT_MODE=full. В противном случае он остаётся в metadata.
Установите MAC_DEV_BRIDGE_AUDIT_MODE перед установкой в одно из значений:
MAC_DEV_BRIDGE_AUDIT_MODE=off
MAC_DEV_BRIDGE_AUDIT_MODE=metadata
MAC_DEV_BRIDGE_AUDIT_MODE=full
full может сохранять чувствительные аргументы команд и содержимое файлов даже после обычного редактирования по шаблонам токенов. Относитесь к журналу аудита как к чувствительному. Ключ выполнения туннеля удаляется из окружения процесса моста до того, как любой инструмент оболочки или файловой системы сможет запуститься, хотя неограниченный доступ к оболочке всё ещё может достичь других учётных данных, доступных учётной записи macOS.
Сам мост не содержит клиента вывода OpenAI, а адаптеры Codex вызывают методы сервера приложения только для чтения. Тем не менее, проверьте поведение, специфичное для учётной записи, после подключения:
bridge_status и fs_stat на безвредном пути.Не используйте shell_exec для запуска самого Codex, если цель — избежать использования модели Codex.
Из извлечённого пакета или установленного каталога:
./uninstall.sh
Установщик удаляет LaunchAgent, установку моста, символьную ссылку команды, файл разблокировки и ключ выполнения Keychain.
Он не удаляет каталог данных, поэтому после удаления сохраняются следующие элементы, включая два активных учётных данных:
http-token — токен-носитель (режим 0600)oauth-state.json — идентификатор клиента OAuth плюс дайджесты токенов доступа и обновления (режим 0600)oauth-client-id, mcp-http.pid, cloudflared.pid, jobs/ и журнал аудитаУдалите также ~/Library/Application Support/MacDeveloperBridge, если хотите избавиться от учётных данных. Он также не останавливает запущенный фронтенд; сначала выполните scripts/disable.sh.