npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ssh-agent-workspace

v1.0.6

Published

Persistent SSH workspaces for AI agents. Stateful tmux-backed sessions that survive reconnects, restarts, and network drops — with runtime security and tool configuration.

Readme

ssh-agent-workspace

Stateful persistent workspace for AI agents over SSH.

Unlike traditional SSH MCP servers that execute every command in a fresh shell, SSH Agent Workspace provides a tmux-backed workspace that survives multiple commands, SSH reconnects, MCP restarts, and network interruptions. Your working directory, environment variables, shell history, and running processes remain intact.

Traditional SSH MCP

AI
 └─ ssh exec channel
      └─ command
           └─ state lost every time


SSH Agent Workspace

AI
 └─ persistent tmux workspace
      ├─ cwd persists
      ├─ env persists
      ├─ shell history persists
      ├─ running processes persist
      └─ auto recovery

Why This Exists

Most SSH MCP servers use exec channels. Every command starts from scratch:

❌ No persistent cwd — cd /var/www before every command
❌ No persistent env vars — re-export forever
❌ Interactive programs break — vim, htop, docker attach don't work
❌ State disappears after reconnect — start over from nothing

SSH Agent Workspace treats SSH as a persistent workspace instead of a command runner. Give your AI agent a real terminal that stays alive.


Core Features

  • Stateful workspaces — Persistent tmux-backed sessions. cwd, env, history survive everything.
  • Automatic recovery — Reconnect to existing sessions after SSH drops or MCP restarts.
  • Runtime reconfiguration — Enable/disable tools, update per-host security policies without restart.
  • Deterministic output — Prompt sentinel-based execution. No sleep-based output guessing.

Quick Start

Install

npm install -g ssh-agent-workspace

Or from source:

git clone https://github.com/ShiroNexo/ssh-agent-workspace.git
cd ssh-agent-workspace
npm install && npm run build

Setup Your SSH Config

Hosts must be defined in ~/.ssh/config:

Host prod
  HostName 10.0.0.5
  User deploy
  IdentityFile ~/.ssh/id_ed25519

Host staging
  HostName 10.0.0.10
  User deploy
  IdentityFile ~/.ssh/id_ed25519

Host internal
  HostName 172.16.0.50
  User admin
  ProxyJump bastion

Host bastion
  HostName jump.example.com
  User jumpuser

Try It

> connect host=prod
→ { session_id: "sess_abc", tmux_session: "mcp_prod_x1y2z3" }

Your agent now has a persistent workspace on prod. Running cd /var/www once means the agent stays there for every subsequent command.

Add to Your MCP Client

Add to ~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "workspace": {
      "type": "local",
      "command": ["npx", "-y", "ssh-agent-workspace"]
    }
  }
}

Or via CLI:

opencode mcp add workspace -- npx -y ssh-agent-workspace
claude mcp add workspace -- npx -y ssh-agent-workspace

Or add to ~/.config/claude-code/claude_code_config.json or project .mcp.json:

{
  "mcpServers": {
    "workspace": {
      "command": "npx",
      "args": ["-y", "ssh-agent-workspace"],
      "autoApprove": [
        "mcp__workspace__connect",
        "mcp__workspace__exec",
        "mcp__workspace__send_input",
        "mcp__workspace__read_output",
        "mcp__workspace__list_hosts",
        "mcp__workspace__list_sessions",
        "mcp__workspace__sftp_upload",
        "mcp__workspace__sftp_download",
        "mcp__workspace__sftp_list"
      ]
    }
  }
}

Tip: The autoApprove block lets the agent use those tools without asking permission each time. Add or remove tools based on your comfort level.

Go to Cursor SettingsMCPNew MCP Server. Use this config:

{
  "mcpServers": {
    "workspace": {
      "command": "npx",
      "args": ["-y", "ssh-agent-workspace"]
    }
  }
}
codex mcp add workspace -- npx -y ssh-agent-workspace

Or add to ~/.codex/config.toml:

[mcp_servers.workspace]
command = "npx"
args = ["-y", "ssh-agent-workspace"]

Add to ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "workspace": {
      "command": "npx",
      "args": ["-y", "ssh-agent-workspace"]
    }
  }
}
{
  "mcpServers": {
    "workspace": {
      "command": "npx",
      "args": ["-y", "ssh-agent-workspace"]
    }
  }
}
gemini mcp add workspace npx -y ssh-agent-workspace
{
  "mcpServers": {
    "workspace": {
      "command": "npx",
      "args": ["-y", "ssh-agent-workspace"]
    }
  }
}
qodercli mcp add workspace -- npx -y ssh-agent-workspace

Using npx means no global install needed. npx auto-downloads the latest version. If you installed globally (npm install -g ssh-agent-workspace), replace "npx" / "-y" / "ssh-agent-workspace" with "ssh-agent-workspace" as the command directly.


Key Features

Workspace Persistence

Every session is a dedicated tmux session on the remote host. Your agent's working directory, environment variables, shell history, and running processes persist across commands, disconnections, and server restarts.

connect → tmux workspace created with PS1='__MCP_PROMPT__> '
             │
exec "cd /var/www"    → prompt wait → output returned → cwd is now /var/www
exec "docker ps"      → runs in /var/www, no need to cd again
exec "vim app.js"     → vim opens and stays running in tmux

(SSH drops, MCP restarts...)

reconnect_to_tmux    → same tmux session, same cwd, vim still open

