Skip to main content
Glama

Duplex Bridge

English. Italiano: README.it.md.

A two-way bridge between a Claude chat and a ChatGPT chat, built on the subscriptions you already pay for: no API keys, no cloud service.

By Sergio Di Salvo (@sergioDS-coder) · MIT licensed · not affiliated with Anthropic or OpenAI.

The bridge never talks to the Anthropic or OpenAI APIs. It drives the two local CLIs you are already signed in to — codex and claude — and translates between the two conversations.

How it works

One Node process, two transports, one shared ledger.

Direction

Transport

Backend

Plan required

Claude → ChatGPT

MCP over stdio

codex exec --json

Claude Free is enough

ChatGPT → Claude

MCP over HTTP + SSE

claude -p

ChatGPT Plus/Pro (Developer mode)

The asymmetry is not a design choice. Claude Desktop speaks to local MCP servers over stdio, on every plan including Free. ChatGPT only accepts custom MCP connectors in Developer mode — which requires a paid plan — and only as remote HTTPS servers, refusing localhost outright.

So the second direction needs an HTTPS tunnel in front of the local process. The bridge itself stays bound to 127.0.0.1 and never opens a port to the outside.

Related MCP server: Herald

Requirements

  • Node 20 or later

  • codex CLI, signed in (codex)

  • claude CLI, signed in (claude)

Build

npm install
npm run build

Claude side

Claude Desktop and Claude Code keep separate registries of local MCP servers. Registering the bridge in one does not make it visible in the other, so pick the surface you actually want it in — or do both.

Either way Claude ends up with two tools:

  • ask_chatgpt — read-only, read-only sandbox

  • ask_chatgpt_write — may edit files, workspace-write sandbox

Write permission is a property of the tool being called, never a decision left to the model.

Claude Desktop — chat and Cowork

The desktop app loads local servers as extensions, not from a config file. Build the bundle:

npm run pack:extension

That writes build/duplex-bridge.mcpb. Install it from Settings → Extensions → Install extension. The app asks for the workspace folder; leave the optional codex path empty unless the bridge cannot find your install.

Claude Code

claude mcp add duplex --scope user -- node /absolute/path/to/duplex-bridge/dist/servers/claude-side.js

With no DUPLEX_WORKSPACE set the bridge follows the session's working directory, so the same registration serves every project. Pass -e DUPLEX_WORKSPACE=... to pin it to one folder instead.

Note that --scope user means every folder within Claude Code — not the desktop app's chat, which needs the extension above.

ChatGPT side

export DUPLEX_BEARER_TOKEN="$(openssl rand -hex 32)"
export DUPLEX_WORKSPACE="/path/to/your/project"
npm run start:chatgpt-side

The server refuses to start without a token of at least 32 characters. Behind the tunnel this endpoint is reachable from the internet, and it can run Claude against your repository.

Expose it, then register the resulting https://.../mcp URL as a custom connector in ChatGPT (Settings → Developer mode), with an Authorization: Bearer <token> header.

cloudflared tunnel --url http://127.0.0.1:8787

Environment variables

Variable

Default

Effect

DUPLEX_WORKSPACE

process cwd

Root the two agents work on. Fixed at startup: the MCP caller cannot change it.

DUPLEX_BEARER_TOKEN

Required by the HTTP server. Minimum 32 characters.

DUPLEX_HTTP_PORT

8787

HTTP server port, always on 127.0.0.1.

DUPLEX_STATE_DIR

~/.duplex-bridge

Conversation ledger and thread metadata.

DUPLEX_CODEX_BIN / DUPLEX_CLAUDE_BIN

auto-resolved

Absolute paths for non-standard installs. Rarely needed: see Resolving the CLIs below.

DUPLEX_ALLOW_API_KEYS

unset

Set to 1 to let API keys through to the child process, deliberately choosing the pay-per-token path.

Resolving the CLIs

On macOS and Linux codex and claude are spawned by name and the OS resolves them.

