Skip to main content

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).
  • EndpointsController (operator side) and Agent (remote side), connected by an injected Transport. 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, and remote_download tools over stdio.

The core package has zero dependencies. The MCP server requires the optional [server] extra (mcp>=1.2,<2).

Architecture

graph TD
  subgraph Operator["Operator machine"]
    Client["MCP client<br/>(Claude, Pi, Cursor, …)"]
    Server["MCP server<br/>(cliptunnel-mcp)"]
    Controller["Controller<br/>send_command → Future"]
    CT1["ClipboardTransport<br/>(OS clipboard)"]
  end

  subgraph Remote["Locked-down machine"]
    CT2["ClipboardTransport<br/>(OS clipboard)"]
    Agent["Agent<br/>ACK → process → R/E"]
    Dispatch["dispatch<br/>shell · fs · bin"]
  end

  Client -- "MCP / stdio" --> Server
  Server --> Controller
  Controller -- "CT1 wire" --> CT1
  CT1 -- "clipboard slot<br/>(last-writer-wins)" --> CT2
  CT2 --> Agent
  Agent --> Dispatch
  Dispatch -- "response" --> Agent
  Agent -- "CT1 wire" --> CT2
  CT2 -- "clipboard slot" --> CT1
  CT1 --> Controller

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

This builds a ClipboardTransport backed by the system clipboard (pbcopy/pbpaste on macOS, user32 on Windows, xclip/xsel on Linux) 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": []
    }
  }
}

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.

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 retries times (default 3). The Agent retransmits the response every response_ack_timeout seconds (default 1.0).
  • Deduplication: the Agent's SeqTracker tracks 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 (pbcopy/pbpaste on macOS, user32 on Windows, xclip/xsel on Linux). It implements both Transport and RevisionMonitor, so both endpoints get change-aware waits instead of pure polling. 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
macOS Tested pbcopy/pbpaste (built-in)
Windows Tested ctypes + user32 (no extra deps)
Linux Core works xclip (fallback: xsel) — install one of them

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cliptunnel_mcp-0.1.0.tar.gz (39.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cliptunnel_mcp-0.1.0-py3-none-any.whl (27.5 kB view details)

Uploaded Python 3

File details

Details for the file cliptunnel_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: cliptunnel_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 39.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cliptunnel_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ff55981c04a852ff608b4c923829e73d402b0c46fbf2d5467b57381d312ddc28
MD5 7ffc1576198b297b624e26deff31134d
BLAKE2b-256 2bb409315921e48b2490a5a4882be2c74dc86917b4f3103906c218ce2c3c5301

See more details on using hashes here.

Provenance

The following attestation bundles were made for cliptunnel_mcp-0.1.0.tar.gz:

Publisher: publish.yml on jordi-murgo/cliptunnel-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cliptunnel_mcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cliptunnel_mcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 27.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cliptunnel_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1c413f0f6af8750ed4f960c2589c495e9418cd7f11d664a59e4648bef31b93d5
MD5 1d683453cadd74d77a9a7cc9573c4f33
BLAKE2b-256 d9b6861d367a61acd154e843728a730b61f40f2a28ff43c6e3621c8a066fe88f

See more details on using hashes here.

Provenance

The following attestation bundles were made for cliptunnel_mcp-0.1.0-py3-none-any.whl:

Publisher: publish.yml on jordi-murgo/cliptunnel-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.1.1

2 files

1.1.0

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.4

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.3.2

2 files

0.3.0

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page