Deterministic Output

Instead of sleep 3 && capture, every exec call polls tmux capture-pane until the exact prompt string __MCP_PROMPT__> appears. No race conditions, no false positives from output that looks like a prompt.

Send command → grace interval → poll every 250ms → prompt detected → capture → return output

Auto-Recovery

On server start, ssh-agent-workspace scans all configured hosts for mcp_* tmux sessions and automatically reconnects. Disable with MCP_SSH_RESTORE_SESSIONS=false.

Runtime Reconfiguration

| Tool | What it does | |---|---| | tools_config | Enable/disable any tool at runtime. Persistent. tools_config itself can never be disabled. | | host_security | Set per-host read-only, command allowlist/denylist at runtime. Overrides global env vars per host. |

No restart needed. Changes apply immediately.

Three-Layer Security

| Layer | Scope | Mechanism | |---|---|---| | Global | All hosts | MCP_SSH_READONLY, MCP_SSH_ALLOWED_HOSTS, MCP_SSH_DENYLIST_COMMANDS | | Per-Host | Individual host | host_security tool: readonly, allow_commands, deny_commands | | Per-Operation | Single command/query | SQL keyword blocklist, path sanitization, shell escaping |


Tools (25)

| Tool | Description | |---|---| | connect | Create persistent tmux workspace on remote host | | reconnect_to_tmux | Reattach to existing workspace after disconnect | | exec | Run command, wait for prompt, return output | | send_input | Inject raw input (non-blocking) | | read_output | Capture pane tail | | interrupt | Ctrl-C / Ctrl-D signal | | disconnect | Close session. Optionally kill tmux or keep alive | | list_hosts | List ~/.ssh/config aliases | | list_sessions | List active workspaces |

| Tool | Description | |---|---| | sftp_upload | Upload file to remote | | sftp_download | Download file from remote | | sftp_list | List remote directory |

| Tool | Description | |---|---| | connection_status | SSH liveness + tmux existence | | health_check | CPU / RAM / Disk / Load / Uptime | | tail_log | Log tail with optional follow |

| Tool | Description | |---|---| | deploy | Upload → backup → chmod → chown → restart | | backup | tar.gz archive → download → cleanup | | sync | Rsync-lite via SFTP (bidirectional, dry-run) | | ssh_tunnel_open | Local port forward or SOCKS5 proxy | | ssh_tunnel_list | List active tunnels | | ssh_tunnel_close | Close tunnel, free port |

| Tool | Description | |---|---| | group_exec | Run command across multiple workspaces (parallel/sequential) | | db_query | Read-only MySQL / PostgreSQL / MongoDB via SSH |

| Tool | Description | |---|---| | tools_config | Enable/disable tools at runtime | | host_security | Per-host read-only, command allow/denylist |

Full reference: docs/TOOLS.md | Security: docs/SECURITY.md


Usage Examples

> connect host=prod
→ session_id: "sess_abc", tmux_session: "mcp_prod_x1y2z3"

> exec session_id=sess_abc command="cd /var/www && docker ps"
→ { output: "3 containers running" }

> exec session_id=sess_abc command="ls"
→ { output: "..." }  (cwd is still /var/www)

> group_exec session_ids=["sess_abc","sess_def"] command="uptime"
→ [{ host: "prod", output: "up 14 days" }, { host: "staging", output: "up 3 days" }]

> health_check session_id=sess_abc
→ { cpu: 12%, memory: 45%, disk: [{ "/": 56% }], uptime: "14 days" }

> db_query session_id=sess_abc type=mysql database=mydb query="SELECT COUNT(*) FROM users"
→ [{ "COUNT(*)": 15423 }]

> deploy session_id=sess_abc files=[{"local":"dist/app.js","remote":"/var/www/app.js"}] backup=true chmod="755" restart_service="nginx"

> host_security action=set host=prod readonly=true
→ Host 'prod' locked to read-only

> tools_config disable backup
→ Tool 'backup' disabled. Removed from MCP tool list.

Configuration

Environment Variables

| Variable | Default | Description | |---|---|---| | LOG_LEVEL | info | trace, debug, info, warn, error | | MCP_SSH_READONLY | false | Block all write operations globally | | MCP_SSH_ALLOWED_HOSTS | (all) | Comma-separated host whitelist | | MCP_SSH_DENYLIST_COMMANDS | (none) | Global command blocklist | | MCP_SSH_RESTORE_SESSIONS | true | Auto-restore workspaces on startup |


Project Structure

src/
├── core/
│   ├── SessionManager.ts       # Session registry + persistence
│   ├── SSHManager.ts           # SSH2 connections, SFTP, proxy-jump
│   ├── TmuxManager.ts          # Tmux operations (create, capture, signal)
│   ├── StorageManager.ts       # Persistent session storage (JSON)
│   ├── ToolConfigManager.ts    # Runtime tool enable/disable
│   └── HostSecurityManager.ts  # Per-host security policies
├── tools/                      # 25 MCP tool handlers
└── utils/                      # Security, SSH config parsing, logging, validation

Troubleshooting

ssh-add -l              # Verify SSH agent key
ssh <alias>             # Test manual login
chmod 600 ~/.ssh/id_*   # Fix key permissions

# Clean stale workspaces on remote
tmux ls | grep '^mcp_' | awk -F: '{print $1}' | xargs -I{} tmux kill-session -t {}

License

MIT