The problem
Claude Code is multimodal. It can read and reason about images using the Read tool. But when you run Claude Code on a remote server over SSH, there's no way to paste an image from your local clipboard into the session. The clipboard lives on your local machine, while Claude Code runs on the server.
I ran into the same issue with Neovim and built sshimg.nvim to solve it. Now I've built the same thing for Claude Code: claude-ssh-image-skill.
How it works
The solution has three parts:
ccimgd: a daemon running on your local machine that reads PNG images from the clipboard (wl-pasteon Wayland,xclipon X11,pngpasteon macOS) and serves them as base64-encoded JSON over TCP on port 9998ccimg: a client binary on the remote server that connects to the daemon, receives the image, and saves it as a temporary PNG file/paste-image: a Claude Code skill that runsccimgand then uses theReadtool to display the image to Claude
The connection between client and daemon runs through an SSH reverse tunnel:
Local Machine Remote Server (SSH)
┌──────────────────────┐ ┌──────────────────────────┐
│ Clipboard (PNG) │ │ Claude Code │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ ccimgd (Port 9998) │◄────────────────│ /paste-image Skill │
│ - wl-paste │ SSH Reverse │ - TCP Request to ccimgd │
│ - Returns base64 │ Tunnel │ - Receives base64 image │
│ image in response │ (Port 9998) │ - Saves as temp file │
│ │ │ - Read → Claude sees │
└──────────────────────┘ │ the image │
└──────────────────────────┘
Building
Both binaries are written in Go and compile as fully static binaries with no runtime dependencies:
git clone https://github.com/AlexZeitler/claude-ssh-image-skill.git
cd claude-ssh-image-skill
./build.shThis builds statically linked binaries for all supported platforms:
daemon/ccimgd-linux-amd64,daemon/ccimgd-darwin-amd64,daemon/ccimgd-darwin-arm64client/ccimg-linux-amd64,client/ccimg-darwin-amd64,client/ccimg-darwin-arm64
Setup
Local machine (Linux)
cp daemon/ccimgd-linux-amd64 ~/.local/bin/ccimgd
cp daemon/ccimgd.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now ccimgdLocal machine (macOS)
Requires pngpaste for clipboard access:
brew install pngpaste
cp daemon/ccimgd-darwin-arm64 /usr/local/bin/ccimgd # or ccimgd-darwin-amd64 for Intel
cp daemon/com.ccimgd.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.ccimgd.plistRemote server
Copy the client binary and the Claude Code skill:
scp client/ccimg-linux-amd64 your-server:~/.local/bin/ccimg
scp skill/paste-image.md your-server:~/.claude/commands/paste-image.mdTo skip permission prompts, add the Bash permission to ~/.claude/settings.json on the server:
{
"permissions": {
"allow": [
"Bash(ccimg)"
]
}
}SSH connection
Connect with a reverse tunnel so the remote ccimg can reach the local ccimgd:
ssh -R 9998:localhost:9998 your-serverOr add it permanently to ~/.ssh/config:
Host your-server
RemoteForward 9998 localhost:9998
Usage
Copy an image to the clipboard on your local machine, then run this in Claude Code on the remote server:
/paste-image
Claude receives the image and can reason about it: screenshots, diagrams, error messages, whatever you need.
The skill
The skill itself is minimal. It's a Markdown file that tells Claude what to do:
Paste an image from the local clipboard into this session.
Instructions:
1. Run `ccimg` using the Bash tool.
2. Use the Read tool to read the printed file path.
This will display the image to Claude.Claude Code skills are just natural language instructions stored in ~/.claude/commands/. When you invoke /paste-image, Claude follows the instructions: run the client, get the file path, read the image.
The daemon
ccimgd listens on 127.0.0.1:9998 and waits for a TCP connection. When a client connects and sends a newline-terminated message, the daemon reads the clipboard image and responds with a JSON object:
type response struct {
OK bool `json:"ok"`
Image string `json:"image,omitempty"`
Error string `json:"error,omitempty"`
}The Image field contains the base64-encoded PNG data. The daemon auto-detects the platform and uses wl-paste on Wayland, xclip on X11, or pngpaste on macOS.
The client
ccimg connects to 127.0.0.1:9998 (which the SSH reverse tunnel forwards to the local machine), sends {}\n, receives the JSON response, decodes the base64 image, writes it to a temp file, and prints the path to stdout. That's all Claude Code needs to pick it up with Read.
Coexistence with sshimg.nvim
If you also use sshimg.nvim, both tools can run simultaneously. imgd uses port 9999 while ccimgd uses port 9998.
Source
The full source is on GitHub: AlexZeitler/claude-ssh-image-skill