Remote workspaces for coding agents, over plain SSH.
Set up your coding agent on one machine. It can then work on any machine you can SSH into.
The remote side runs one small binary that the agent drives over your existing SSH
config. No daemon, no open port, no copying your project around. If
ssh myhost works, this works.
You need two binaries on your own machine. The server is installed on Linux x86_64 remotes for you when you add a workspace.
BASE=https://github.com/hesic73/remote-workspace/releases/latest/download
mkdir -p ~/.local/bin
for b in remote-workspace remote-workspace-mcp; do
curl -fsSL "$BASE/$b-linux-x86_64-musl" -o ~/.local/bin/$b
chmod +x ~/.local/bin/$b
done
remote-workspace --version
$releaseBase = "https://github.com/hesic73/remote-workspace/releases/latest/download"
$installDir = "$env:LOCALAPPDATA\remote-workspace\bin"
New-Item -ItemType Directory -Force $installDir | Out-Null
foreach ($name in @("remote-workspace", "remote-workspace-mcp")) {
Invoke-WebRequest "$releaseBase/$name-windows-x86_64.exe" -OutFile "$installDir\$name.exe"
}
$env:Path = "$installDir;$env:Path" # this shell only; add it to your user PATH
remote-workspace --version
Checksums: SHA256SUMS (Linux),
SHA256SUMS-windows-x86_64 (Windows).
git clone https://github.com/hesic73/remote-workspace
cd remote-workspace
cargo build --release --locked
install -m755 target/release/remote-workspace \
target/release/remote-workspace-mcp ~/.local/bin/
| Client / MCP | Remote host | Server installation |
|---|---|---|
| Linux x86_64 or Windows x64 | Linux x86_64, POSIX shell | Automatic |
| Windows x64 | Windows, PowerShell OpenSSH default shell | Manual build and --remote-bin |
Linux clients cannot use PowerShell endpoints. Other POSIX targets need a server built and installed manually.
One command probes the host, installs the matching server, runs a real round trip against your directory, and only then records the workspace. The directory must already exist.
remote-workspace workspace add robot \
--host robot@workstation \
--root /home/robot/project
Adding workspace 'robot'
SSH connected
Remote shell posix
Remote platform linux-x86_64
Workspace root valid
Server installed 0.6.2
Protocol 3
Workspace probe passed
Fleet configuration updated
Workspace 'robot' is ready.
If any step fails, nothing is recorded and the error names the layer that broke. Add as many workspaces as you like, on as many machines as you like.
On the remote Windows host, build a checkout of the same release with
cargo build --release --locked -p remote-workspace-server. Place
target\release\remote-workspace-server.exe at a persistent path and
configure PowerShell as the OpenSSH DefaultShell. From a Windows client:
remote-workspace workspace add windows --host user@windows-host --root C:/projects/app --remote-bin C:/tools/remote-workspace-server.exe --remote-shell powershell
PowerShell endpoints use one short SSH/server process per request, serialized per workspace. Installing and upgrading a Windows server is manual.
One entry serves your whole fleet, and it can be added before any workspace exists: workspaces added later are picked up on the next call. Restart the host application after registering, since MCP servers are loaded at startup.
claude mcp add --scope user remote-workspace -- remote-workspace-mcp
In claude_desktop_config.json. Use an absolute path, because a GUI app
does not inherit your shell's PATH.
{
"mcpServers": {
"remote-workspace": {
"command": "/home/you/.local/bin/remote-workspace-mcp"
}
}
}
codex mcp add remote-workspace -- remote-workspace-mcp
grok mcp add --scope user remote-workspace -- remote-workspace-mcp
On Windows, use remote-workspace-mcp.exe or its absolute path.
remote-workspace-mcp --check
fleet config /home/you/.remote-workspace/workspaces.toml ok: 2 workspace(s)
robot [robot@workstation:/home/robot/project]: ok
lab [lab-gpu-1:/data/experiments]: ok
Your agent now has nine tools: list, read, create, edit and delete files, run commands,
and transfer single files in either direction. One edit_file call carries up to a
hundred replacements: they apply in order, and either all of them land or the file is
left untouched. Every tool
except list_workspaces takes a required workspace argument, so a
call cannot land on the wrong machine by default. Inspecting what happened afterwards
is a job for the remote-workspace CLI, not a tool the agent carries.
Every command runs in a fresh process, so conda activate in one call never
leaks into the next. Say once what a command's environment should be, in a config file
on the remote host:
# ~/.remote-workspace/config.toml on the remote machine
default_profile = "user"
[profiles.user]
shell = ["bash", "-lc"]
setup = """
. "$HOME/miniforge3/etc/profile.d/conda.sh"
conda activate myenv
"""
[profiles.raw]
shell = ["bash", "-c"]
setup = ""
Point the workspace at it when adding, with
--config /home/robot/.remote-workspace/config.toml.
bash -lic
loads your dotfiles, but without a terminal it prints job-control warnings on every
command, and zsh prompt plugins can fail noisily. Sourcing what you need is quieter.
Note that zsh reads .zshrc only when interactive, so dropping -i
without an explicit setup can change which python runs.
Your own binaries first: the client installs only the server release matching its own version.
BASE=https://github.com/hesic73/remote-workspace/releases/latest/download
for b in remote-workspace remote-workspace-mcp; do
curl -fsSL "$BASE/$b-linux-x86_64-musl" -o ~/.local/bin/$b
chmod +x ~/.local/bin/$b
done
remote-workspace workspace upgrade
The second command installs once per SSH identity, transfers nothing when a host is
current, never downgrades a newer server, and never edits your fleet file. Restart the
host application afterwards, since MCP servers are loaded at startup. If you skip it,
the MCP refuses a server on a different protocol with server_too_old or
client_too_old instead of failing call by call.
Your fleet is ~/.remote-workspace/workspaces.toml on your own machine,
written by workspace add and re-read whenever it changes, so a new workspace
appears without restarting your agent.
On the remote, the operation log sits in
~/.remote-workspace/state/, outside your project. Nothing appears in
git status, and a destructive command inside the workspace cannot take the
history with it. The log records what happened; it does not reverse it, so keep anything
that must survive a mistake in version control. Paths beginning @scratch/ address a transient area
there for the agent's own working files; anything untouched for a week is deleted, so
keep what matters in the workspace or pull it down with download_file.
To see which tools your agent actually reaches for, register the MCP with
--log-dir ~/.remote-workspace/log and read it back:
remote-workspace stats
tool calls errors
run_command 1800 24
edit_file 678 37
read_file 201 5
list_directory 8 0
It is off by default, because a logged request carries the full text of every file your agent writes.
| Symptom | Usually |
|---|---|
connect_failed |
SSH itself cannot reach the host. Try ssh <host> true. Anything needing a password prompt fails rather than hanging. |
probe_failed, state directory is locked |
Another session is using that workspace; one server serves a root at a time. A server orphaned by a dropped connection releases the lock about fifteen minutes after its last activity (longer if a command is still running). server.jsonl in the state directory records when each server started and stopped. |
server_too_old, client_too_old |
Your binaries and the remote server are on different protocols. See upgrading. |
Connected, but the wrong python |
A profile problem rather than a connection problem. Compare run_command under profile: "raw" with your default. |
Edited workspaces.toml by hand, cannot connect |
Editing it yourself installs nothing and checks nothing. A wrong SSH username quietly produces paths that do not exist. Remove the entry and use workspace add. |