PluginWorld
Ds

dsh-presence

MCP

Exposes 20 tools over stdio that let coding agents treat any SSH-reachable machine as a local workspace: reading, writing, editing, searching and listing files, running commands and background jobs, driving persistent terminals, and forwarding ports over one pooled connection, with nothing installed on the remote host.

@fanrj3 · v0.17.7 · MIT License · updated today

SECURITY

B

SCORE

69

STARS

▲ 1

PLUG IN

git clone https://github.com/fanrj3/dsh-presence.git

See the README to configure this MCP server

README

Presence icon

Presence

Your SSH hosts, as local tabs.

Persistent remote workspaces, native file editing, search, exec, terminals and port forwards for coding agents — with nothing installed on the remote host.

English · 简体中文 · Releases · MCP server

npm plugin npm mcp license awesome-dsh-plugin

Presence — remote machines as local tabs


Why

Running an agent on every remote machine means installing it N times, upgrading it N times and configuring proxies per host. Running ssh host '<command>' from the local agent means a new handshake per command, no native file tools and no continuity.

Presence keeps the agent local and extends its reach through one persistent SSH connection per host. The remote side is still plain sshd: SFTP, exec and a PTY.

Approach Cost
Install the agent on every host N installs, N upgrades, per-host proxying
ssh host '<command>' per call Handshake per command, no file tools, no sessions
Presence One connection per host, native tools, nothing installed remotely

Features

  • Remote zero-install — uses only sshd built-ins: the SFTP subsystem, exec channels and a PTY.
  • One handshake — a pool keeps one connection per host; every later call is a request on it.
  • Native remote tools — rg/grep runs on the target, commands run in the target shell; path, permission and encoding semantics are the remote ones.
  • Local-like edits — atomic writes, mode preservation, ambiguity refusal, read-before-write guard, presence_edit with dry-run diffs.
  • Real workspaces — a remote directory becomes a normal DSH workspace with a file tree and terminals that open on the remote machine.
  • Recoverable deletes — destructive shell commands dry-run first; presence_trash moves paths to a remote trash with restore.
  • Two shells, one kernel — the DSH plugin and a standalone MCP server (presence-mcp, 20 tools) share the same kernel.

Quick start

1. Install the DSH plugin

dsh plugin add @rjfan/dsh-presence@0.17.7

From a source checkout:

dsh plugin add /path/to/dsh-presence

2. Pick a host and open a workspace

Open Settings → Presence → New, choose a host from ~/.ssh/config (or paste a manual ssh command), then pick a remote folder. It appears as a workspace; the left sidebar, file tree, terminal and agent tools all use that remote directory.

3. Work

You: 把远程 8000 端口转发到本地,然后打开看看
Agent: presence_forward { remotePort: 8000 }
       → http://127.0.0.1:8000

MCP server

The same kernel is available to agents that cannot install DSH plugins (Codex CLI, Claude Code, OpenCode, Cursor, …).

npx -y presence-mcp            # 20 tools over stdio
// Claude Desktop / OpenCode style
{
  "mcpServers": {
    "presence": {
      "command": "npx",
      "args": ["-y", "presence-mcp"],
      "env": {
        "PRESENCE_HOST": "server",
        "PRESENCE_WORKSPACE": "server:/srv/app"
      }
    }
  }
}

See mcp/README.md for Codex TOML, Claude Code, OpenCode and Cursor/Windsurf examples.

How it works

┌───────────────────────────────┐
│  DSH plugin  /  MCP server    │  two shells, one kernel
└──────────────┬────────────────┘
               │
        Presence kernel
        ├── ssh-config   hosts, ports, users, keys, jumps from ~/.ssh/config
        ├── connection   one persistent connection: SFTP / exec / PTY + pool + cancel
        └── host pool    one connection per host, created on demand, idle-collected
               │
        SSH (your existing keys, known_hosts, jumps, proxies)
               │
        target: Linux / macOS, stock sshd
Agent operation Channel Remote dependency
read / write / edit / list / stat SFTP subsystem sftp-server (stock sshd)
exec, search (rg/grep), git exec channel commands already on the host
interactive terminal shell channel + PTY the host's login shell

Measured on a Linux host:

Operation Latency
ssh host '<cmd>' (new connection every time) 520–540 ms
Presence connect (one handshake, key auth) 145 ms
Presence sftp stat p50 0.6 ms / p95 0.9 ms
Presence exec true (new channel + shell) p50 7.6 ms
8 concurrent exec channels 15 ms total

Keep TCP_NODELAY on: without it, request/response hits delayed ACK and p95 goes from 0.9 ms to 48 ms.

Tools

The DSH plugin exposes 18 tools. Relative paths resolve against the active remote workspace; host is optional inside a workspace.

