Mac Developer Bridge

by alexanderradahl (community) · macOS, Claude Desktop, Claude Code, OpenCode

MCP MCP Servers Open Source v0.2.0 · 15.08.2026 активный

Даёт ChatGPT настоящий терминал на вашем Mac. Open-source MCP-мост для shell, файлов, PTY-сессий, задач и истории Codex.

v0.2.0
15.08.2026 current

Установка
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.

CI Лицензия: MIT

Mac Developer Bridge превращает диалог с ChatGPT в слой рассуждений для вашего настоящего Mac. Он может выполнять команды оболочки, редактировать файлы, запускать интерактивные терминальные сессии, управлять длительными фоновыми задачами, читать сохранённые Codex-треды без запуска ещё одного витка модели Codex и, по желанию, управлять вашими реальными вкладками Chrome, в которых вы вошли в систему, в фоновом режиме, не перехватывая фокус.

Mac Developer Bridge: ChatGPT рассуждает через MCP в оболочке, PTY-сессиях, истории Codex и живом Mac

Пример: «Найди сессию 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.

Что это даёт

  • Восстановить сохранённый Codex-тред, изучить репозиторий, на который он ссылается, и продолжить работу из ChatGPT.
  • Запускать тесты, сборки, Git, менеджеры пакетов, CLI для баз данных, AppleScript и другие инструменты, уже установленные на вашем Mac.
  • Поддерживать интерактивные оболочки и терминальные программы через настоящий PTY, а не имитировать терминал через stdin.
  • Запускать длительные локальные задачи, позже просматривать их журналы и останавливать всю группу процессов.
  • Читать и изменять файлы в любом месте, доступном вашему пользователю macOS.
  • По желанию управлять одобренными страницами в вашем реальном профиле Chrome, в котором вы вошли в систему, не выводя Chrome на передний план.

Это намеренно отличается от локального кодинг-агента. Здесь нет второго цикла рассуждений. Агентом остаётся 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 от имени вошедшего пользователя macOS
  • Фоновые задания в откреплённом режиме с постоянными журналами stdout/stderr, проверкой состояния и завершением групп процессов
  • Неограниченные чтение, запись, добавление, список, stat, копирование, перемещение, chmod, symlink, mkdir и рекурсивное удаление файлов
  • Применение unified-диффов через git apply
  • Обнаружение и чтение сохранённых тредов Codex без возобновления треда или запуска витка модели Codex
  • Постраничное получение витков Codex для историй, слишком больших для одного ответа
  • Локальное аудирование в формате JSONL
  • Исходящее только приватное соединение через OpenAI Secure MCP Tunnel или обычный loopback HTTP-фронтенд, который Cloudflare Tunnel публикует через HTTPS
  • Постоянство данных на пользователя через macOS LaunchAgent
  • Защёлка разблокировки с отказом по умолчанию: bridge.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 Чтение хвоста локального аудита моста

Фоновый Chrome без перехвата фокуса

На 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

Обычный рабочий процесс:

  1. chrome_open открывает одобренный URL-адрес в бездействующей вкладке, арендованной из группы MDB.
  2. chrome_snapshot для чтения страницы и получения достаточно стабильных селекторов для видимых элементов управления.
  3. chrome_fill / chrome_click / chrome_navigate по мере необходимости.
  4. 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, а не получает разрешение на перехват фокуса.

Предпочтение в порядке убывания:

  1. API или MCP-коннектор для сервиса;
  2. веб-приложение сервиса через группу Chrome MDB с выполненным входом;
  3. автоматизация GUI нативного приложения, только когда взаимодействие на переднем плане действительно необходимо.

Когда включён режим Строгие разрешения, управление нативным приложением на переднем плане блокируется, если оператор не создаст одноразовое разрешение, ограниченное конкретным приложением:

./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 Завершает сессию и освобождает её

Ограничения, которые будут заметны при обычном использовании:

  • Длина строки. Пока терминал находится в каноническом режиме — это значение по умолчанию, используемое во всех интерактивных приглашениях, — дисциплина линии отбрасывает входную строку размером 1024 байта или более, а не усекает её. pty_write отказывает в такой записи с PTY_WRITE_CANON_LIMIT, а не сообщает байты, которые программа никогда не увидит. Байты накапливаются между вызовами до появления \r или \n, поэтому разбиение на части не обходит это. Отправляйте строки длиной не более 1023 байт. Сессия, переведшая свой терминал в raw-режим, проверяется и допускается.
  • Параллельность. Лимит сессий забирается, а не просто проверяется, поэтому параллельные вызовы pty_start не могут превысить его.
  • Хранение. Каждая сессия хранит последние MAC_DEV_BRIDGE_PTY_RING_BYTES вывода в фиксированном кольце; pty_read сообщает lostBytes, когда курсор отстаёт.
  • Изоляция. См. SECURITY.md — pty_close сообщает leaderGroupGone, ttyProcessesKilled и uncontainedPids отдельно, и containmentVerified равно true только когда ничего не выжило.