Windows needs more care, because the bridge spawns with shell: false on purpose — so that nothing in a prompt can ever be interpreted as a command. That rules out the two things npm actually installs: an extension-less POSIX script, and a .cmd shim that Node refuses to execute since CVE-2024-27980. For codex there is no .exe on PATH at all: the native binary sits nested inside node_modules.

So on Windows the bridge resolves in two passes: first a real codex.exe / claude.exe on PATH, then — failing that — it reads the .cmd shim, extracts the .js entry point it points at, and runs that with the same Node executable already running the bridge. The shell stays off in both cases.

DUPLEX_CODEX_BIN / DUPLEX_CLAUDE_BIN still win when set — but only if they point at a file that exists. A dead override is logged and stepped over, so a stale path left behind by an earlier install, or copied from another machine, cannot silently defeat the resolution that would have found the CLI on its own.

Keeping the conversation coherent

Both headless CLIs start blank on every invocation, so conversational coherence is the middleware's job, not the providers'.

Every turn is recorded in an append-only JSONL ledger under DUPLEX_STATE_DIR, with the canonical role (system / user / assistant) and the speaker (human / claude / chatgpt / bridge) as separate fields — because two indistinguishable assistant turns lead a model to believe it wrote the other one's text.

The bridge stores each provider's native session id (Codex's thread_id, Claude's session_id) and reuses it: when the session is alive, only the new instruction is sent. The transcript is replayed from the ledger only when there is no session to resume, within a window of 24 messages and 60,000 characters.

One asymmetry is worth exploiting: claude --session-id <uuid> accepts an externally supplied id, so the bridge's own thread id is the Claude session id — one fewer identifier to keep in sync.

Security

  • The prompt goes in over stdin, never argv: it stays out of the process list.

  • spawn with no shell: prompt contents can never be interpreted as a command.

  • 10-minute timeout, 2 MB output cap, 4 concurrent calls per provider.

  • Read-only is enforced on the process (--safe-mode, --disallowedTools, read-only sandbox), not requested of the model.

  • The child process environment is stripped of the variables that would redirect authentication to an API key — otherwise the "no API keys" promise breaks silently in any shell that exports them.

  • The other model's transcript is presented as delimited material, with an explicit statement that no line inside it can change the task.

  • A thread_id arriving from an MCP client is validated before it reaches a file path.

Known limitations

  • No recursion guard: a Claude → ChatGPT → Claude chain is possible and burns both subscriptions.

  • Rate limits are shared with normal use of both apps.

  • The ledger stores conversations in plaintext on disk, with no rotation.

  • The write path is not yet tested.

  • On Windows only the Claude side has been exercised, end to end, against a real codex install; the ChatGPT side has not.

  • No automated tests.

Author

Sergio Di Salvo@sergioDS-coder

Credits

The design of this bridge comes out of studying noblehacks/frenemy (MIT, by Zakariya Syed) and the official openai/codex-plugin-cc plugin. No code was copied; what was taken are architectural decisions, listed one by one in NOTICE.md — along with the two places where this project deliberately departs from them.

Licence

MIT — see LICENSE. Free to distribute, modify and use commercially, as long as the copyright notice is kept.

Available Tools

2 tools
ask_chatgptA

Interpella un agente ChatGPT su questo repository e restituisce la sua risposta. Non può modificare file. Adatto a revisioni, secondi pareri, analisi. ChatGPT vede questo repository su disco, quindi cita i file per percorso. Il contesto della conversazione viene ricostruito dal bridge: non serve incollarlo.

