ADOS Bridge
Provides an optional Cloudflare quick tunnel to publish the bridge's local MCP endpoint over public HTTPS so ChatGPT connectors can reach it, with the tunnelled URL printed in the startup setup guide.
Provides read-only Git evidence tools (opencode_git_status, opencode_git_diff) that run local Git inside the authorized repository only, reporting branch, ahead/behind status, staged/modified/untracked/deleted/renamed/conflicted files, and diffs — with explicit arguments, no shell, no index mutation, and a credential-free environment.
Provides a Tailscale Funnel flow for exposing the bridge's MCP endpoint publicly over HTTPS, enabled by default by the init command and run as a background service on port 10000.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ADOS Bridgeopen a session in my repo and show the current git status"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ADOS Bridge
Supervised local execution: drive your own OpenCode from ChatGPT. ADOS Bridge exposes a local OpenCode session to ChatGPT over a secure MCP connection, so the model can read, edit and report inside your own repositories while every destructive step stays behind an explicit human decision.
Developed and maintained by Matías Valdebenito Quezada.
Provenance: ADOS Bridge is based on opencode-chatgpt-bridge by yuga-hashimoto, and remains MIT-licensed. The original copyright notice is preserved in LICENSE; attribution and modification details are in NOTICE. No ownership of the upstream code is claimed here. The npm package name and CLI binary remain
opencode-chatgpt-bridge.
If ADOS Bridge is useful to you, a star on the repository is the simplest way to help others find it.
Features
What is implemented and shipped in V1:
OpenCode session lifecycle — start/stop a per-repository
opencode serve, create and list sessions, send prompts (sync or async), fetch messages and diffs, abort. Streamable HTTP MCP at/mcpfor ChatGPT connectors and other MCP clients; the session map persists in~/.opencode-chatgpt-bridge/sessions.json; agents, slash commands, and provider/model diagnostics are exposed throughopencode_capabilities.Operational state —
opencode_statederivesidle/busy/waiting-human/stalled/errorfrom session status, pending interventions, recent messages, and a bounded SSE reader per managed server, instead of a raw busy flag.Human interventions and checkpoints — pending permissions and questions are listed and answered explicitly (
opencode_list_interventions,opencode_respond_permission,opencode_answer_question). Destructive tools return a checkpoint and only run withconfirmCheckpoint=true(default on), and the bridge never picks an answer for you.Idempotent messaging —
opencode_send_messageaccepts amessageID, so an ambiguous send can be retried without creating a second prompt; the bridge never retries a POST automatically.Read-only Git evidence —
opencode_git_statusandopencode_git_diffrun local Git inside the authorized repo only (explicit args, no shell, no index mutation, credential-free environment) for branch, ahead/behind, staged/modified/untracked/deleted/renamed/conflicted files, and diffs.Allowlisted repositories and security boundaries — roots are realpath-checked against
OPENCODE_BRIDGE_ALLOWED_ROOTSand re-validated on every call (deny by default), file reads stay inside the session repository, the bridge listens on127.0.0.1, the bearer token is masked everywhere except the explicitshow-token, and.env/ state files are written 0600 with atomic writes. This is an authorization boundary, not a sandbox — read the Security model.Operations and tunneling — optional Cloudflare quick tunnel, Tailscale Funnel flow, macOS LaunchAgent background mode,
doctordiagnostics, and a printed setup guide on every start.Engineering — TypeScript, strict typecheck, unit tests,
pnpm run validate.
Related MCP server: CodexPro
Architecture
flowchart LR
CG["ChatGPT / ChatGPT Mobile"] -->|"Secure MCP connector (/mcp)"| TUN["Secure MCP tunnel<br/>Cloudflare / Tailscale Funnel"]
TUN -->|"HTTPS + bearer token"| BR["ADOS Bridge<br/>127.0.0.1:8790<br/>allowlist, checkpoints, state"]
BR -->|"opencode serve (127.0.0.1, per repo)"| OC["OpenCode"]
OC --> REPO["Local repository<br/>(allowlisted root)"]
REPO -.->|"future / optional integration"| POS["ADOS Project OS<br/>roadmap - not part of V1"]Forward path:
ChatGPT / ChatGPT Mobile
-> Secure MCP (secure tunnel connector /mcp)
-> ADOS Bridge (opencode-chatgpt-bridge, 127.0.0.1:8790)
-> local opencode serve (127.0.0.1, per authorized repo)
-> local repositoryReturn path:
local repository
-> opencode (edits, diff, status)
-> ADOS Bridge (tool results, state, Git evidence)
-> Secure MCP
-> ChatGPTOnly the bridge is reachable from outside; OpenCode and the repository stay local. ADOS Project OS is shown as a future/optional integration and is not part of this release.
This project is intentionally separate from ADOS Project OS. It is focused only on the ChatGPT <-> opencode bridge use case. It is not a global project administration layer: it does not track portfolio/progress, priorities, global governance, ActionIntent, or cross-project decisions (see V1 scope).
Quick start
Requires Node.js 20+, pnpm 10+, and an authenticated opencode — see Requirements.
git clone https://github.com/matiasgonzalovq/ados-bridge.git
cd ados-bridge
pnpm install
pnpm run build
pnpm run init -- --allowed-roots /path/to/your/repos
pnpm startinit creates a ready-to-use .env with a random bridge token, your allowed repo roots, automatic port fallback, and Tailscale Funnel enabled by default.
When the bridge starts, it prints a setup guide with:
local health URL
local MCP URL
automatic fallback port when the preferred port is already in use
public HTTPS MCP URL when tunnel is enabled; Tailscale background service defaults to port 10000 to avoid conflicting with 443 and other active Funnel listeners
ChatGPT settings link
connector name, description, and URL to paste
header auth (preferred); the token itself is always masked
opencode CLI status and setup notes
first ChatGPT prompt to try
Requirements
Node.js 20+
pnpm 10+
opencodeinstalled and authenticated locallycloudflaredfor the default tunnel flow, or your own HTTPS tunnel
Check opencode:
opencode --version
opencodeRun opencode once inside a repo and make sure your model provider is configured before expecting ChatGPT to drive it. The bridge starts opencode serve for each repo and talks to it over HTTP. After creating a bridge session, call opencode_capabilities from ChatGPT to inspect connected providers, available auth methods, and default model configuration.
Commands
opencode-chatgpt-bridge init --allowed-roots /path/to/repos
opencode-chatgpt-bridge start
opencode-chatgpt-bridge doctor
opencode-chatgpt-bridge show-tokenFrom source, use pnpm:
pnpm run init -- --allowed-roots /path/to/repos
pnpm start
pnpm run doctor
node dist/cli.js show-tokenshow-token is the only command that prints the raw bridge token. start, doctor,
status output, and logs always show a masked value.
Background mode on macOS
You do not need to keep a terminal open. Install the bridge as a user LaunchAgent:
pnpm run build
pnpm run install-service
pnpm run service-statusLogs are written to:
~/.opencode-chatgpt-bridge/bridge.log
~/.opencode-chatgpt-bridge/bridge.err.logTo stop background mode:
pnpm run uninstall-serviceThe service uses this repository directory as its working directory, loads .env, starts the bridge, and keeps it alive after login.
ChatGPT setup
Open ChatGPT Web and go to:
https://chatgpt.com/#settings/ConnectorsManual path:
Settings -> Apps & Connectors -> Advanced settings -> enable Developer mode
Settings -> Connectors -> CreateUse the values printed by the bridge. They look like this:
Connector name: opencode local bridge
Description: Control local opencode sessions, inspect diffs, and manage local coding tasks.
Connector URL: https://example.trycloudflare.com/mcp
Bearer token: abcd…wxyz (masked)The token is never printed in full by start or doctor.
If your ChatGPT connector UI supports auth headers, use the plain /mcp URL and set:
Authorization: Bearer <OPENCODE_BRIDGE_TOKEN>Header auth is the preferred mode. If your connector cannot send headers, print the URL-token variants explicitly and append them to the MCP URL:
opencode-chatgpt-bridge show-token
# then use <connector-url>/<token> or <connector-url>?token=<token>Once linked on ChatGPT Web, the connector should be available in ChatGPT mobile apps as well.
opencode setup
No extra opencode project configuration is required by the bridge, but opencode itself must be usable locally.
The bridge starts opencode like this:
OPENCODE_SERVER_USERNAME=opencode \
OPENCODE_SERVER_PASSWORD=<generated-or-env-password> \
opencode serve --hostname 127.0.0.1 --port <auto>Notes:
If
OPENCODE_SERVER_PASSWORDis not set, the bridge generates a random password per managed opencode server.If
OPENCODE_BASE_URLis set, the bridge uses that existing opencode server instead of spawning one.The opencode server is kept on
127.0.0.1; only the bridge is exposed to ChatGPT.Provider/model login is handled by opencode. Run
opencodein an opencode TUI first (not the bridge) and confirm it can answer/edit before using ChatGPT.
Attaching the opencode TUI (manual debugging)
The bridge never opens an opencode TUI and never attaches one automatically. For a manual look at a server the bridge manages, run this yourself in a terminal:
opencode attach http://127.0.0.1:<port> -u <username> -p <password>
# or credentials from the environment:
OPENCODE_SERVER_USERNAME=opencode OPENCODE_SERVER_PASSWORD=<password> opencode attach http://127.0.0.1:<port>Verified locally against opencode 1.18.30: attaching works and shows the live transcript, but it
needs an interactive TTY (it fails when run without one) and the credentials must be supplied
explicitly, otherwise it reports 401 Unauthorized. Treat this as a human debugging step only.
MCP tools
Bridge and project tools
bridge_health- inspect bridge config, managed opencode processes, event observers, and the stalled threshold.list_projects- list Git repos under the allowed roots.
opencode process/session tools
opencode_start- start or reuse anopencode serveprocess for a repo.opencode_stop- stop one or all managed opencode servers.opencode_create_session- create a new opencode session and return abridgeSessionId.opencode_list_sessions- list bridge sessions known to this bridge.opencode_get_session_status- poll status for a session.opencode_send_message- send a prompt; defaults to async mode; accepts an optionalmessageIDfor idempotent retries.opencode_get_messages- fetch session transcript/messages.opencode_get_diff- fetch file diffs for a session.opencode_abort- abort a running session.opencode_respond_permission- respond to opencode permission prompts.opencode_state- derived operational state, pending interventions, activity, and last error for one session.opencode_list_interventions- pending opencode permissions and questions for one session.opencode_answer_question- answer a pending opencode question (checkpointed).
project inspection tools
opencode_read_file- read a file through opencode.opencode_find_files- fuzzy-find files through opencode.opencode_vcs_status- get VCS and file status.opencode_git_status- read-only Git evidence: branch, clean/staged/untracked/deleted/renamed/conflicted lists, ahead/behind.opencode_git_diff- read-only Git evidence: unstaged + staged diffs and untracked-file evidence (never viagit add).opencode_capabilities- list opencode agents, slash commands, providers, auth methods, and default model config.
Operational state
opencode_get_session_status reports what opencode itself says (idle, busy, retry). That is
not enough on its own: a session waiting for a human answer reports busy, and a session stuck
mid-run reports busy forever. Call opencode_state for the operational answer:
State | Meaning |
| Nothing pending and opencode is not busy. |
| opencode is busy and there is recent activity (or no evidence of inactivity). |
| A permission or question is pending and needs an explicit human choice. |
| opencode is busy but nothing has been observed for longer than the threshold. |
| The newest observed signal is an error (message error or |
Derivation order: pending intervention wins, then a fresh error, then busy vs stalled, else idle.
Signals come from GET /session/status, pending permissions/questions, the last 10 messages, and
the observed event stream. Fields the bridge cannot observe are null or []; the report never
invents an operation, an error, or a repository authorization.
Configuration:
OPENCODE_BRIDGE_STALLED_MS=120000 # or --stalled-ms 120000; floor 5000Event stream observation
The bridge opens one SSE reader per managed opencode server (GET /event, basic auth), lazily on
the first opencode_state call, and stops it when that server stops or the bridge shuts down.
Readers reconnect with capped backoff, keep at most 50 sessions x 10 events, and only ever feed
operational state: no history, no control channel, nothing that can block a prompt. Server-level
events (heartbeats, plugins) never count as session activity.
Interventions
opencode_list_interventions polls GET /permission and GET /question and filters them to the
session, so polling still works when the bridge attached to the event stream late.
opencode_answer_question posts the chosen labels (answers holds one array of option labels per
question) and is checkpointed: it returns a destructive checkpoint first and only runs with
confirmCheckpoint=true. Answering is always an explicit human choice; the bridge never picks an
option itself. An unknown question id surfaces opencode's 404 as an error, without retrying.
Prompt idempotency
opencode_send_message accepts an optional messageID. When you retry after an ambiguous failure
(network error, timeout), resend with the same value: opencode dedupes prompt creation on that
id, and the bridge additionally checks whether the message already exists and returns
duplicate: true instead of sending again. The bridge never retries an ambiguous POST on its own.
The result also reports stateTouch, which says whether the bridge's own bookkeeping after the
send worked; a failed stateTouch never means the prompt was rejected.
Git evidence (read-only)
opencode_git_diff and opencode_get_diff can differ, and neither shows untracked files. For real,
trustworthy repository evidence call opencode_git_status and opencode_git_diff. Both run local Git
inside the authorized session repo only (the repoPath re-validated by the current allowedRoots on
every call) and never mutate the index or working tree. Git is spawned with execFile (no shell),
explicit arguments, a 10s timeout, bounded output, and a credential-free environment
(GIT_TERMINAL_PROMPT=0, GIT_OPTIONAL_LOCKS=0), so the evidence reads never even take optional locks.
opencode_git_status- branch, head, upstream, ahead/behind,clean, and the staged / modified / untracked / deleted / renamed / conflicted file lists pluscommitPending/pushPending.opencode_git_diff- unstaged changes (git diff), staged changes (git diff --cached), and evidence for untracked files (detected withgit ls-files --others --exclude-standard, shown viagit diff --no-index -- /dev/null <path>— nogit addis ever run).
Untracked evidence is containment-checked: a file is only read when it resolves strictly inside the
work-tree root; symlinks are never followed (an escape or symlink is reported with a note instead of
its content). A non-Git repo returns a structured NOT_A_GIT_REPO error rather than crashing.
Example ChatGPT prompt
Use opencode local bridge. First call bridge_health and list_projects.
Then create a session for /path/to/repos/my-repo,
ask opencode to fix the README, poll status, and show opencode_get_diff.Security model
This bridge can cause local code modifications through opencode. Treat it as a local automation gateway.
Default protections:
opencodeitself is bound to127.0.0.1.Repositories must be under
OPENCODE_BRIDGE_ALLOWED_ROOTS, and every bridge session is re-checked against the roots active right now (deny by default, stale sessions cannot bypass it).Bearer-token authentication with header auth preferred; the raw token is masked in
start,doctor, and status output and is printed only by the explicitshow-tokencommand..envand the state file are written 0600 (owner-only);doctorwarns if.envis wider.Destructive tools return a checkpoint (
isError: true) and only proceed withconfirmCheckpoint=truewhen checkpoints are enabled (default on;OPENCODE_BRIDGE_CHECKPOINTS=falseor--checkpoints falsedisables). That coversopencode_stop,opencode_abort,opencode_respond_permission, andopencode_answer_question.No POST is retried automatically, so an ambiguous send can never silently create a second prompt; retries are explicit and keyed by
messageID.File reads are always contained to the session repository; there is no escape hatch.
No arbitrary shell execution tool is exposed by this bridge.
Diffs are first-class so clients can inspect changes before committing.
Strongly recommended:
Always set
OPENCODE_BRIDGE_TOKENbefore exposing through any tunnel.Do not bind the bridge to
0.0.0.0unless you know exactly what network can reach it.Keep
OPENCODE_BRIDGE_ALLOWED_ROOTSnarrow.Review
opencode_get_diffbefore committing or pushing generated changes.
Development
pnpm install
pnpm run typecheck # src/ and tests/ (tsconfig.json + tsconfig.test.json)
pnpm test
pnpm run build
pnpm run validateV1 scope
Capabilities
Project allowlist: repositories are gated by
OPENCODE_BRIDGE_ALLOWED_ROOTS(realpath-checked) and re-validated on every call; a stale or unauthorized session is denied by default.Bridge/OpenCode sessions: one
bridgeSessionIdper OpenCode session, persisted in~/.opencode-chatgpt-bridge/sessions.json.Sending instructions:
opencode_send_message(sync or async) with an optionalmessageIDfor idempotent retries (the bridge and OpenCode both dedupe on it; no ambiguous POST is retried automatically).Operational state:
opencode_statederivesidle / busy / waiting-human / stalled / errorfrom session status, pending permissions/questions, recent messages, and the event stream.SSE / events: one bounded SSE reader per managed server (
GET /event) for operational observation.waiting-human: surfaced when a permission or question is pending and needs an explicit human choice.
Permissions & questions:
opencode_list_interventionslists them;opencode_respond_permissionandopencode_answer_questionanswer them behind a destructive checkpoint.Abort:
opencode_abortstops a running session (checkpointed).Checkpoints: destructive tools require
confirmCheckpoint=true(default on).Git status / diff evidence (read-only):
opencode_git_statusandopencode_git_diffread local Git inside the authorized repo — branch, ahead/behind, clean, staged/modified/untracked/deleted/renamed/ conflicted, plus unstaged + staged diffs and untracked-file evidence (never viagit add).Containment: Git runs in the authorized repo only; untracked file content is read only when it resolves inside the repo; symlinks are never followed; commands use explicit args (no shell).
Structured errors: MCP failures return
isError: truewith an actionable message (e.g.NOT_A_GIT_REPO,Unknown bridge session).Secure MCP: the bridge runs on
127.0.0.1; ChatGPT reaches/mcpthrough the Secure MCP connector.OpenCode attach for manual debugging:
opencode attach <url> -u <user> -p <pass>in a terminal (interactive TTY; credentials required).
Explicitly out of scope (belongs to ADOS Project OS)
Mission Control, portfolio / global progress, priorities, or recommendations.
Global governance and cross-project administration.
ActionIntent from Project OS and product decisions.
Managing or scheduling work across repositories.
ADOS Bridge V1 is the execution + evidence layer for a single local project; it deliberately does not decide what to work on or across projects.
Known limitations
Question / intervention E2E: a real E2E was successfully verified: native OpenCode question -> opencode_state waiting-human -> explicit human approval in ChatGPT -> checkpoint -> opencode_answer_question -> OpenCode resumes -> session.idle. The
waiting-humanflow is covered by unit tests and surfaced correctly byopencode_statein runtime.Unstaged renames:
opencode_git_diffmay present an unstaged rename as delete + add (no-M), whileopencode_git_statusreports it correctly as a rename.Rare path quoting: paths with unusual characters may need more robust unquoting in
git diffevidence;core.quotepath=falseis applied via the Git environment.Very large repositories: status parsing caps entries (5000) and diff evidence caps files/patch size; evidence is operational and may not be exhaustive for huge repos.
Roadmap
Direction of travel for ADOS Bridge. Nothing in this section is shipped — it is planning only, and items may change or be dropped:
Secure tunnel autostart hardening — make tunnel bring-up reliable across reboot/login (retry and health checks for the Cloudflare quick tunnel, cleaner Tailscale Funnel lifecycle, fail-loud startup when the public endpoint is not ready).
ADOS Project OS integration — an optional layer that connects this V1 execution + evidence runtime to Project OS (mission control, priorities, ActionIntent). Explicitly out of scope for V1; ADOS Bridge stays usable standalone without it.
Packaging and onboarding — a smoother install path (published package / single-command install), guided first-run onboarding, and a CI validation workflow running
pnpm run validateon every push.
Track progress in Issues; released versions are tagged on the Tags page.
Contributing
Contributions are welcome — see CONTRIBUTING.md for the short workflow
(fork, branch, pnpm run validate, focused pull request).
Before you start, note two non-negotiables:
Never commit secrets. No
.env, tokens, private keys, or credentials —.envis gitignored and only.env.exampleis tracked.Security reports do not belong in issues or pull requests. Follow SECURITY.md.
Security
See SECURITY.md for responsible disclosure guidance. In short: use GitHub private vulnerability reporting if it is enabled for this repository; otherwise open a minimal, non-sensitive issue asking for a private contact path. Never post secrets, tokens, or exploit details publicly.
License
MIT — see LICENSE. The original MIT license text and
Copyright (c) 2026 yuga-hashimoto are preserved exactly as provided upstream.
ADOS Bridge is based on opencode-chatgpt-bridge
by yuga-hashimoto. Modifications and additional ADOS Bridge development are
Copyright (c) 2026 Matías Valdebenito Quezada, released under the same MIT terms.
See NOTICE for the full attribution statement.
This server cannot be deployed
Maintenance
Related MCP Connectors
Use your Mac, Windows or Linux computer from ChatGPT, Claude or Codex: files, commands, documents.
Authenticated MCP Agent (Openai)
The OpenRouter MCP server plugs OpenRouter into the AI tools you already use. Once connected, your assistant can pull live OpenRouter data (models, prices, your credits, rankings, and docs) and send quick test messages, all without leaving your editor.
MCP connector that lets ChatGPT list, search, and run your Apple Shortcuts via a local Mac agent
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceA local MCP bridge that lets ChatGPT control opencode sessions for code modification, file reading, and repository management on your own computer.2MIT
- AlicenseAqualityCmaintenanceEnables ChatGPT Developer Mode to operate as a local coding agent by connecting to your own repository through MCP, allowing it to read, write, search, edit files, run safe verification commands, and manage handoff context for continued work.15198 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables ChatGPT to read and edit local project files, inspect Git changes, and run approved development scripts through a secure MCP tunnel, with optional Codex Desktop integration.109 npm4Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables ChatGPT to operate authorized local projects through MCP, including reading and modifying code, viewing Git changes, running configured programs, and reading local Codex sessions to continue work.63 npm6MIT