Skip to main content

MCP server — give any AI agent SSH access to remote Linux/Unix machines

Project description

SSHand

An open MCP server that gives any AI agent SSH access to remote Linux/Unix machines — shell commands, file read/write, and SFTP transfers.

Works with Claude.ai, Claude Desktop, ChatGPT, Cursor, VS Code Copilot, OpenAI Agents SDK, and any other MCP-compatible client.


Quick start

# 1. Install
pip install sshand

# 2. Run the interactive setup wizard
sshand setup

The wizard walks you through adding your first host, testing the connection, and copying the right config snippet for whichever AI client you use.


Installation options

Option A — pip (recommended for most users)

pip install sshand        # from PyPI
pip install -e .            # from source, editable install

Option B — uvx (zero-install, no venv needed)

uv is the modern Python package manager. With it installed you can run SSHand without any manual install step:

uvx sshand                # run server directly
uvx sshand setup          # run setup wizard

This is the cleanest option to recommend to non-technical users.

Option C — plain Python (no install)

git clone https://github.com/YOUR_GITHUB_USERNAME/sshand
cd sshand
pip install -r requirements.txt
python server.py            # start server
python setup_wizard.py      # run wizard

Connecting to your AI client

Claude.ai and ChatGPT (web + desktop)

For Claude.ai web and ChatGPT, SSHand runs as an HTTP server and you connect it through each app's native integrations UI.

→ See INTEGRATIONS.md for the full step-by-step guide, including how to expose the server publicly using ngrok or Cloudflare Tunnel, and how to add it to Claude.ai Integrations and ChatGPT Desktop.


Claude Desktop

Add to your claude_desktop_config.json:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "ssh": {
      "command": "uvx",
      "args": ["sshand"],
      "env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
    }
  }
}

Or with plain Python:

{
  "mcpServers": {
    "ssh": {
      "command": "python",
      "args": ["/absolute/path/to/sshand/server.py"],
      "env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/sshand/hosts.yaml" }
    }
  }
}

Restart Claude Desktop after saving.


Cursor

Create or update .cursor/mcp.json in your project (or the global Cursor MCP settings):

{
  "mcpServers": {
    "ssh": {
      "command": "uvx",
      "args": ["sshand"],
      "env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
    }
  }
}

VS Code (GitHub Copilot Chat)

Add to .vscode/mcp.json or your workspace settings.json:

{
  "mcp": {
    "servers": {
      "ssh": {
        "type": "stdio",
        "command": "uvx",
        "args": ["sshand"],
        "env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
      }
    }
  }
}

OpenAI Agents SDK

# Terminal 1 — keep this running
sshand --transport http --port 8000
from agents import Agent
from agents.mcp import MCPServerStreamableHttp

ssh_server = MCPServerStreamableHttp(url="http://localhost:8000/mcp")
agent = Agent(name="ops-agent", mcp_servers=[ssh_server])

Any other MCP client (HTTP)

sshand --transport http --port 8000

MCP endpoint: http://localhost:8000/mcp

For remote access, put a reverse proxy (nginx / Caddy) in front with TLS. Never expose port 8000 directly on a public interface.


Host inventory

Hosts are stored in hosts.yaml (set a different path with the SSH_MCP_HOSTS_FILE env var). You can edit the file directly or let your agent call ssh_add_host.

Copy hosts.yaml.example to hosts.yaml to get started:

cp hosts.yaml.example hosts.yaml

Auth types

Key file — most common, most secure:

auth:
  type: key
  key_path: ~/.ssh/id_rsa       # ~ is expanded automatically
  passphrase: null              # set if the key is encrypted

Password — convenient for dev/test, avoid on internet-facing servers:

auth:
  type: password
  password: s3cr3t

SSH agent — no credentials stored at all, delegates to the running ssh-agent:

auth:
  type: agent

Available tools