Федеративные дочерние MCP-серверы

Если сконфигурирован реестр провайдеров, инструменты каждого провайдера анонсируются с префиксом 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

Диалог плагина 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="…", что позволяет клиенту обнаружить остальное.

Приложение в строке меню (HTTP-транспорт)

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 каждый раз. Меню показывает, какой режим активен.

Почему его стоит использовать вместо сырых команд:

  • Это супервизор. Start запускает mcp-http.mjs и cloudflared; Stop и Quit останавливают только то, что было запущено, а не обнаруживают процессы по имени.
  • Start записывает файл разблокировки, а Stop удаляет его, поэтому остановка является защитной через защёлку вызова bridge.mjs, а не просто убийством процесса.
  • Токен хранится в файле с режимом 0600 и передаётся через 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 по замыслу переживают фронтенд, и только этот скрипт забирает их из реестра заданий.

Предварительные требования

Оба транспорта:

  1. macOS и вошедший в систему пользователь рабочего стола.
  2. Node.js 18 или новее.
  3. Режим разработчика ChatGPT.
  4. Работающий CLI codex только для трёх инструментов истории Codex. Доступ к оболочке и файловой системе не зависит от Codex.

Безопасный MCP-туннель OpenAI дополнительно требует:

  1. Официальный бинарный файл tunnel-client, загруженный из OpenAI Platform Tunnels или официального релиза OpenAI на GitHub, исполняемый и доступный в PATH или в ~/.local/bin/tunnel-client.
  2. Идентификатор туннеля OpenAI, привязанный к рабочему пространству ChatGPT, которое будет его использовать.
  3. Ключ API времени выполнения, субъект которого имеет права Tunnels Read + Use.
  4. Параметр подключения Tunnel в диалоге плагина ChatGPT.

Cloudflare Tunnel + Server URL дополнительно требует:

  1. cloudflared, аутентифицированный для учётной записи Cloudflare.
  2. Имя хоста, которым вы управляете, или URL быстрого туннеля.
  3. openssl для генерации токена-носителя (bearer token).
  4. Параметр подключения Server URL с OAuth — см. Connecting to ChatGPT. Режима No-Auth нет: /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.

Установщик:

  1. Требует точного подтверждения полного доступа.
  2. Проверяет macOS, Node, tunnel-client, обнаружение Codex, формат идентификатора туннеля, режим аудита и оболочку.
  3. Копирует мост в ~/.local/share/mac-developer-bridge.
  4. Создаёт ~/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED с правами 0600.
  5. Запускает тесты синтаксиса, протокола MCP, файловой системы, исправлений, процессов, очистки секретов и адаптера Codex.
  6. Сохраняет ключ времени выполнения в связке ключей.
  7. Создаёт уникальный профиль stdio для tunnel-client и запускает tunnel-client doctor.
  8. Устанавливает и запускает постоянный LaunchAgent для пользователя.

Поддерживаются пользовательские расположения с помощью переменных окружения 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.

Подключение ChatGPT

  1. Включите режим разработчика в ChatGPT.
  2. Откройте плагины ChatGPT и создайте приложение режима разработчика.
  3. Настройте подключение в зависимости от транспорта:
    • Туннельный транспорт: выберите Tunnel, затем выберите или вставьте тот же идентификатор туннеля, который использовался при установке. Недоступно на личных аккаунтах — параметр отображается, но отключён.
    • HTTP-транспорт: выберите Server URL и введите https://<hostname>/mcp. Аутентификация предлагает только OAuth, No Auth или Mixed — см. Connecting to ChatGPT; в этом диалоге нет поля для bearer-токена.
  4. Просмотрите и включите инструменты, и отметьте признание рисков.
  5. Начните новый чат, выберите приложение и вызовите 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/zsh
  • установленный бинарный файл tunnel-client, только для транспорта Tunnel

cloudflared не нуждается в этом: он только пересылает 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. Он записывает:

  • отметку времени и имя инструмента
  • отредактированный предпросмотр аргументов
  • SHA-256 хэш полных аргументов
  • краткую сводку результата или ошибку

При 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 вызывают методы сервера приложения только для чтения. Тем не менее, проверьте поведение, специфичное для учётной записи, после подключения:

  1. Запишите текущий баланс кредитов Codex/Work.
  2. В чате вызывайте только bridge_status и fs_stat на безвредном пути.
  3. Обновите страницу использования Codex/Work.
  4. Подтвердите, что использование модели Codex не было записано.
  5. Затем прочитайте сохранённую ветку Codex и продолжите работу здесь.

Не используйте 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.

Войдите, чтобы оставить комментарий