cc-clip
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 32 GitHub stars
Code Fail
- rm -rf — Recursive force deletion command in scripts/install.sh
Permissions Pass
- Permissions — No dangerous permissions requested
This tool bridges your local clipboard with remote servers over SSH, allowing you to paste images directly into Claude Code or Codex CLI. It also forwards remote notifications back to your local machine so you know when a remote task finishes.
Security Assessment
The application accesses your local clipboard data, reads your SSH configuration, and establishes network connections via SSH to your remote servers. It does not request dangerous system permissions or contain hardcoded secrets. However, the automated install script contains a recursive force deletion command (`rm -rf`), which is a standard cleanup mechanism but can be risky if the script's path variables are manipulated. Overall risk: Low.
Quality Assessment
The project is very actively maintained, with its last push happening today. It uses the permissive MIT license and has earned 32 GitHub stars, indicating a small but growing community of users. The repository is well-documented, featuring clear prerequisites and quick-start guides for multiple operating systems.
Verdict
Safe to use, but you should review the install script beforehand if you plan to use the automated setup.
Paste images into remote Claude Code & Codex CLI over SSH — clipboard bridging for macOS and Windows.
cc-clip
Paste images & receive notifications across SSH — remote Claude Code & Codex CLI feel local.
Install → setup → paste. Clipboard works over SSH.
The Problem
When running Claude Code or Codex CLI on a remote server via SSH, image paste often doesn't work and notifications don't reach you. The remote clipboard is empty — no screenshots, no diagrams. And when Claude finishes a task or needs approval, you have no idea unless you're staring at the terminal.
The Solution
Image paste:
Claude Code (macOS): Mac clipboard → cc-clip daemon → SSH tunnel → xclip shim → Claude Code
Claude Code (Windows): Windows clipboard → cc-clip hotkey → SSH/SCP → remote file path → Claude Code
Codex CLI: Mac clipboard → cc-clip daemon → SSH tunnel → x11-bridge/Xvfb → Codex CLI
Notifications:
Claude Code hook → cc-clip-hook → SSH tunnel → local daemon → macOS/cmux notification
Codex notify → cc-clip notify → SSH tunnel → local daemon → macOS/cmux notification
One tool. No changes to Claude Code or Codex. Clipboard and notifications both work over SSH.
Prerequisites
- Local machine: macOS 13+ or Windows 10/11
- Remote server: Linux (amd64 or arm64) accessible via SSH
- SSH config: You must have a Host entry in
~/.ssh/configfor your remote server
If you don't have an SSH config entry yet, add one:
# ~/.ssh/config
Host myserver
HostName 10.0.0.1 # your server's IP or domain
User your-username
IdentityFile ~/.ssh/id_rsa # optional, if using key auth
If you are on Windows and want the SSH/Claude Code workflow, use the dedicated guide:
Quick Start
Step 1: Install cc-clip
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/ShunmeiCho/cc-clip/main/scripts/install.sh | sh
Windows:
Follow the dedicated guide:
On macOS / Linux, add ~/.local/bin to your PATH if prompted:
# Add to your shell profile (~/.zshrc or ~/.bashrc)
export PATH="$HOME/.local/bin:$PATH"
# Reload your shell
source ~/.zshrc # or: source ~/.bashrc
Verify the installation:
cc-clip --version
macOS "killed" error? If you see
zsh: killed cc-clip, macOS Gatekeeper is blocking the binary. Fix:xattr -d com.apple.quarantine ~/.local/bin/cc-clip
Step 2: Setup (one command)
macOS:
# For Claude Code only
cc-clip setup myserver
# For both Claude Code + Codex CLI
cc-clip setup myserver --codex
This single command:
- Installs local dependencies (
pngpaste) - Configures SSH (
RemoteForward,ControlMaster no) - Starts the local daemon (via macOS launchd)
- Deploys the binary and shim to the remote server
If setup reports an error, read the error message carefully — it includes specific instructions for how to fix the issue. For example, if
Xvfbis not found on the remote server and auto-install fails, you will see the exact command to run:# SSH into your server and install manually: ssh myserver sudo apt install xvfb # Debian/Ubuntu sudo dnf install xorg-x11-server-Xvfb # RHEL/FedoraAfter fixing, re-run
cc-clip setup myserver --codex.
Windows:
Use the dedicated guide:
See it in action (Windows)
Step 3: Connect and use
macOS:
Open a new SSH session to your server (the tunnel activates on SSH connection):
ssh myserver
Then use Claude Code or Codex CLI as normal — Ctrl+V now pastes images from your Mac clipboard.
Important: The image paste works through the SSH tunnel. You must connect via
ssh myserver(the host you set up). The tunnel is established on each SSH connection.
Windows:
See:
Verify it works
# Copy an image to your Mac clipboard first (Cmd+Shift+Ctrl+4), then:
cc-clip doctor --host myserver
On Windows, the equivalent quick check is:
Why cc-clip?
| Approach | Works over SSH? | Any terminal? | Image support? | Setup complexity |
|---|---|---|---|---|
| Native Ctrl+V | Local only | Some | Yes | None |
| X11 Forwarding | Yes (slow) | N/A | Yes | Complex |
| OSC 52 clipboard | Partial | Some | Text only | None |
| cc-clip | Yes | Yes | Yes | One command |
How It Works
graph LR
subgraph local ["Local Mac"]
A["Clipboard"] --> B["pngpaste"]
B --> C["cc-clip daemon<br/>127.0.0.1:18339"]
end
subgraph win ["Local Windows"]
J["Clipboard"] --> K["cc-clip hotkey / send"]
end
subgraph remote ["Remote Linux"]
F["Claude Code"] -- "Ctrl+V" --> E["xclip shim"]
E -- "curl" --> D["127.0.0.1:18339"]
K -- "ssh/scp upload" --> L["~/.cache/cc-clip/uploads"]
L -- "paste path" --> F
G["Codex CLI"] -- "Ctrl+V / arboard" --> H["Xvfb CLIPBOARD"]
H -- "SelectionRequest" --> I["x11-bridge"]
I -- "HTTP" --> D
end
C == "SSH RemoteForward" ==> D
style local fill:#1a1a2e,stroke:#e94560,color:#eee
style remote fill:#1a1a2e,stroke:#0f3460,color:#eee
style A fill:#e94560,stroke:#e94560,color:#fff
style F fill:#0f3460,stroke:#0f3460,color:#fff
style G fill:#0f3460,stroke:#0f3460,color:#fff
- macOS Claude path: the local daemon reads your Mac clipboard via
pngpaste, serves images over HTTP on loopback, and the remotexclipshim fetches images through the SSH tunnel - Windows Claude path: the local hotkey reads your Windows clipboard, uploads the image over SSH/SCP, and pastes the remote file path into the active terminal
- Codex CLI path: x11-bridge claims CLIPBOARD ownership on a headless Xvfb, serves images on-demand when Codex reads the clipboard via X11
- Notification path: remote Claude Code hooks and Codex notify pipe events through
cc-clip-hook→ SSH tunnel → local daemon → macOS Notification Center or cmux
SSH Notifications
When Claude Code or Codex CLI runs on a remote server, notifications don't work over SSH — TERM_PROGRAM isn't forwarded, hooks execute on the remote where terminal-notifier doesn't exist, and tmux swallows OSC sequences.
cc-clip solves this by acting as a notification transport bridge: remote hook events travel through the same SSH tunnel used for clipboard, and the local daemon delivers them to your macOS notification system (or cmux if installed).
What you'll see
| Event | Notification | Example |
|---|---|---|
| Claude finishes responding | "Claude stopped" + last message preview | Claude stopped: I've implemented the notification bridge... |
| Claude needs tool approval | "Tool approval needed" + tool name | Tool approval needed: Claude wants to Edit cmd/main.go |
| Codex task completes | "Codex" + completion message | Codex: Added error handling to fetch module |
| Image pasted via Ctrl+V | "cc-clip #N" + fingerprint + dimensions | cc-clip #3: a1b2c3d4 . 1920x1080 . PNG |
| Duplicate image detected | Same as above + duplicate marker | cc-clip #4: Duplicate of #2 |
Image paste notifications help you track what was pasted without leaving your workflow:
- Sequence number (#1, #2, #3...) lets you detect gaps (e.g., #1 → #3 means #2 was lost)
- Duplicate detection alerts when the same image is pasted twice within 5 images
- Click notification to open the full image in Preview.app (macOS, requires
terminal-notifier)
Setup notifications
Step 1: Make sure cc-clip serve is running locally (or use cc-clip service install for auto-start).
Step 2: Configure your remote Claude Code hooks. The easiest way is to ask Claude Code itself to do it. SSH into your server, start Claude Code, and paste this prompt:
Please add cc-clip-hook to my Claude Code hooks configuration. Add it to both Stop and Notification hooks in ~/.claude/settings.json. The command is just "cc-clip-hook" (it's already in PATH at ~/.local/bin/). Keep any existing hooks (like ralph-wiggum) — just append cc-clip-hook alongside them. Show me the diff before and after.
Claude Code will read your current settings.json, add the hooks correctly, and show you the changes.
Alternatively, you can configure manually:
Manual hook configurationEdit ~/.claude/settings.json on the remote server and add cc-clip-hook to the Stop and Notification hook arrays:
{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "cc-clip-hook" }
]
}
],
"Notification": [
{
"hooks": [
{ "type": "command", "command": "cc-clip-hook" }
]
}
]
}
}
If you already have hooks (e.g., ralph-wiggum-stop.sh), add a new entry to the array — don't replace existing ones.
Restart Claude Code after editing (hooks are read at startup).
Step 3 (Codex only): Codex notification is auto-configured by cc-clip connect if ~/.codex/ exists on the remote. No manual steps needed.
Step 4: Generate and register a notification nonce (if you haven't used cc-clip connect):
# On local Mac — generate nonce and write to remote
NONCE=$(openssl rand -hex 32)
curl -s -X POST -H "Authorization: Bearer $(head -1 ~/.cache/cc-clip/session.token)" \
-H "User-Agent: cc-clip/0.1" -H "Content-Type: application/json" \
-d "{\"nonce\":\"$NONCE\"}" http://127.0.0.1:18339/register-nonce
ssh myserver "mkdir -p ~/.cache/cc-clip && echo '$NONCE' > ~/.cache/cc-clip/notify.nonce && chmod 600 ~/.cache/cc-clip/notify.nonce"
Note:
cc-clip connecthandles steps 2-4 automatically. Manual setup is only needed if you use plainsshinstead ofcc-clip connect.
Troubleshooting notifications
Notifications don't appearStep-by-step verification (on the remote server):
# 1. Is the tunnel working?
curl -sf --connect-timeout 2 http://127.0.0.1:18339/health
# Expected: {"status":"ok"}
# 2. Is the hook script the correct version?
grep "curl" ~/.local/bin/cc-clip-hook
# Expected: a curl command with --connect-timeout
# 3. Is the nonce file present?
cat ~/.cache/cc-clip/notify.nonce
# Expected: a 64-character hex string
# 4. Manual test:
echo '{"hook_event_name":"Stop","stop_hook_reason":"stop_at_end_of_turn","last_assistant_message":"test"}' | cc-clip-hook
# Expected: local Mac shows notification popup
# 5. Check health log for failures:
cat ~/.cache/cc-clip/notify-health.log
# If exists: shows timestamps and HTTP error codes
Common issues:
| Problem | Fix |
|---|---|
| Tunnel down (step 1 fails) | Kill stale sshd: sudo kill $(sudo lsof -ti :18339), then reconnect SSH |
| Old hook script (step 2 empty) | Reinstall: cc-clip connect myserver or manually copy the script |
| Missing nonce (step 3 fails) | Register nonce (see Step 4 above) |
| Daemon running old binary | Rebuild (make build) and restart (cc-clip serve) |
Security
| Layer | Protection |
|---|---|
| Network | Loopback only (127.0.0.1) — never exposed |
| Clipboard auth | Bearer token with 30-day sliding expiration (auto-renews on use) |
| Notification auth | Dedicated nonce per-connect session (separate from clipboard token) |
| Token delivery | Via stdin, never in command-line args |
| Notification trust | Hook notifications marked verified; generic JSON shows [unverified] prefix |
| Transparency | Non-image calls pass through to real xclip unchanged |
Daily Usage
After initial setup, your daily workflow is:
# 1. SSH to your server (tunnel activates automatically)
ssh myserver
# 2. Use Claude Code or Codex CLI normally
claude # Claude Code
codex # Codex CLI
# 3. Ctrl+V pastes images from your Mac clipboard
The local daemon runs as a macOS launchd service and starts automatically on login. No need to re-run setup.
Windows workflow
On Windows, some Windows Terminal -> SSH -> tmux -> Claude Code combinations do not trigger the remote xclip path when you press Alt+V or Ctrl+V. cc-clip therefore provides a Windows-native workflow that does not depend on remote clipboard interception.
For first-time setup and day-to-day usage, use:
The Windows workflow uses a dedicated remote-paste hotkey (default: Alt+Shift+V) so it does not collide with local Claude Code's native Alt+V.
Commands
| Command | Description |
|---|---|
cc-clip setup <host> |
Full setup: deps, SSH config, daemon, deploy |
cc-clip setup <host> --codex |
Full setup with Codex CLI support |
cc-clip connect <host> |
Deploy to remote (incremental) |
cc-clip connect <host> --token-only |
Sync token only (fast) |
cc-clip connect <host> --force |
Full redeploy ignoring cache |
cc-clip notify --title T --body B |
Send a notification through the tunnel |
cc-clip notify --from-codex-stdin |
Read Codex JSON from stdin and notify |
cc-clip doctor --host <host> |
End-to-end health check |
cc-clip status |
Show local component status |
cc-clip service install |
Install macOS launchd service |
cc-clip service uninstall |
Remove launchd service |
cc-clip send [<host>] --paste |
Windows: upload clipboard image and paste remote path |
cc-clip hotkey [<host>] |
Windows: register the remote upload/paste hotkey |
| Command | Description |
|---|---|
cc-clip setup <host> |
Full setup: deps, SSH config, daemon, deploy |
cc-clip setup <host> --codex |
Full setup including Codex CLI support |
cc-clip connect <host> |
Deploy to remote (incremental) |
cc-clip connect <host> --codex |
Deploy with Codex support (Xvfb + x11-bridge) |
cc-clip connect <host> --token-only |
Sync token only (fast) |
cc-clip connect <host> --force |
Full redeploy ignoring cache |
cc-clip serve |
Start daemon in foreground |
cc-clip serve --rotate-token |
Start daemon with forced new token |
cc-clip service install |
Install macOS launchd service |
cc-clip service uninstall |
Remove launchd service |
cc-clip service status |
Show service status |
cc-clip send [<host>] |
Upload clipboard image to a remote file |
cc-clip send [<host>] --paste |
Windows: paste the uploaded remote path into the active window |
cc-clip hotkey [<host>] |
Windows: run a background remote-paste hotkey listener |
cc-clip hotkey --enable-autostart |
Windows: start the hotkey listener automatically at login |
cc-clip hotkey --disable-autostart |
Windows: remove hotkey auto-start at login |
cc-clip hotkey --status |
Windows: show hotkey status |
cc-clip hotkey --stop |
Windows: stop the hotkey listener |
cc-clip notify --title T --body B |
Send a generic notification through the tunnel |
cc-clip notify --from-codex "$1" |
Parse Codex JSON arg and notify |
cc-clip notify --from-codex-stdin |
Read Codex JSON from stdin and notify |
cc-clip doctor |
Local health check |
cc-clip doctor --host <host> |
End-to-end health check |
cc-clip status |
Show component status |
cc-clip uninstall |
Remove xclip shim from remote |
cc-clip uninstall --codex |
Remove Codex support (local) |
cc-clip uninstall --codex --host <host> |
Remove Codex support from remote |
Configuration
All settings have sensible defaults. Override via environment variables:
| Setting | Default | Env Var |
|---|---|---|
| Port | 18339 | CC_CLIP_PORT |
| Token TTL | 30d | CC_CLIP_TOKEN_TTL |
| Debug logs | off | CC_CLIP_DEBUG=1 |
| Setting | Default | Env Var |
|---|---|---|
| Port | 18339 | CC_CLIP_PORT |
| Token TTL | 30d | CC_CLIP_TOKEN_TTL |
| Output dir | $XDG_RUNTIME_DIR/claude-images |
CC_CLIP_OUT_DIR |
| Probe timeout | 500ms | CC_CLIP_PROBE_TIMEOUT_MS |
| Fetch timeout | 5000ms | CC_CLIP_FETCH_TIMEOUT_MS |
| Debug logs | off | CC_CLIP_DEBUG=1 |
Platform Support
| Local | Remote | Status |
|---|---|---|
| macOS (Apple Silicon) | Linux (amd64) | Stable |
| macOS (Intel) | Linux (arm64) | Stable |
| Windows 10/11 | Linux (amd64/arm64) | Experimental (send / hotkey) |
Requirements
Local (macOS): macOS 13+ (pngpaste, auto-installed by cc-clip setup)
Local (Windows): Windows 10/11 with PowerShell, ssh, and scp available in PATH
Remote: Linux with xclip, curl, bash, and SSH access. The macOS tunnel/shim path is auto-configured by cc-clip connect; the Windows upload/hotkey path uses SSH/SCP directly.
Remote (Codex --codex): Additionally requires Xvfb. Auto-installed if passwordless sudo is available, otherwise: sudo apt install xvfb (Debian/Ubuntu) or sudo dnf install xorg-x11-server-Xvfb (RHEL/Fedora).
Troubleshooting
# One command to check everything
cc-clip doctor --host myserver
"zsh: killed" after installation
Symptom: Running any cc-clip command immediately shows zsh: killed cc-clip ...
Cause: macOS Gatekeeper blocks unsigned binaries downloaded from the internet.
Fix:
xattr -d com.apple.quarantine ~/.local/bin/cc-clip
Or reinstall (the latest install script handles this automatically):
curl -fsSL https://raw.githubusercontent.com/ShunmeiCho/cc-clip/main/scripts/install.sh | sh
"cc-clip: command not found"
Cause: ~/.local/bin is not in your PATH.
Fix:
# Add to your shell profile
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
Replace ~/.zshrc with ~/.bashrc if you use bash.
Step-by-step verification:
# 1. Local: Is the daemon running?
curl -s http://127.0.0.1:18339/health
# Expected: {"status":"ok"}
# 2. Remote: Is the tunnel forwarding?
ssh myserver "curl -s http://127.0.0.1:18339/health"
# Expected: {"status":"ok"}
# 3. Remote: Is the shim taking priority?
ssh myserver "which xclip"
# Expected: ~/.local/bin/xclip (NOT /usr/bin/xclip)
# 4. Remote: Does the shim intercept correctly?
# (copy an image to Mac clipboard first)
ssh myserver 'CC_CLIP_DEBUG=1 xclip -selection clipboard -t TARGETS -o'
# Expected: image/png
If step 2 fails, you need to open a new SSH connection (the tunnel is established on connect).
If step 3 fails, the PATH fix didn't take effect. Log out and back in, or run: source ~/.bashrc
Symptom: A newly opened SSH tab warns remote port forwarding failed for listen port 18339, and image paste in that tab does nothing.
Cause: cc-clip uses a fixed remote port (18339) for the reverse tunnel. If another SSH session to the same host already owns that port, or a stale sshd child is still holding it, the new tab cannot establish its own tunnel.
Fix:
# Inspect the remote port without opening another forward:
ssh -o ClearAllForwardings=yes myserver "ss -tln | grep 18339 || true"
- If another live SSH tab already owns the tunnel, use that tab/session, or close it before opening a new one.
- If the port is stuck after a disconnect, follow the stale
sshdcleanup steps below. - If you truly need multiple concurrent SSH sessions with image paste, give each host alias a different
cc-clipport instead of sharing18339.
Most common cause: DISPLAY environment variable is empty. You must open a new SSH session after setup — existing sessions don't pick up the updated shell rc file.
Step-by-step verification (run these on the remote server):
# 1. Is DISPLAY set?
echo $DISPLAY
# Expected: 127.0.0.1:0 (or 127.0.0.1:1, etc.)
# If empty → open a NEW SSH session, or run: source ~/.bashrc
# 2. Is the SSH tunnel working?
curl -s http://127.0.0.1:18339/health
# Expected: {"status":"ok"}
# If fails → open a NEW SSH connection (tunnel activates on connect)
# 3. Is Xvfb running?
ps aux | grep Xvfb | grep -v grep
# Expected: a Xvfb process
# If missing → re-run: cc-clip connect myserver --codex --force
# 4. Is x11-bridge running?
ps aux | grep 'cc-clip x11-bridge' | grep -v grep
# Expected: a cc-clip x11-bridge process
# If missing → re-run: cc-clip connect myserver --codex --force
# 5. Does the X11 socket exist?
ls -la /tmp/.X11-unix/
# Expected: X0 file (matching your display number)
# 6. Can xclip read clipboard via X11? (copy an image on Mac first)
xclip -selection clipboard -t TARGETS -o
# Expected: image/png
Common fixes:
| Step fails | Fix |
|---|---|
| Step 1 (DISPLAY empty) | Open a new SSH session. If still empty: source ~/.bashrc |
| Step 2 (tunnel down) | Open a new SSH connection — tunnel is per-connection |
| Steps 3-4 (processes missing) | cc-clip connect myserver --codex --force from local |
| Step 6 (no image/png) | Copy an image on Mac first: Cmd+Shift+Ctrl+4 |
SSH ControlMaster breaks RemoteForwardNote: DISPLAY uses TCP loopback format (
127.0.0.1:N) instead of Unix socket format (:N) because Codex CLI's sandbox blocks access to/tmp/.X11-unix/. If you previously set up cc-clip with an older version, re-runcc-clip connect myserver --codex --forceto update.
Symptom: Tunnel works during connect, but curl http://127.0.0.1:18339/health hangs in your SSH session.
Cause: An existing SSH ControlMaster connection was reused without RemoteForward.
Fix: cc-clip setup auto-configures this. If you set up SSH manually, add to ~/.ssh/config:
Host myserver
ControlMaster no
ControlPath none
Stale sshd process blocks port 18339
Symptom: Warning: remote port forwarding failed for listen port 18339
Fix: Kill the stale process on remote:
sudo ss -tlnp | grep 18339 # find the PID
sudo kill <PID> # kill it
Then reconnect: ssh myserver
Fix: cc-clip connect myserver --token-only
Token uses sliding expiration — auto-renews on every use. Only expires after 30 days of zero activity.
Launchd daemon can't find pngpasteFix: cc-clip service uninstall && cc-clip service install (regenerates plist with correct PATH).
Symptom: cc-clip setup was working before, but now shows zsh: killed when re-running.
Cause: The launchd service is running the old binary. Replacing the binary while the daemon holds it open can cause conflicts.
Fix:
cc-clip service uninstall
curl -fsSL https://raw.githubusercontent.com/ShunmeiCho/cc-clip/main/scripts/install.sh | sh
cc-clip setup myserver
More issues
See Troubleshooting Guide for detailed diagnostics, or run cc-clip doctor --host myserver.
Contributing
Contributions and bug reports welcome. Please open an issue first for major changes.
git clone https://github.com/ShunmeiCho/cc-clip.git
cd cc-clip
make build && make test
Related
Claude Code:
- anthropics/claude-code#5277 — Image paste in SSH sessions
- anthropics/claude-code#29204 — xclip/wl-paste dependency
Codex CLI:
- openai/codex#6974 — Linux: cannot paste image
- openai/codex#6080 — Image pasting issue
- openai/codex#13716 — Clipboard image paste failure on Linux
- openai/codex#7599 — Image clipboard does not work in WSL
Other:
- ghostty-org/ghostty#10517 — SSH image paste discussion
License
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found