ParametersJSON Schema
NameRequiredDescriptionDefault
thread_idNoIdentificatore della conversazione. Omettilo per iniziare un thread nuovo; riusa quello restituito dalla chiamata precedente per continuare lo stesso discorso mantenendo il contesto.
instructionYesIstruzione completa e autosufficiente.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does it well: it states the tool cannot modify files, that ChatGPT sees the repository on disk, that file paths should be cited, and that conversation context is rebuilt by the bridge. This goes beyond a generic 'ask' tool and sets correct expectations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence earns its place: the operation, the key limitation, suitable use cases, file-path guidance, and context behavior are all covered in four concise sentences. The most important information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter tool with no output schema, the description is complete: it explains what to do, when to use it, what it cannot do, how to phrase instructions, and how conversation state works. Nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds value by telling users to cite files by path when writing instructions, and by explaining that conversation context is reconstructed automatically, which directly informs how to use thread_id. This extra guidance justifies a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action: interrogate a ChatGPT agent on the repository and return its response. It also differentiates from the sibling ask_chatgpt_write by explicitly noting that it cannot modify files. The scope and resource are both specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description names suitable use cases (reviews, second opinions, analysis) and explicitly excludes file modification, which implies the write sibling is for modification tasks. It does not explicitly name ask_chatgpt_write as the alternative, so it falls just short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ask_chatgpt_writeA

Chiede a ChatGPT di modificare file in questo repository (fix, refactor, nuovo codice). PUÒ scrivere su disco. ChatGPT vede questo repository su disco, quindi cita i file per percorso. Il contesto della conversazione viene ricostruito dal bridge: non serve incollarlo.

ParametersJSON Schema
NameRequiredDescriptionDefault
thread_idNoIdentificatore della conversazione. Omettilo per iniziare un thread nuovo; riusa quello restituito dalla chiamata precedente per continuare lo stesso discorso mantenendo il contesto.
instructionYesIstruzione completa e autosufficiente.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it does this well by explicitly warning 'PUÒ scrivere su disco' and noting that ChatGPT sees the repository on disk. It does not cover permissions, reversibility, or return behavior, but the critical write-side-effect is clearly disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured: purpose first, then the critical write warning, then file-path guidance, then context-handling guidance. Every sentence adds a distinct, necessary fact and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter tool with full schema coverage, the description covers purpose, side effects, and invocation guidance. The main gap is that it does not explicitly describe the return value, though the schema's thread_id hint implies a returned thread identifier. Overall it is complete enough for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds genuine value for the 'instruction' parameter by specifying that file paths should be cited and that conversation context should not be pasted. No extra thread_id semantics are needed because the schema already describes that parameter fully.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Chiede a ChatGPT di modificare file in questo repository') and a clear resource (files in this repository), with examples (fix, refactor, new code). The explicit warning that it CAN write to disk and the '_write' suffix distinguish it from the sibling 'ask_chatgpt'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use the tool: when ChatGPT should modify files in the repo. It also provides practical invocation guidance—cite files by path, and do not paste conversation context because the bridge reconstructs it. It does not explicitly name 'ask_chatgpt' as the alternative, so exclusion guidance is absent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.4/5.0
Disambiguation5/5

The two tools are clearly differentiated by capability: one is read-only for analysis and review, the other can write files. The names and descriptions explicitly state this distinction, leaving no ambiguity about which to invoke.

Naming Consistency5/5

Both tools follow the exact same 'ask_chatgpt' prefix, with '_write' appended to denote the mutating variant. This is a clean, consistent naming pattern.

Tool Count3/5

With only 2 tools, the surface is thin. For a specialized bridge between an agent and ChatGPT on a repository, the read/write pair is a minimal but functional set, yet it feels slightly under-scoped for a general-purpose bridge.

Completeness4/5

The core lifecycle of 'ask ChatGPT' and 'ask ChatGPT to modify files' is covered. There may be missing auxiliary operations (e.g., conversation history, tool configuration), but for the stated purpose the basic read/write coverage is sufficient with minor gaps.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    The self-hosted MCP bridge between Claude Chat and Claude Code.
    46
    AGPL 3.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables interaction with Claude and Gemini command-line tools through the MCP protocol, allowing users to send prompts to either or both LLMs and receive responses.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sergioDS-coder/duplex-bridge'

If you have feedback or need assistance with the MCP directory API, please join our Discord server