cc-clip

skill
Security Audit
Fail
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
Purpose
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.
SUMMARY

Paste images into remote Claude Code & Codex CLI over SSH — clipboard bridging for macOS and Windows.

README.md

cc-clip logo

cc-clip

Paste images & receive notifications across SSH — remote Claude Code & Codex CLI feel local.

Release License: MIT Go Stars

cc-clip demo
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/config for 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:

  1. Installs local dependencies (pngpaste)
  2. Configures SSH (RemoteForward, ControlMaster no)
  3. Starts the local daemon (via macOS launchd)
  4. Deploys the binary and shim to the remote server
See it in action (macOS)

cc-clip macOS demo

If setup reports an error, read the error message carefully — it includes specific instructions for how to fix the issue. For example, if Xvfb is 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/Fedora

After fixing, re-run cc-clip setup myserver --codex.

Windows:

Use the dedicated guide:

See it in action (Windows)

cc-clip Windows demo

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
  1. macOS Claude path: the local daemon reads your Mac clipboard via pngpaste, serves images over HTTP on loopback, and the remote xclip shim fetches images through the SSH tunnel
  2. 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
  3. Codex CLI path: x11-bridge claims CLIPBOARD ownership on a headless Xvfb, serves images on-demand when Codex reads the clipboard via X11
  4. 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 SSHTERM_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 configuration

Edit ~/.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 connect handles steps 2-4 automatically. Manual setup is only needed if you use plain ssh instead of cc-clip connect.

Troubleshooting notifications

Notifications don't appear

Step-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
All commands
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
All settings
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.

Ctrl+V doesn't paste images (Claude Code)

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

New SSH tab says "remote port forwarding failed for listen port 18339"

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 sshd cleanup steps below.
  • If you truly need multiple concurrent SSH sessions with image paste, give each host alias a different cc-clip port instead of sharing 18339.
Ctrl+V doesn't paste images (Codex CLI)

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

Note: 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-run cc-clip connect myserver --codex --force to update.

SSH ControlMaster breaks RemoteForward

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

Token expired after 30+ days of inactivity

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 pngpaste

Fix: cc-clip service uninstall && cc-clip service install (regenerates plist with correct PATH).

Setup fails: "killed" during re-deployment

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:

Codex CLI:

Other:

License

MIT

Reviews (0)

No results found