cliptunnel-mcp
Operate a locked-down remote machine through its clipboard by tunneling shell and file commands over a shared clipboard slot, exposing them as MCP tools like remote_shell and remote_fs_*.
README
cliptunnel-mcp
Operate a locked-down remote machine through its clipboard.
What it does
cliptunnel-mcp turns a shared clipboard into a reliable control channel between two machines. When the remote machine sits behind a Citrix session, a locked-down VDI, or any environment that blocks SSH, file transfer, and networking but still exposes a clipboard, ClipTunnel tunnels commands through that single slot and exposes them as Model Context Protocol tools.
The package ships three layers:
- Protocol — a wire format (
CT1) with base64 payloads, sequence numbers, and typed messages (command, response, error, ack). - Endpoints —
Controller(operator side) andAgent(remote side), connected by an injectedTransport. Both run background threads with ARQ retransmission, sequence-bound deduplication, and generation-safe lifecycle. - MCP server — a FastMCP application that exposes the Controller's helpers as
remote_shell,remote_fs_*,remote_upload,remote_download, andremote_sysinfotools over stdio.
The core package has zero dependencies. The MCP server requires the optional [server] extra (mcp>=1.2,<2).
Architecture
Both endpoints share a single last-writer-wins clipboard slot. The protocol uses stop-and-wait ARQ: the Controller writes one command, the Agent ACKs immediately, processes the command in a worker pool, then writes one typed response (R or E) and retransmits it until the Controller's matching ACK arrives. The Controller sends one command at a time and resolves futures as responses come back.
Wire format
CT1|<from>|<to>|<seq>|<type>|<payload>
| Field | Value |
|---|---|
CT1 |
Protocol signature + version |
from |
C (Controller) or A (Agent) |
to |
C or A |
seq |
Positive integer, monotonic per Controller session |
type |
C (command), R (response), E (error), A (ack) |
payload |
Base64-encoded UTF-8 |
Installation
pip install cliptunnel-mcp # core + cliptunnel-agent binary
pip install cliptunnel-mcp[server] # adds cliptunnel-mcp server binary (mcp>=1.2,<2)
Both modes install console entry points:
| Binary | Extra needed | Purpose |
|---|---|---|
cliptunnel-agent |
(none) | Runs the Agent on the local OS clipboard. |
cliptunnel-mcp |
[server] |
Runs the MCP server over stdio. |
Quick start
Agent (remote machine)
The simplest way to run the Agent is the installed binary:
cliptunnel-agent
Antivirus / EDR workaround (Windows): unsigned
.exeentry points may be quarantined. Usepython -minstead — it runs through the already-trusted Python interpreter with no generated binary:python -m cliptunnel_mcp.agent # instead of cliptunnel-agent python -m cliptunnel_mcp.server # instead of cliptunnel-mcp
This builds a ClipboardTransport backed by the system clipboard (pbcopy/pbpaste on macOS, user32 on Windows, wl-copy/wl-paste on Wayland, xclip/xsel on X11) and wires operations.dispatch as the command handler. The Agent watches the clipboard slot, ACKs commands, processes them in a worker pool, and writes responses back. Press Ctrl+C to stop.
Controller + MCP server (operator machine)
On the operator side, configure your MCP client (Claude Desktop, Cursor, Pi, etc.) to launch the server binary:
{
"mcpServers": {
"cliptunnel": {
"command": "cliptunnel-mcp",
"args": []
}
}
}
If the cliptunnel-mcp binary is blocked by antivirus, use python -m:
{
"mcpServers": {
"cliptunnel": {
"command": "python",
"args": ["-m", "cliptunnel_mcp.server"]
}
}
}
The server binary injects a Controller backed by a ClipboardTransport and runs the FastMCP application over stdio. All remote_* tools are available immediately.
Note: the MCP server requires
pip install cliptunnel-mcp[server].
Controller only (no MCP)
For programmatic use without an MCP client:
from cliptunnel_mcp.clipboard_transport import ClipboardTransport
from cliptunnel_mcp import Controller
import json
controller = Controller(transport=ClipboardTransport())
# Async — returns a Future
future = controller.send_command(json.dumps({"op": "shell", "cmd": "whoami"}))
result = future.result(timeout=30)
# Sync — blocks until response or timeout
output = controller.send_command_sync(json.dumps({"op": "fs.read", "path": "/etc/hostname"}))
Programmatic Agent
If you need a custom handler or transport:
from cliptunnel_mcp.clipboard_transport import ClipboardTransport
from cliptunnel_mcp import Agent
from cliptunnel_mcp.operations import dispatch
agent = Agent(transport=ClipboardTransport(), handler=dispatch)
# Blocks until agent.close() — run in a thread or manage lifecycle yourself.
API surface
Controller
The operator-side endpoint. Sends commands asynchronously, dispatches one at a time, and resolves futures as responses arrive.
| Method | Description |
|---|---|
send_command(command: str) -> Future |
Queue a command; returns a Future that resolves with the response payload or None on failure. |
send_command_sync(command: str) -> str | None |
Send and block until response or timeout seconds. |
close() |
Stop background threads. Idempotent. |
Constructor parameters: transport (required), timeout, retries, poll_interval, ack_timeout, initial_seq, persist_seq, seq_store.
Agent
The remote-side endpoint. Watches the slot, ACKs commands immediately, processes them in a worker pool, and writes one typed response at a time with retransmission.
| Method | Description |
|---|---|
close() |
Stop this agent generation. Idempotent; never strands a thread. |
Constructor parameters: transport (required), handler (required), poll_interval, max_workers, response_ack_timeout.
dispatch
The default Agent handler. Parses JSON payloads and routes to the matching operation.
from cliptunnel_mcp.operations import dispatch
output, is_error = dispatch('{"op": "shell", "cmd": "echo hello"}')
Protocol primitives
| Symbol | Description |
|---|---|
pack(msg) -> str |
Serialize a Message into wire format. |
unpack(raw) -> Message | None |
Parse a wire string; None on malformed input. |
validate(raw, my_role) -> bool |
True if raw is well-formed and addressed to my_role. |
Message |
Dataclass: frm, to, seq, mtype, payload. |
MsgType |
Enum: COMMAND, RESPONSE, ERROR, ACK. |
Role |
Enum: CONTROLLER, AGENT. |
SeqTracker |
Per-seq dedupe state: new → processing → done. |
Transport protocol
class Transport(Protocol):
def read(self) -> str: ...
def write(self, value: str) -> None: ...
class RevisionMonitor(Protocol):
@property
def revision(self) -> int: ...
def wait_for_change(self, after: int, timeout: float = 1.0) -> int: ...
A transport must implement read/write (last-writer-wins). Implementing RevisionMonitor (or exposing wait_for_revision / wait_for_change) enables change-aware waits instead of polling.
Operations
The dispatch handler supports these operations:
| Operation | Parameters | Returns |
|---|---|---|
shell |
cmd |
JSON: {stdout, stderr, returncode} |
fs.read |
path |
JSON: {content, lines} |
fs.write |
path, content |
wrote N bytes to PATH |
fs.list |
path |
JSON: [{name, size, is_dir}] |
fs.delete |
path |
deleted PATH |
fs.replace |
path, old, new |
replaced 1 occurrence in PATH (exact-once match) |
fs.search |
path, pattern |
JSON: [{line, content}] (regex) |
fs.find |
path, pattern |
JSON: [PATH, ...] (glob, ** recurses) |
fs.bin_read |
path |
JSON: {path, size, b64} |
fs.bin_write |
path, b64 |
wrote N bytes to PATH |
MCP tools
The server exposes 13 tools over stdio:
| Tool | Description |
|---|---|
remote_shell |
Execute a shell command; auto-sync (10 s) then async with job_id polling. |
remote_shell_result |
Poll for the result of an async shell command. |
remote_fs_read |
Read a file. |
remote_fs_write |
Create or overwrite a file (creates parent dirs). |
remote_fs_list |
List directory entries. |
remote_fs_delete |
Delete a file. |
remote_fs_replace |
Search-and-replace in a file (exact-once match). |
remote_fs_search |
Regex search in a file. |
remote_fs_find |
Glob-find files under a directory. |
remote_fs_bin_read |
Read a binary file as base64. |
remote_fs_bin_write |
Write base64 content to a binary file. |
remote_upload |
Upload a local file to the remote machine. |
remote_download |
Download a remote file to the local machine. |
remote_sysinfo |
Return system info (OS, Python, CPU, memory, disk) from the remote machine. |
Lifecycle and coalescing semantics
- One command at a time: the Controller dispatches commands serially. The pending command's seq is published atomically with the slot write so the reader never observes the command before the dispatcher.
- Immediate ACK: the Agent ACKs every command before processing, freeing the slot for the Controller.
- One response at a time: the Agent holds exactly one pending response envelope. A new command never implicitly ACKs a pending response — only the Controller's matching
A(seq)releases it. - Retransmission: both sides retransmit on ACK timeout. The Controller retries up to
retriestimes (default 3). The Agent retransmits the response everyresponse_ack_timeoutseconds (default 1.0). - Deduplication: the Agent's
SeqTrackertracks per-seq state (new → processing → done). Duplicate commands are ACKed; done ones replay the cached typed response; in-flight ones are already being processed. - Stale message guard: the Controller skips any R/E with
seq <= min_seq— stale slot content from a previous session. - Generation-safe: all stop state and queues are local to each instance. Closing and starting a new Agent or Controller never strands threads.
- Paced writes: the Controller enforces a bounded inter-write gap (2× poll interval) so the Agent can read each message before it is overwritten.
Backend selection
ClipTunnel ships ClipboardTransport, a transport backed by the OS clipboard. On Wayland it uses wl-paste --watch for event-driven change detection (zero polling, zero CPU when idle). On macOS, Windows, and X11 it polls every 100 ms with hash-based change detection. It implements both Transport and RevisionMonitor, so both endpoints get change-aware waits. The binaries cliptunnel-agent and cliptunnel-mcp use it automatically.
For custom setups — a Citrix clipboard redirection, a shared Gist, a network pipe — implement the Transport protocol (read() -> str, write(str) -> None) and optionally RevisionMonitor (revision + wait_for_change). Inject it into Controller or Agent directly.
Platform support
| Platform | Status | Clipboard backend | Change detection |
|---|---|---|---|
| macOS | Tested | pbcopy/pbpaste (built-in) |
Polling (100 ms) |
| Windows | Tested | ctypes + user32 (no extra deps) |
Polling (100 ms) |
| Linux / Wayland | Tested | wl-copy/wl-paste (wl-clipboard package) |
Event-driven |
| Linux / X11 | Core works | xclip (fallback: xsel) |
Polling (100 ms) |
Development
# Create a virtual environment
uv venv && source .venv/bin/activate
# Install in development mode
uv pip install -e . pytest
# Run the test suite (161 tests)
python -m pytest -q
# or
python -m unittest discover -s tests -t .
# Bare mode — no install, just PYTHONPATH
PYTHONPATH=src:. python -m pytest -q
The test suite uses a deterministic ClipboardSlot test double that models the last-writer-wins channel with revisions and bounded waits. No clipboard hardware is needed.
Limitations
- Text-only clipboard: the protocol carries UTF-8 strings. Binary files are base64-encoded, which roughly doubles their size over the wire.
- Single slot: the clipboard holds one value at a time. The ARQ protocol serializes all traffic through it, so throughput is bounded by the clipboard round-trip latency.
- No encryption: the wire format is plain base64. If the clipboard is observable, use an encryption layer in your transport or handler.
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。