Tool Description Read-only?
ssh_list_hosts List all configured SSH targets
ssh_add_host Register a new SSH host
ssh_remove_host Remove a host from inventory
ssh_test_connection Verify auth works for a host
ssh_run_command Execute a shell command + capture output
ssh_read_file Read a remote file's contents
ssh_write_file Create or overwrite a remote file
ssh_list_directory Browse a remote directory
ssh_upload_file Push a local file to the remote host (SFTP)
ssh_download_file Pull a remote file to this machine (SFTP)
ssh_get_local_info Return OS and path style of the MCP server host

Example conversations

"What servers do you have access to?"ssh_list_hosts

"Check disk usage on webserver"ssh_run_command(alias='webserver', command='df -h')

"Read the nginx config on the web box"ssh_read_file(alias='webserver', remote_path='/etc/nginx/nginx.conf')

"Tail the last 100 lines of syslog on bastion"ssh_run_command(alias='bastion', command='tail -n 100 /var/log/syslog')

"Deploy this config file to devbox"ssh_write_file(alias='devbox', remote_path='/etc/myapp/config.yaml', content='...')

"Download today's DB backup from webserver"ssh_download_file(alias='webserver', remote_path='/backups/db-today.sql.gz', local_path='/tmp/db-today.sql.gz')


CLI reference

sshand [subcommand] [options]

Subcommands:
  setup                  Interactive first-run wizard

Options:
  --transport {stdio,http}   Transport (default: stdio)
  --port INT                 HTTP port (default: 8000)
  --host STR                 HTTP bind address (default: 127.0.0.1)

Examples:
  sshand                    # start stdio server
  sshand setup              # first-run wizard
  sshand --transport http   # start HTTP server on :8000

Security notes

  • Keep hosts.yaml out of version control if it contains passwords — it is excluded by the included .gitignore. Copy hosts.yaml.example to hosts.yaml to get started.
  • Prefer key-based or agent auth over password auth for any internet-facing host.
  • The HTTP transport binds to 127.0.0.1 by default.
  • When exposing the HTTP server publicly (for Claude.ai / ChatGPT web), use TLS and consider adding authentication via a reverse proxy.
  • ssh_run_command is marked destructiveHint: true — MCP clients that respect annotations will prompt before running potentially dangerous commands.

Project structure

sshand/
├── server.py           # FastMCP server — all 11 tools + CLI entry point
├── ssh_client.py       # Async paramiko wrapper (command exec + SFTP)
├── host_config.py      # YAML host inventory manager
├── setup_wizard.py     # Interactive first-run setup wizard
├── platform_utils.py   # Windows SSH agent helpers
├── hosts.yaml.example  # Safe template — copy to hosts.yaml and fill in your values
├── hosts.yaml          # Your SSH targets (gitignored — copy from hosts.yaml.example)
├── INTEGRATIONS.md     # Guide: Claude.ai and ChatGPT native extensions
├── pyproject.toml      # Package metadata + pip/uvx install config
├── requirements.txt    # Plain pip install fallback
└── README.md

Project details


Download files

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

Source Distribution

sshand-0.1.0.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

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

sshand-0.1.0-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sshand-0.1.0.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for sshand-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9658e413242ee6cf48e49b156490510a92dd5b04f16d551b65fcb8ef71a44125
MD5 b23ae739218c428c63adc3935388dbbe
BLAKE2b-256 364cf7ac36377a5faba0e4ab0b89aa1977d2078777d284c7243a8841c4ec4028

See more details on using hashes here.

File details

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

File metadata

  • Download URL: sshand-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 29.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for sshand-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8c9647ca314002e4eb1e2e95e9b84e565add952ef6512454d5b35d0a8d80e40a
MD5 ccb8aa050d48f593202d2fd6c007dc0e
BLAKE2b-256 1a67bf79a84a3487ea47195a7b1bc9dc82ed844364e9543db846406efbdd5a76

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page