FIG 01 · HERO
Talk to tmux peer agents and generate images — six fixed tools, zero tool-budget creep
One Rust binary gives Claude Code a single outbound layer: message whatever runs in another tmux pane (Codex CLI, another Claude, a shell) and generate or edit images, all through six fixed MCP tools. Drivers plug in underneath, so new channels never inflate your tool listing.
/plugin marketplace add XBlueSky/cc-uplink
/plugin install cc-uplink@cc-uplink$ claude
Transcript: you ask codex, running in the pane next door, to review your diff via channel_send. cc-uplink delivers the request as one injected line in codex's terminal, carrying the exact reply command to run. Codex reviews three files, notes that retry.rs:41 swallows the error cause, and runs the reply command verbatim. The reply — "LGTM — one nit: retry.rs:41 swallows the error cause" — lands back in your own pane, correlation id matching the one you sent.
FIG 02 · SIGNAL JOURNEY
signal journey [4/4]
$ claude > ask codex to review my diff ⏺ channel_send(to: "tmux:%3", message: "review my diff") ⎿ delivered · id:6b3b20e6 · injection verified [reply id:6b3b20e6] LGTM — one nit: retry.rs:41 swallows the error cause
▌idle [uplink from:claude pane:%1 id:6b3b20e6] review my diff (reply: run `tmux send-keys -t %1 -l '[reply id:6b3b20e6] <your answer>' \; send-keys -t %1 Enter`) > reading the envelope… reviewing 3 files > retry.rs:41 swallows the error cause $ tmux send-keys -t %1 -l '[reply id:6b3b20e6] LGTM — one nit: retry.rs:41 swallows the error cause' \; send-keys -t %1 Enter
A peer, in the pane next door
Whatever runs in another tmux pane — the Codex CLI, another Claude, a plain shell — is addressable. You ask in plain words; channel_send wraps it into an envelope. The peer installs nothing.
The notification is the message itself
cc-uplink injects one plaintext line into the peer's terminal — that line IS the notification. Its tail carries the exact command to reply with: a correlation id and a tmux send-keys back to your pane.
The peer answers by running what it was handed
codex does the review, then runs the reply command verbatim. No SDK, no webhook, no queue — the envelope was the protocol.
The reply lands in your pane, not in a queue
The [reply id:…] line arrives in your own pane, correlation id matching the one you sent. channel_recv stays an audit log — nothing to poll, ever.
FIG 03 · IMAGES
Same six tools, a completely different channel
Images go through the same fixed surface: one channel_invoke call. The image:codex driver shells out to Codex CLI's built-in imagegen under your ChatGPT login — no OpenAI API key anywhere — and hands back a file path on disk.
> generate a hero image of a lighthouse for the README ⏺ channel_invoke(channel: "image:codex", op: "generate", args: { prompt: "a lighthouse in watercolour" }) ⎿ codex exec → built-in imagegen (via your ChatGPT login — no API key) ⎿ paths: ["~/project/lighthouse.png"]

FIG 04 · TOOLS
Adding a driver adds zero tools
Drivers plug in underneath a fixed surface. However many ways Claude can reach the outside world, the tool listing stays exactly this long.
- channel_list
- channel_describe
- channel_send
- channel_invoke
- channel_recv
- channel_doctor
fixed surface — 6 tools
- tmux
- image:openai
- image:codex
- …the next one
FIG 05 · DOCS
Read the contracts
The parts most tools hide are the parts this one documents.