Show all tools
Tool What it does
presence_hosts List SSH hosts, connection state and default host (format:"json" supported).
presence_connect Open/reuse a host connection and report the remote identity.
presence_exec Run a remote command; pipefail, background, waitForPort, structured JSON, script, saveStdout; destructive commands dry-run first.
presence_jobs Manage background jobs: list, status, wait (with timedOut), stop (with alive/verified/portReleased), purge.
presence_read Read a remote file with line numbers, offset/limit, or raw:true for verbatim content.
presence_read_many Read 1–50 remote files in one call, with per-file byte/line caps.
presence_write Atomic write; preserves mode; returns real mode, size and full sha256; ifMatchSha256 optimistic lock.
presence_edit Exact-text replacement or line-range replacement, with compact diff, dryRun, JSON fields.
presence_ls List a directory; recursive, totalBytes, JSON output.
presence_stat Batch stat for 1–100 paths: type, size, mode, mtime, optional sha256; missing paths return exists:false.
presence_search Remote rg/grep search, count-only mode, JSON matches.
presence_glob Find remote files by glob with size/mtime.
presence_import Local → remote binary-safe upload with mode and sha256.
presence_export Remote → local real file (content-addressed), for preview or native apps.
presence_forward Forward a remote port to 127.0.0.1; open/list/close with JSON.
presence_port_probe Check listeners and TCP reachability for up to 20 ports; `expect:open
presence_http_probe Wait for port → forward → local HTTP assertions → close → verify the local port was released.
presence_terminal Persistent PTY: open with cwd/command, write (ackOnly), read, close; tmux-backed sessions survive reconnects.
presence_trash Recoverable delete: move to remote trash, list/restore; batch paths[]; returns sourceRemoved and restoreHint.

Safety and recovery

  • Destructive commands never run unconfirmed. rm -r/-f, git clean -fdx, find -delete, shred, truncate and dd of= are first replayed with no-op shims to expand their real targets; unset variables and protected paths (workspace root, home, /, .git) are refused. A second call with confirm:true is required to execute.
  • Prefer presence_trash for anything under a workspace: it moves the path to ~/.dsh/presence-trash/<timestamp>/ and returns a restore hint.
  • Writes are atomic and keep the original file mode; presence_edit refuses ambiguous anchors instead of guessing.
  • Read before write: built-in read/write/edit share a read-before-write guard; presence_write records the observation so both APIs can be mixed.

Configuration

The plugin reads these environment variables on the host process:

Variable Default Meaning
PRESENCE_HOST — Default SSH host (alias from ~/.ssh/config or a manual host).
PRESENCE_WORKSPACE — host:/abs/path to bind a remote workspace.
PRESENCE_PASSWORD_<ALIAS> — Password for a host; <ALIAS> is uppercase with non-alphanumerics replaced by _.
PRESENCE_MAX_CHANNELS 8 Concurrent channels per connection.
PRESENCE_CONNECT_TIMEOUT_MS 25000 Connection attempt timeout.
PRESENCE_IDLE_TIMEOUT_MS 900000 Idle connection timeout (connections with terminals are kept).
PRESENCE_READ_ONLY false Refuse writes/edits; reads, search and exec still work.

Full configuration and the path/permission matrix are in docs/VERIFICATION.md and docs/HANDOFF.md.

Development

npm install
npm install --prefix .dev --ignore-scripts rollup @rollup/plugin-node-resolve \
  @rollup/plugin-commonjs @rollup/plugin-terser codemirror @codemirror/language-data \
  @codemirror/theme-one-dark
node scripts/build-client.mjs
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package.ps1 -Version 0.17.7
# → dist/presence-plugin-0.17.6.zip

Tests require one SSH-reachable host:

node scripts/regression.mjs server     # transport, symlinks, timeouts, binary
node scripts/job-probe.mjs server      # jobs + port forwarding
node scripts/feature-probe.mjs server  # wait, sessions, pgid, readiness, search
node scripts/import-probe.mjs server   # local→remote binary, mode, sha256
node scripts/safety-probe.mjs server   # destructive guard + observation store

Known limits

  • The mount path (~/.dsh/presence/mounts/...) is virtual: it works through ctx.fs / agent tools, but native apps (Explorer, Office, host Python) cannot see it — use presence_export first.
  • SSH MaxSessions (default 10) bounds concurrent channels; the pool uses at most 8.
  • PTY terminals follow the connection; cross-reconnect shells need tmux on the target (used automatically by presence_terminal { session }).
  • ProxyJump/ProxyCommand are parsed and warned about, but not used for dialing yet.

Contributing

Issues and PRs are welcome. For a feature, open an issue describing the remote workflow you want; for a fix, keep the diff focused and add a probe under scripts/ when behavior changes.

License

MIT

Star History

Star History Chart

SIMILAR PLUGINS