Skip to content
Conduit

mcp server

remote-ssh-mcp

Work on a remote SSH host through a persistent, watchable tmux session, via MCP.

Square596/remote-ssh-mcpcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "square596-remote-ssh-mcp": {
      "command": "uvx",
      "args": [
        "remote-ssh-mcp"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 25, 2026

remote-ssh-mcp

An MCP server that lets coding agents work on a remote host through a persistent tmux+SSH session. Every tool call (run, read, write, edit, grep, glob) is routed through the same tmux pane on the remote, so you can tmux attach and watch the agent work in real time.

Why

Most coding-agent clients operate on the local filesystem and a fresh non-interactive shell per call. That makes "I want the agent to work on my remote server" awkward: you end up wrapping every command in ssh host '<cmd>', losing shell state (cwd, venv, env vars) between calls, and having no way to watch what's happening.

remote-ssh-mcp fixes that by:

  • Opening one persistent tmux session per remote host, using ssh -A by default so forwarded-agent operations are available when your local agent is usable, and keeping shell state across calls.
  • Giving each agent (parent + subagents) its own tmux window, so parallel work doesn't race on the same shell.
  • Exposing a full toolkit (remote_run, remote_read, remote_write, remote_edit, remote_grep, remote_glob, …) that mirrors local agent tools — but every operation flows through the visible tmux pane.

You can tmux attach -t remote-ssh-mcp/<host> at any time to watch.

Status

Alpha. The current release ships with explicit connection lifecycle state, automatic pane recovery after command timeouts, single-window-per-connection serialization, typed tool responses on modern MCP clients, and 1 MB caps on individual command responses and file reads. See Limitations.

Install

Prerequisites

  • tmux 3.0+ on your laptop.
  • An SSH config entry for the remote host (i.e. ssh <host> works in a normal terminal). Agent forwarding is requested by default via ssh -A; pass agent_forwarding=false to remote_connect to disable it.
  • python3 on the remote host (used for verified, atomic file writes).
  • uv on your laptop for the bundled stable plugin config. If you install the MCP server manually, uv or pipx is fine.

As a plugin with bundled skills

The plugin bundles the MCP server and skills that brief MCP-capable agents on how to use it. In Claude Code, install it with:

/plugin marketplace add Square596/remote-ssh-mcp
/plugin install remote-ssh-mcp@Square596

In Codex, install the same plugin with:

codex plugin marketplace add Square596/remote-ssh-mcp
codex plugin add remote-ssh-mcp@Square596

The plugin directory includes metadata for both clients. Both Claude Code and Codex use the bundled .mcp.json plus the remote-ssh skill.

The bundled MCP config requires uv in PATH. It verifies the installed package, source tag, and MCP SDK locally. A matching installation launches without contacting GitHub. The first start after installation or a plugin update installs the exact release and tested SDK declared by the plugin; if that installation cannot be verified, startup fails instead of running a different tool version.

Plugin updates are discovered by the client marketplace rather than by the MCP server. For Codex, refresh the marketplace and reinstall its snapshot:

codex plugin marketplace upgrade Square596
codex plugin add remote-ssh-mcp@Square596

For Claude Code, update the marketplace and plugin:

/plugin marketplace update Square596
/plugin update remote-ssh-mcp@Square596

Claude Code can also update third-party marketplaces automatically when that option is enabled for the marketplace.

An already-running MCP process does not hot-reload after an upgrade. Restart the MCP client or begin a new client session to load the updated server. Run remote-ssh-mcp --version to see which version a newly launched process will use.

Repository plugin layout

This repository carries both Claude Code and Codex plugin metadata:

  • .claude-plugin/marketplace.json is the Claude Code marketplace catalog. Claude users add this repository as a marketplace and install remote-ssh-mcp from it.
  • .agents/plugins/marketplace.json is the Codex marketplace catalog for the same plugin.
  • plugins/remote-ssh-mcp/.claude-plugin/plugin.json is the Claude Code manifest for the plugin itself.
  • plugins/remote-ssh-mcp/.codex-plugin/plugin.json is the Codex manifest for the plugin itself, including Codex UI metadata.
  • plugins/remote-ssh-mcp/.mcp.json and plugins/remote-ssh-mcp/skills/remote-ssh/SKILL.md are shared by both plugin loaders.

For Claude Code, keep only plugin.json inside .claude-plugin/; component directories such as skills/ and config files such as .mcp.json live at the plugin root.

As an MCP server (any MCP client)

This section is for manual MCP setup when you are not installing the plugin package from a marketplace. The first JSON block intentionally mirrors plugins/remote-ssh-mcp/.mcp.json: plugin loaders read that file automatically, while plain MCP clients need an equivalent server definition in their own MCP config.

For the stable 0.4.3 release, use the same pinned launcher as the bundled plugin:

{
  "mcpServers": {
    "remote-ssh": {
      "command": "sh",
      "args": [
        "-lc",
        "command -v uv >/dev/null 2>&1 || { echo 'remote-ssh-mcp plugin requires uv in PATH. Install uv, or preinstall remote-ssh-mcp and configure your MCP client to run command: remote-ssh-mcp.' >&2; exit 127; }; rsm_tool_root=\"$(uv tool dir 2>/dev/null)\" && rsm_bin_root=\"$(uv tool dir --bin 2>/dev/null)\" || { echo 'remote-ssh-mcp plugin could not locate the uv tool directories.' >&2; exit 1; }; rsm_python=\"$rsm_tool_root/remote-ssh-mcp/bin/python\"; rsm_bin=\"$rsm_bin_root/remote-ssh-mcp\"; rsm_expected='0.4.3|2.0.0|https://github.com/Square596/remote-ssh-mcp|v0.4.3'; rsm_spec() { \"$rsm_python\" -c 'import importlib.metadata as m,json; d=m.distribution(\"remote-ssh-mcp\"); u=json.loads(d.read_text(\"direct_url.json\") or \"{}\"); print(\"|\".join((d.version,m.version(\"mcp\"),u.get(\"url\",\"\"),u.get(\"vcs_info\",{}).get(\"requested_revision\",\"\"))))' 2>/dev/null; }; if [ -x \"$rsm_bin\" ] && [ \"$(rsm_spec)\" = \"$rsm_expected\" ]; then exec \"$rsm_bin\"; fi; uv tool install --quiet --force --reinstall --with 'mcp==2.0.0' 'git+https://github.com/Square596/remote-ssh-mcp@v0.4.3' >&2 || { echo 'remote-ssh-mcp plugin could not install its pinned release.' >&2; exit 1; }; if [ ! -x \"$rsm_bin\" ] || [ \"$(rsm_spec)\" != \"$rsm_expected\" ]; then echo 'remote-ssh-mcp plugin installed an unexpected package or dependency version.' >&2; exit 1; fi; exec \"$rsm_bin\""
      ],
      "env_vars": ["SSH_AUTH_SOCK"]
    }
  }
}

For Codex, env_vars forwards the local SSH_AUTH_SOCK value into the MCP server process so ssh -A can use your local ssh-agent. This requires Codex itself to be launched from an environment where SSH_AUTH_SOCK is set.

For an opt-in development setup that follows the moving default branch, use:

{
  "mcpServers": {
    "remote-ssh": {
      "command": "sh",
      "args": [
        "-lc",
        "command -v uv >/dev/null 2>&1 || { echo 'remote-ssh-mcp requires uv in PATH.' >&2; exit 127; }; uv tool install --quiet --upgrade --with 'mcp>=2,<3' git+https://github.com/Square596/remote-ssh-mcp >&2; exec \"$(uv tool dir --bin)/remote-ssh-mcp\""
      ],
      "env_vars": ["SSH_AUTH_SOCK"]
    }
  }
}

This development configuration resolves GitHub and dependencies on every client startup and may pick up unreleased changes.

You can also install the stable Python package directly as a uv-managed tool:

uv tool install --with 'mcp==2.0.0' 'git+https://github.com/Square596/remote-ssh-mcp@v0.4.3'

The bundled and recommended stable install selects the MCP SDK version tested for the release. The server also supports MCP SDK 1.x back to 1.2. To use the older SDK line, install it manually from the same immutable source tag:

uv tool install --reinstall --with 'mcp>=1.2,<2' 'git+https://github.com/Square596/remote-ssh-mcp@v0.4.3'

Do not combine that fallback with the bundled plugin configuration, which restores the release's tested SDK on startup.

Then add the installed command to your MCP client config:

{
  "mcpServers": {
    "remote-ssh": {
      "command": "remote-ssh-mcp"
    }
  }
}

For Claude Code CLI:

claude mcp add remote-ssh remote-ssh-mcp

Installed tools remain on their selected tag. To move a manual installation to a newer release, reinstall it with that release's tag and documented SDK pin:

uv tool install --force --reinstall --with 'mcp==2.0.0' \
  'git+https://github.com/Square596/remote-ssh-mcp@v0.4.3'

Usage

Once installed, ask your agent to connect to a host with the remote-ssh skill or call the tool directly:

remote_connect(host="<host>", project_path="/home/me/myproject")

<host> is whatever alias you use in ~/.ssh/config — the same string that works for plain ssh <host>.

The skill will:

  1. Run local ssh-add, then connect via ssh -A <host>, opening a fresh tmux window in the remote-ssh-mcp/<host> session.
  2. cd into your project path.
  3. Tell the agent to use remote_* tools for all subsequent file/exec work, track host/project/connection state, and brief subagents to create their own connection.

To watch:

tmux attach -t remote-ssh-mcp/<host>

Ctrl-b w lists windows (one per active connection — parent + each subagent).

Agent config sync

The plugin also ships a remote-agent-config-sync skill for importing remote agent instructions into a local directory. It is meant for files such as AGENTS.md, CLAUDE.md, .agents/, .codex/, .claude/, .cursor/rules/, hooks, rules, and skills.

That workflow deliberately uses local rsync or scp -rp instead of adding generic transfer tools to the MCP API:

  • remote_* tools stay focused on watchable remote coding work through tmux.
  • rsync/scp handle bulk local/remote file movement better than base64 round-trips through a terminal pane.
  • Adaptation happens only after files are copied locally. The remote config files are not modified by the sync workflow.
  • Ambiguous hooks, install scripts, absolute paths, secrets, credentials, and destructive commands should be reviewed with the user before local adaptation.

Tools

All file/exec tools take a connection_id returned by remote_connect.

ToolLocal equivalentNotes
remote_connect(host, project_path?, label?, agent_forwarding?, ssh_add_paths?)—Opens new tmux window. Returns {connection_id, host, cwd, agent_warning, forwarded_agent_present, ssh_add_paths, ssh_add_exit_code, ssh_add_output}.
remote_disconnect(connection_id)—Closes window. Closes session if last window.
remote_status()—Lists active connections with lifecycle and recent activity details.
remote_run(connection_id, cmd, timeout?)ShellPersistent shell with multi-line command and heredoc support. Reports timeout, truncation, and pane recovery when applicable.
remote_read(connection_id, path, offset?, limit?)ReadReturns up to 1 MB of text; flags chunks containing invalid UTF-8.
remote_write(connection_id, path, content)WriteAtomically writes UTF-8 text after transport verification; preserves existing modes and symlinks.
remote_edit(connection_id, path, old, new, replace_all?)EditExact single- or multi-line match; errors if old is non-unique unless replace_all=true.
remote_grep(connection_id, pattern, path?, glob?)GrepUses rg if available, else grep -r; reports failures and exact truncation.
remote_glob(connection_id, pattern, path?)GlobWalks files with remote Python; reports failures and exact truncation.

Troubleshooting

Couldn't connect to <host>. Check in this order:

  1. ssh <host> works in a regular terminal (host is in ~/.ssh/config).
  2. The host supports non-interactive key-based auth (BatchMode=yes).
  3. If you need forwarded-agent operations from the remote host, local ssh-add can load your keys and agent forwarding is allowed.

agent_warning is present. The SSH connection worked, but the MCP server could not confirm a usable forwarded ssh-agent. Normal remote commands can still work through IdentityFile or other OpenSSH config. Private git fetches from the remote that rely on forwarded agent keys may fail. Pass agent_forwarding=false if you do not want the MCP server to run local ssh-add, connect with ssh -A, or check forwarded-agent readiness.

Explicit ssh_add_paths partially fail. remote_connect still proceeds if bulk ssh-add <paths...> returns non-zero. The response includes the expanded ssh_add_paths, ssh_add_exit_code, and ssh_add_output so the agent can tell you which requested paths may need checking before reconnecting.

A command timed out. remote_run interrupts the foreground command and probes the shell before returning. A pane_recovered=true result means the connection remains usable. If recovery fails, remote_status marks the connection unresponsive; disconnect it and create a fresh connection.

How are writes verified? The pane shows concise start and completion messages, while the payload is streamed to a no-echo receiver. The receiver checks the decoded byte count and SHA-256 before atomically replacing the destination. Successful tool responses stay concise and do not include the checksum. If verification or recovery fails, the error includes the failed stage, whether the destination is known to be unchanged, and whether the pane recovered for subsequent commands. Existing file modes are preserved, new files follow the remote shell's umask, and writes through symlinks update their targets without replacing the links.

Subagents seem to share my window. The skill instructs subagents to call remote_connect themselves at task start. If they don't, they fall through to your window. Check the parent's prompt to subagents.

Limitations

  • One pane per connection, serialized calls. Parallel calls on the same connection_id queue. Use separate connections (subagents) for parallelism, or nohup … & for true background work.
  • Single remote_run responses are capped at 1 MB. When output is larger, the response preserves its beginning and end and sets truncated=true.
  • Limits are bounded. remote_run timeouts range from 1 second to 24 hours; grep and glob requests accept from 1 to 10,000 results.
  • Single remote_read calls are capped at ~1 MB. Read larger files in chunks with offset and limit; remote_edit chunks internally for UTF-8 text files.
  • No built-in scp/rsync tools. Use the remote-agent-config-sync skill or local shell commands for bulk transfer; keep remote_* for remote project work.
  • No interactive TUI driving. Things that need a TTY (vim, less in interactive mode, sudo password prompts) won't work cleanly. Use non-interactive equivalents.
  • File tools expose text through MCP. remote_write and remote_edit encode their string inputs as UTF-8. remote_read replaces invalid UTF-8 bytes and adds encoding_warning. Use scp/rsync or a binary-safe remote_run command when exact binary bytes matter.

Development

See CONTRIBUTING.md for local checks and docs/releasing.md for the release checklist. Changes are recorded in CHANGELOG.md, and vulnerabilities should be reported as described in SECURITY.md.

License

MIT — see LICENSE.

↑↓ navigate openesc close