Skip to main content
Glama
zzzjjack
by zzzjjack
README.md
# chatgpt-deepseek-bridge

[![CI](https://github.com/zzzjjack/chatgpt-deepseek-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/zzzjjack/chatgpt-deepseek-bridge/actions/workflows/ci.yml)

Let ChatGPT (or any MCP client) delegate coding tasks to a **local
Hermes-backed coding agent**, with async job management so slow model runs
never block the MCP frontend.

This project is **not** an official OpenAI product and is not endorsed by
OpenAI. It uses the official [Secure MCP Tunnel](https://developers.openai.com/docs/guides/chatgpt-mcp)
and [tunnel-client](https://github.com/openai/tunnel-client) as the transport,
and [Hermes Agent](https://hermes-agent.nousresearch.com) as the local agent
runtime. It is tested with DeepSeek models served through OpenCode Go
(`opencode-go` provider), but the model/provider are Hermes configuration
values — other Hermes-supported providers work without code changes.

## What it does

```
ChatGPT (MCP client)
   │  tools: deepseek_investigate / deepseek_execute / deepseek_continue
   │         deepseek_job_status / deepseek_job_result / bridge_status
   ▼
OpenAI Secure MCP Tunnel (tunnel-client daemon)
   ▼
mcp_bridge_server.py  (stdio MCP server)
   ▼
bridge_core.py        (job queue, dedup, single-task lock, git verification)
   ▼
hermes chat -q -Q     (local agent, one-shot invocation)
   ▼
DeepSeek / any Hermes-supported model
```

## Features

- **Async jobs**: `investigate` / `execute` / `continue` return a `job_id`
  in ~1s; Hermes keeps working in the background (5–30 min is normal).
- **Job persistence**: job state and results are stored on disk
  (`state/jobs/<job_id>.json`) — surviving MCP reconnects, tunnel restarts,
  and bridge process restarts.
- **`deepseek_job_status` / `deepseek_job_result`**: local reads only, never
  invoke the model again, zero extra token cost.
- **Idempotency**: identical requests (tool + normalized task + project root)
  within a dedup window reuse the existing job — a retried `execute` never
  runs a second writer agent.
- **Single-task lock** with TTL + dead-process detection; safe queueing.
- **Read-only verification**: `investigate` is checked with git
  before/after snapshots; any change is reported as a `violation`.
- **Safety recovery**: on Hermes session-persistence failures the bridge
  retries automatically — investigate only when verified read-only;
  execute/continue only when no side effects were detected, otherwise it
  spawns a fresh session that completes *only the remaining work*
  (`partial_execution_possible` flag, original session id preserved).
- **Watchdog**: auto-restarts a dead tunnel daemon (~30s).
- **Windows notifications**: tray bubble on job completion/failure (fails
  silently, never affects job results).
- **Environment hardening**: spawns Hermes with validated
  TEMP/TMP/TMPDIR and verifies the Hermes home / session DB are writable.

## Requirements

- Windows (PowerShell 7+; scripts use `pwsh`)
- Python 3.11+
- [Hermes Agent](https://hermes-agent.nousresearch.com) CLI installed and
  configured with a model provider (e.g. DeepSeek via `opencode-go`)
- An OpenAI account with Developer Mode and a Secure MCP Tunnel
  (create at https://platform.openai.com/settings/organization/tunnels)
- A git repository you want the bridge to operate on

## Installation

```powershell
pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\install-chatgpt-deepseek.ps1
```

This creates `.venv`, installs the pinned `mcp==1.28.1` SDK, locates
`hermes.exe` (config → `-HermesPath` → common install locations), and
downloads the official `tunnel-client` release from GitHub. Add
`-SkipTunnelClient` to skip the download (bridge-only setups).

## Configuration

Copy the templates and fill in your values:

```powershell
Copy-Item .env.example .env
Copy-Item config.example.json config.json
```

- `.env` → `CONTROL_PLANE_API_KEY=...` — your tunnel **runtime key**
  (create in the OpenAI tunnels page; this is *not* the tunnel id).
  **Never commit this file.**
- `config.json` →
  - `project_root`: absolute path of the git project the bridge may operate on
  - `hermes_command`: absolute path to `hermes.exe`
  - `model` / `provider`: any Hermes-supported combination
    (defaults are DeepSeek via `opencode-go`)

## Running the tests

Requires Python 3.11+ with only `requirements.txt` installed (see
Installation) and a local `config.json` (see Configuration) whose
`project_root` points at any directory. The stdio compat test
(`tests/test_server_discover_compat.py`) bootstraps a minimal config on a
clean checkout; the bridge_core tests (`test_recovery.py`,
`test_async_flow.py`, `tests/test_git_verification.py`) import
`bridge_core` at load time and need `config.json` to already exist.
`test_async_flow.py` spawns the configured `hermes_command` — point it at
a real agent CLI, or a stub that prints an answer and exits 0.

```powershell
\.venv\Scripts\python.exe tests	est_server_discover_compat.py
\.venv\Scripts\python.exe tests	est_git_verification.py
\.venv\Scripts\python.exe test_recovery.py
\.venv\Scripts\python.exe test_async_flow.py
```

Git must be available on PATH for verification; if it is not, jobs fail with
`error_type: git_verification_failed` instead of silently reporting a
verified run.

## Starting the bridge

```powershell
pwsh -NoProfile -File .\scripts\init-tunnel.ps1 -TunnelId tunnel_xxxxx
pwsh -NoProfile -File .\scripts\start-chatgpt-deepseek.ps1
pwsh -NoProfile -File .\scripts\status-chatgpt-deepseek.ps1
pwsh -NoProfile -File .\scripts\stop-chatgpt-deepseek.ps1
```

`start` launches tunnel-client (which spawns the bridge server) plus a
watchdog that auto-restarts the daemon if it dies. Then, in
ChatGPT → Developer Mode → Plugins, create a developer app and connect it to
your tunnel id. Use **Refresh Tools** after any tool description change.

## MCP tools

| Tool | Purpose |
|---|---|
| `deepseek_investigate(task)` | Starts a background **read-only** investigation job; returns `job_id` in ~1s |
| `deepseek_execute(task)` | Starts a background implementation job (serialized, deduped) |
| `deepseek_continue(session_id, task)` | New background job continuing a previous Hermes session's context |
| `deepseek_job_status(job_id)` | Local status read (`queued` / `running` / `completed` / `failed`) — never invokes the model |
| `deepseek_job_result(job_id)` | Returns the persisted full result (report, git verification, session id, duration) — never invokes the model |
| `bridge_status` | Bridge/tunnel health, lock state, recent jobs |

## Async job workflow (for MCP clients)

1. Call `deepseek_investigate` / `deepseek_execute` / `deepseek_continue` →
   you get `{status: "accepted", job_id, mode}` almost immediately.
2. **Do not poll in a tight loop.** Jobs commonly take 5–30 minutes. Check
   `deepseek_job_status` at most once every 5 minutes; if you cannot defer a
   check that long, return control to the user and tell them the job is
   running in the background. A Windows notification appears when it finishes.
3. Once status is `completed`, call `deepseek_job_result` **once** and stop
   polling. It only reads saved data — no model quota is consumed.
4. `deepseek_continue` is for *new* work on an old session, not for fetching
   a previous job's result.

## Security model / limitations

See [SECURITY.md](SECURITY.md) — read it before deploying.

- This bridge drives a local agent with **shell and filesystem access**.
- `investigate` read-only is enforced by git before/after verification, not
  by an OS sandbox.
- Only point the bridge at projects you trust (agent reads AGENTS.md etc.).
- The tunnel credential in `.env` is high-value — protect it, rotate it,
  never commit it.

## Troubleshooting

- **ChatGPT reports "connection timed out" on a tool call**: the model run
  exceeded the MCP frontend's patience; the job keeps running in the
  background. Wait for the local notification, then call
  `deepseek_job_result` with the `job_id`. The watchdog restores a dead
  daemon within ~30s.
- **`warning: Failed to set cwd to temp dir` in Hermes output**: emitted by
  the uv trampoline when TEMP/TMP is invalid; non-fatal. The bridge now
  sanitizes these variables before spawning Hermes.
- **`hermes_failed` with session persistence errors**: the bridge
  auto-recovers (see Features). If jobs keep failing, run
  `hermes sessions repair` while no Hermes process is running.
- **tunnel-client exits at startup**: make sure `CONTROL_PLANE_API_KEY` in
  `.env` is the runtime key (starts with `sk-proj-`), not the tunnel id
  (starts with `tunnel_`).
- **MSYS/git hangs**: the bridge isolates git through a helper process; if
  you still see hangs, avoid running the bridge from an MSYS shell.

## License

MIT — see [LICENSE](LICENSE). The tunnel-client binary is distributed under
Apache-2.0 by OpenAI (downloaded at install time, not bundled here).

Maintenance

ActivityStale
ResponsivenessNo issues