sshand
The sshand MCP server enables AI agents to securely manage remote Linux/Unix machines via SSH, providing shell execution, file operations, and host inventory management.
Host Management:
ssh_list_hosts: List all configured SSH targets (alias, hostname, port, username, auth type).ssh_add_host: Register a new SSH host with key-based, password, or SSH agent authentication.ssh_remove_host: Remove a host from the inventory.ssh_test_connection: Verify connectivity and authentication to a host.
Command Execution:
ssh_run_command: Execute arbitrary shell commands on a remote host, capturing stdout, stderr, and exit code; supports timeout, environment variables, working directory, and sudo.
File Operations:
ssh_read_file: Read remote files (text or base64-encoded binary).ssh_write_file: Create or overwrite remote files (text or base64-encoded binary); auto-creates missing parent directories.ssh_upload_file: Upload local files to a remote host via SFTP.ssh_download_file: Download remote files to the local machine via SFTP.
Directory Browsing:
ssh_list_directory: List directory contents with file names, types, sizes, permissions, and timestamps.
Local Machine Introspection:
ssh_get_local_info: Get the OS, home directory, working directory, and path style (Windows vs POSIX) of the MCP server host — useful before upload/download operations.
Enables AI agents in VS Code Copilot to interact with remote servers via SSH, running commands and managing files.
Allows AI agents on ChatGPT and the OpenAI Agents SDK to perform remote SSH operations including command execution, file read/write, and SFTP transfers.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sshandcheck the disk usage on production-server"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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. First-run setup: add a host, test it, print client config
sshand setup
# 3. Manage hosts any time (add / test / remove + reprint config)
sshand manageBoth commands open the same clean interactive terminal UI — use the arrow keys to move and Enter to select. setup runs the guided first-run flow; manage is the ongoing host manager.
Related MCP server: mcp-remote-ssh
Setup guide
sshand setup walks you through three steps:
1. Add a host. You'll be asked for:
Alias — a short nickname like
webserverordb-prod.Hostname / IP, port (default
22), and username.Authentication method:
SSH key file (recommended) — path to your private key, plus an optional passphrase.
Password — convenient for dev/test, avoid on internet-facing hosts.
SSH agent — delegates to your running
ssh-agent; no credentials stored. On Windows the wizard also checks whether the OpenSSH Authentication Agent service is running and offers to start it.
The host is saved to hosts.yaml (or the path in SSH_MCP_HOSTS_FILE). An existing host is never overwritten without confirmation.
2. Test the connection. SSHand immediately connects and runs a no-op command, so you find out right away if something is wrong (bad key path, wrong port, unreachable host) instead of mid-conversation later. The host is saved either way — if the test fails, fix the issue and run sshand setup again.
3. Print client config. Pick a client (or All of them) and SSHand prints a ready-to-paste config snippet with the correct absolute paths already filled in:
Claude Desktop · Cursor · VS Code (GitHub Copilot Chat) · OpenAI (Agents SDK / ChatGPT Desktop) · Hermes Agent · OpenClaw (via MCPorter) · Other (HTTP)
Full per-client instructions are in Connecting to your AI client below.
Run
sshand setupagain any time to add another host or reprint a snippet — it won't touch your existing hosts.
Managing your hosts
sshand manage opens the interactive host manager — a live table of your configured hosts plus a menu:
List hosts — refresh the table.
Add a host — the same flow as setup, with an optional connection test afterwards.
Test a host — connect and report success or failure.
Remove a host — delete an entry (with confirmation).
Client config snippets — reprint the paste-ready config for any client, prefaced with the list of hosts the agent will be able to reach.
The screen refreshes in place after each action, with the result of your last action shown above the menu. Press q / choose Quit to exit.
Installation options
Option A — pip (recommended for most users)
pip install sshand # from PyPI
pip install -e . # from source, editable installOption 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 wizardThis is the cleanest option to recommend to non-technical users.
Option C — plain Python (no install)
git clone https://github.com/muradmalik23/sshand
cd sshand
pip install -r requirements.txt
python server.py # start server
python manage.py setup # run the setup wizard
python manage.py # interactive host managerConnecting 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"]
}
}
}Or with plain Python:
{
"mcpServers": {
"ssh": {
"command": "python",
"args": ["/absolute/path/to/sshand/server.py"]
}
}
}No env var needed for either option — hosts are stored at ~/.sshand/hosts.yaml automatically. See Host inventory below if you want a different location.
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"]
}
}
}No env var needed — hosts are stored at ~/.sshand/hosts.yaml automatically.
VS Code (GitHub Copilot Chat)
Add to .vscode/mcp.json or your workspace settings.json:
{
"mcp": {
"servers": {
"ssh": {
"type": "stdio",
"command": "uvx",
"args": ["sshand"]
}
}
}
}No env var needed — hosts are stored at ~/.sshand/hosts.yaml automatically.
OpenAI Agents SDK
# Terminal 1 — keep this running
sshand --transport http --port 8000from 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])Hermes Agent
Hermes Agent (Nous Research) reads MCP server config from ~/.hermes/config.yaml under the mcp_servers key — same command/args/env shape as everywhere else:
mcp_servers:
ssh:
command: "uvx"
args: ["sshand"]If you installed SSHand from source instead of via uvx, point command at python and add ["/absolute/path/to/sshand/server.py"] as args, same as the Claude Desktop snippet above.
No env var needed either way — hosts are stored at ~/.sshand/hosts.yaml automatically.
Start (or reload) Hermes to pick it up:
hermes chat # fresh start
/reload-mcp # or, from inside a running sessionHermes prefixes every tool with mcp_<server_name>_, so e.g. ssh_run_command shows up as mcp_ssh_ssh_run_command — you won't normally need the prefixed name, Hermes picks the right tool from your prompt on its own.
OpenClaw
OpenClaw doesn't take MCP servers directly — it calls them through MCPorter, a separate CLI that OpenClaw shells out to for schema discovery and tool calls. Install MCPorter first:
npm install -g mcporterThen register SSHand with it:
mcporter config add ssh --command uvx --args sshandThat writes an entry to config/mcporter.json (or ~/.mcporter/mcporter.json for a machine-wide install) in the same mcpServers shape used everywhere else:
{
"mcpServers": {
"ssh": {
"command": "uvx",
"args": ["sshand"]
}
}
}No env var needed — hosts are stored at ~/.sshand/hosts.yaml automatically.
Confirm MCPorter can see it and list the tools:
mcporter list ssh --schemaNo further OpenClaw-side config is needed — just ask it to do something that needs SSH (e.g. "check disk usage on webserver") and OpenClaw will invoke mcporter call ssh.ssh_run_command ... on its own.
Any other MCP client (HTTP)
sshand --transport http --port 8000MCP 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 ~/.sshand/hosts.yaml, created automatically on startup regardless of how SSHand was installed (pip, uvx, or from source) — no env var or extra config required. You can edit the file directly or let your agent call ssh_add_host.
Want a different location instead? Set SSH_MCP_HOSTS_FILE to override it, e.g. "env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" } in any of the MCP configs above.
Running from a git clone and want the inventory to live next to the source instead? Point SSH_MCP_HOSTS_FILE at a repo-local copy:
cp hosts.yaml.example hosts.yaml
export SSH_MCP_HOSTS_FILE="$PWD/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 encryptedPassword — convenient for dev/test, avoid on internet-facing servers:
auth:
type: password
password: s3cr3tSSH agent — no credentials stored at all, delegates to the running ssh-agent:
auth:
type: agentAvailable tools
Tool | Description | Read-only? |
| List all configured SSH targets | ✅ |
| Register a new SSH host | ❌ |
| Remove a host from inventory | ❌ |
| Verify auth works for a host | ✅ |
| Execute a shell command + capture output | ❌ |
| Read a remote file's contents | ✅ |
| Create or overwrite a remote file | ❌ |
| Browse a remote directory | ✅ |
| Push a local file to the remote host (SFTP) | ❌ |
| Pull a remote file to this machine (SFTP) | ✅ |
| 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 (add a host, test, print config)
manage Interactive host manager (add / test / remove + config snippets)
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 manage # interactive host manager
sshand --transport http # start HTTP server on :8000Security notes
By default your inventory lives outside the repo at
~/.sshand/hosts.yaml, so it can't accidentally land in version control. If you've pointedSSH_MCP_HOSTS_FILEat a repo-localhosts.yaml, keep it out of git — it's already excluded by the included.gitignore.Prefer key-based or agent auth over password auth for any internet-facing host.
The HTTP transport binds to
127.0.0.1by default.When exposing the HTTP server publicly (for Claude.ai / ChatGPT web), use TLS and consider adding authentication via a reverse proxy.
ssh_run_commandis markeddestructiveHint: 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
├── manage.py # Interactive TUI — setup wizard + host manager (rich + questionary)
├── setup_wizard.py # Client-config snippet builders + Windows agent helpers (used by manage.py)
├── platform_utils.py # Windows SSH agent helpers
├── hosts.yaml.example # Safe template — for repo-local SSH_MCP_HOSTS_FILE setups
├── hosts.yaml # Optional: only present if SSH_MCP_HOSTS_FILE points here (gitignored)
├── INTEGRATIONS.md # Guide: Claude.ai and ChatGPT native extensions
├── pyproject.toml # Package metadata + pip/uvx install config
├── requirements.txt # Plain pip install fallback
└── README.mdAvailable Tools
11 toolsssh_add_hostA
Register a new SSH host in the inventory.
Saves the host details to hosts.yaml so it can be referenced by alias in all other ssh_* tools. Supports key, password, and agent authentication.
Args: alias: Short nickname for the host (e.g., 'webserver', 'db-prod'). Only letters, digits, underscores, and hyphens are allowed. hostname: IP address or FQDN (e.g., '192.168.1.10', 'db.example.com'). username: SSH login username (e.g., 'ubuntu', 'ec2-user'). auth_type: Authentication method — 'key' (private key file), 'password', or 'agent' (ssh-agent). port: SSH port (default 22). key_path: Required when auth_type='key'. Path to the private key file (e.g., '~/.ssh/id_rsa'). key_passphrase: Optional passphrase to decrypt an encrypted private key. password: Required when auth_type='password'. description: Optional human-readable note about this server. overwrite: Set True to replace an existing host with the same alias.
Returns: Success message or error description.
Examples: - Add a key-based host: alias='webserver', hostname='10.0.0.5', username='ubuntu', auth_type='key', key_path='~/.ssh/id_rsa' - Add a password host: alias='legacy', hostname='old.corp.net', username='admin', auth_type='password', password='s3cr3t' - Add an agent-auth host: alias='jump', hostname='bastion.example.com', username='ops', auth_type='agent'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| hostname | Yes | ||
| username | Yes | ||
| auth_type | Yes | ||
| port | No | ||
| key_path | No | ||
| key_passphrase | No | ||
| password | No | ||
| description | No | ||
| overwrite | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses supported authentication methods, parameter dependencies, and returns success/error, though annotations are minimal; misses details on side effects or permissions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with clear sections (Args, Returns, Examples); slightly verbose but each sentence adds value for a 10-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers all parameters, examples, and return type; lacks details about the hosts.yaml format or persistence, but sufficient for a registration tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description fully compensates by explaining each parameter, constraints, and conditional requirements (e.g., key_path for 'key' auth).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the action ('Register a new SSH host') and the storage location ('hosts.yaml'), distinguishing it from sibling tools like ssh_remove_host or ssh_list_hosts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides examples for different auth types and explains the overwrite parameter, but does not explicitly compare to alternatives or state when not to use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_download_fileAIdempotent
Download a file from a remote SSH host to the local machine via SFTP.
The local parent directory is created automatically if needed.
Args: alias: Source host alias. remote_path: Absolute POSIX path of the file on the remote host. local_path: Absolute destination path on the local machine, in the format native to the machine running the MCP server (e.g. 'C:\Users\me\downloads\file.txt' on Windows, '/home/me/downloads/file.txt' on Linux/macOS). Call ssh_get_local_info first if you are unsure which format to use.
Returns: Success message with byte count, or an error description.
Examples: - Fetch a log: remote_path='/var/log/app.log', local_path='/tmp/app.log' - Pull a DB dump: remote_path='/backups/db.sql.gz', local_path='/tmp/db.sql.gz'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| remote_path | Yes | ||
| local_path | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (not read-only, not destructive, idempotent, open world), the description adds that local parent directories are automatically created and indicates return format (success with byte count or error). It does not specify overwrite behavior, but no contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: a concise summary, behavioral note, parameter details, return description, and examples. It is front-loaded with the main purpose, and every part adds value without fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers parameter semantics, automatic directory creation, return format, and examples. It does not discuss overwrite behavior or authentication, but given idempotentHint and sibling tools for host management, these are not critical gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema coverage, the description fully compensates by providing clear semantics: alias is source host alias, remote_path is absolute POSIX path, local_path includes format differences across OS and advice on calling ssh_get_local_info. Examples further clarify usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool downloads a file from a remote SSH host to the local machine via SFTP, specifying direction and method. It distinguishes from siblings like ssh_read_file (content reading) and ssh_upload_file (uploading), and includes automatic local directory creation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description advises calling ssh_get_local_info for path format guidance and provides examples. It implicitly suggests when to use (file transfer from remote to local) but does not explicitly exclude scenarios or contrast with alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_get_local_infoARead-onlyIdempotent
Return OS, home directory, cwd, and path style of the machine running the MCP server.
Call this before ssh_upload_file or ssh_download_file to discover the correct local_path format (Windows backslash vs POSIX forward-slash).
Args: response_format: 'json' (default) or 'markdown'.
Returns: OS info including: os, home, cwd, path_style ('windows' or 'posix').
Examples: - Before uploading: ssh_get_local_info() → learn that local_path needs Windows format - Debugging path errors: confirm the server's working directory
| Name | Required | Description | Default |
|---|---|---|---|
| response_format | No | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. The description does not contradict these and adds details about the return fields (os, home, cwd, path_style) and the two response formats. No additional behavioral traits are disclosed beyond what annotations imply, but the description is consistent and informative.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured. It begins with the core purpose, then provides usage guidance, followed by structured sections for Args, Returns, and Examples. Every sentence adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (one optional parameter, non-destructive, read-only), the description covers purpose, usage, parameter details, return fields, and examples. The inclusion of an output schema is noted, but the description itself is complete for an agent to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, response_format, is documented with its default value and enum options in the 'Args' section of the description. Since schema coverage is 0%, the description fully carries the burden of explaining the parameter, which it does adequately.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns OS, home directory, cwd, and path style of the MCP server machine. It provides a specific verb and resource, and distinguishes itself from sibling tools like ssh_upload_file and ssh_download_file by explicitly stating when to call it beforehand.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Call this before ssh_upload_file or ssh_download_file to discover the correct local_path format' and also mentions debugging path errors. This provides clear guidance on when to use the tool and the context for its use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_list_directoryARead-onlyIdempotent
List the contents of a directory on a remote SSH host.
Returns file names, types, sizes, permissions, and last-modified timestamps. Directories are listed before files, both sorted alphabetically.
Args: alias: Host alias. remote_path: Directory path to list (default '~' = home directory). limit: Maximum number of entries to return. Useful for large directories like /proc or /var/log. Omit to return all entries. response_format: 'markdown' (default) or 'json'.
Returns: Directory listing with columns: Type | Name | Size | Permissions | Modified. JSON format: {path, total, count, truncated, entries: [{name, type, size, permissions, modified}]}
Examples: - Browse home dir: ssh_list_directory(alias='web') - List /var/log: remote_path='/var/log' - List /etc/nginx: remote_path='/etc/nginx'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| remote_path | No | ~ | |
| limit | No | ||
| response_format | No | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate a safe read-only operation. The description adds behavioral details such as sorting order (directories first, then alphabetically) and default path handling, providing useful context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise: an initial summary sentence, followed by clear sections for args, returns, and examples. Every sentence adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (4 parameters, output schema, no nested objects), the description covers all aspects: parameter meanings, return format (both markdown and JSON), sorting behavior, and practical examples. It is fully adequate for agent use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero description coverage, but the description thoroughly explains each parameter (alias, remote_path, limit, response_format) with details and examples, fully compensating for the missing schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool lists directory contents on a remote SSH host, with a specific set of attributes (file names, types, sizes, etc.). It distinguishes itself from sibling tools like ssh_read_file and ssh_run_command by focusing on directory listing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains what the tool does and provides examples, giving clear context for when to use it. However, it does not explicitly mention when not to use it or alternatives, though this is implicit from sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_list_hostsARead-onlyIdempotent
List all SSH hosts registered in the inventory.
Returns the alias, hostname, port, username, and auth type for every configured host. Call this first when you don't know what machines are available.
Args: response_format: 'markdown' (default) for a readable table, or 'json' for structured data.
Returns: Formatted list of hosts, or a message if the inventory is empty.
Examples: - "What servers can I connect to?" -> ssh_list_hosts() - "Show me all SSH targets as JSON" -> ssh_list_hosts(response_format='json')
| Name | Required | Description | Default |
|---|---|---|---|
| response_format | No | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. Description adds that it returns a formatted list or message if empty, with response format options. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is concise with 3 main sentences plus structured Args/Returns/Examples. Every sentence is informative, and the purpose is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with one optional parameter and an output schema, the description covers functionality, return value, parameter usage, and gives examples. No gaps for this complexity level.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, but description explains the response_format parameter: 'markdown (default) for a readable table, or json for structured data'. This adds meaningful semantics beyond the enum listing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states 'List all SSH hosts registered in the inventory' and enumerates the fields returned. It distinguishes from sibling SSH tools that perform other operations like add, remove, or file operations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Description advises 'Call this first when you don't know what machines are available', providing clear context. It does not explicitly list exclusions but the context makes usage intuitive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_read_fileARead-onlyIdempotent
Read the contents of a file on a remote SSH host.
Uses SFTP to transfer the file. Suitable for config files, logs, scripts, etc. For binary files set encoding='raw' to get a base64-encoded string.
Args: alias: Host alias. remote_path: Absolute path of the file to read on the remote host (e.g., '/etc/nginx/nginx.conf'). encoding: Text encoding to decode the file (default 'utf-8'). Use 'raw' to get a base64-encoded string for binary files.
Returns: File contents as text, or base64-encoded bytes for encoding='raw'. Returns an error string on failure.
Examples: - Read nginx config: alias='web', remote_path='/etc/nginx/nginx.conf' - Read /etc/hosts: remote_path='/etc/hosts' - Read binary file: remote_path='/usr/bin/ls', encoding='raw'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| remote_path | Yes | ||
| encoding | No | utf-8 |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, destructiveHint=false, idempotentHint=true, covering the safety profile. The description adds that it uses SFTP and returns an error on failure, which is useful but not extensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with Args, Returns, and Examples sections. Every sentence serves a purpose, and no unnecessary information is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, parameters, return type, and provides examples. An output schema exists, so return details are handled. It is complete for a tool of this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description fully explains all three parameters: alias, remote_path, and encoding. It provides examples and default behavior, adding significant meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Read the contents of a file on a remote SSH host.' This is a specific verb and resource. Among siblings like ssh_list_directory and ssh_download_file, it is distinct.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description mentions suitability for config files, logs, scripts, and binary files with encoding='raw'. It provides context for when to use, though it doesn't explicitly exclude alternatives like ssh_download_file for transfers.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_remove_hostADestructive
Remove a host from the SSH inventory permanently.
This only removes the local config entry — it does NOT touch the remote server in any way.
Args: alias: The host alias to remove.
Returns: Confirmation or error message.
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds value beyond annotations by clarifying that removal is permanent and local-only. This supplements the destructiveHint and readOnlyHint annotations effectively.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with three short paragraphs, each serving a purpose: main action, important caveat, and parameter/return info. No extraneous content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple removal tool with one parameter and an output schema, the description covers the action, side effects, parameter meaning, and return value adequately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description includes 'alias: The host alias to remove,' which adds meaning to the single required parameter. With 0% schema coverage, this is essential and well-provided.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Remove a host from the SSH inventory permanently') and the resource ('host'), distinguishing it from siblings like ssh_add_host or ssh_list_hosts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly notes that removal only affects the local config and does not touch the remote server, providing clear context on when to use the tool. No explicit alternatives are mentioned, but the scope is well-defined.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_run_commandADestructive
Execute a shell command on a remote SSH host and return the output.
The command runs in the login user's default shell. Both stdout and stderr are captured and returned. The exit code is included so you can detect failures.
Args: alias: Target host alias. command: Shell command to execute (e.g., 'ls -la /var/log', 'systemctl status nginx', 'df -h'). timeout: Maximum seconds to wait (default 60, max 3600). env: Optional extra environment variables dict for this command. cwd: Remote working directory. When set the effective invocation becomes 'cd && '. Use absolute POSIX paths (e.g. '/var/www/html'). State is NOT persisted between calls. sudo_password: When set, the command runs via sudo. The password is never echoed back in output. response_format: 'markdown' (default, human-readable) or 'json'.
Returns: Command output (stdout + stderr) with exit code.
Examples: - Inspect disk usage: command='df -h' - Check a service: command='systemctl status nginx' - Tail logs: command='tail -n 50 /var/log/syslog' - Install a package: command='sudo apt-get install -y htop'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| command | Yes | ||
| timeout | No | ||
| env | No | ||
| cwd | No | ||
| sudo_password | No | ||
| response_format | No | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Describes exit code, stdout/stderr capture, sudo behavior, and non-persistent cwd state. Annotations already include destructiveHint=true; description adds useful context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with Args, Returns, Examples. Front-loaded with purpose. Every sentence adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Comprehensive for a command execution tool: explains return values, timeout limits, response_format options, and provides multiple examples. Output schema exists but description covers key behavioral aspects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema coverage, description provides detailed explanations for all 7 parameters, including constraints, defaults, and examples (e.g., timeout max 3600, cwd usage).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states 'Execute a shell command on a remote SSH host and return the output.' Differentiates from sibling tools like ssh_add_host or ssh_list_directory.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Description implicitly defines usage via examples and sibling context; lacks explicit when-not-to-use or alternatives, but purpose is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_test_connectionARead-onlyIdempotent
Verify that a host is reachable and authentication succeeds.
Attempts to open an SSH session and run a trivial echo command. Use this immediately after ssh_add_host to confirm your credentials are correct.
Args: alias: Host alias to test (must exist in inventory).
Returns: '✓ Connected …' on success, or an actionable error on failure.
Examples: - After adding a new host: ssh_test_connection(alias='webserver') - Debugging a connection issue: ssh_test_connection(alias='db-prod')
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Description adds behavioral details beyond annotations: it explains that the tool 'Attempts to open an SSH session and run a trivial echo command.' This clarifies the non-destructive, read-only nature consistent with readOnlyHint=true and idempotentHint=true. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and concise. It opens with a clear purpose, then breaks down into usage, parameter explanation, return values, and examples. Every sentence adds value; no verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simplicity of the tool (single parameter, no nested objects, output schema present), the description is complete. It covers the action, prerequisite, return format, and provides examples. No gaps remain for the agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With one parameter and 0% schema coverage, the description fully compensates: 'alias: Host alias to test (must exist in inventory).' This provides clear semantics and a prerequisite, making the parameter meaningful.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Verify that a host is reachable and authentication succeeds.' It uses a specific verb-resource pair and distinguishes itself from siblings like ssh_run_command and ssh_add_host by focusing solely on connectivity and credential verification.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit usage guidance is provided: 'Use this immediately after ssh_add_host to confirm your credentials are correct.' Examples illustrate typical use cases, and the description implies when to use (e.g., debugging). No contradictory or missing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_upload_fileADestructiveIdempotent
Upload a local file to a remote SSH host via SFTP.
The remote parent directory is created automatically if it doesn't exist. Use this to deploy configs, binaries, or any large file.
Args: alias: Target host alias. local_path: Absolute path to the local file to upload, in the format native to the machine running the MCP server (e.g. 'C:\Users\me\file.txt' on Windows, '/home/me/file.txt' on Linux/macOS). Call ssh_get_local_info first if you are unsure which format to use. remote_path: Absolute POSIX destination path on the remote host (e.g. '/home/user/file.txt').
Returns: Success message with byte count, or an error description.
Examples: - Deploy an app binary: local_path='/dist/myapp', remote_path='/opt/myapp/bin/myapp' - Upload a TLS cert: local_path='/tmp/server.crt', remote_path='/etc/ssl/certs/server.crt'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| local_path | Yes | ||
| remote_path | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds behavioral context beyond annotations by noting that the remote parent directory is created automatically. It also describes the return format (success message with byte count). Annotations already indicate destructive and idempotent behavior, so the description complements them well.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured with clear sections for Args, Returns, and Examples. Every sentence adds value, and the main action is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has three parameters, all are thoroughly described. Return value is explained, and examples provide practical context. No gaps remain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description provides detailed parameter explanations for all three parameters, including format requirements for local_path (native OS) and remote_path (POSIX), and examples. This fully compensates for the lack of schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool uploads a local file to a remote SSH host via SFTP, using specific verbs and resources. It distinguishes itself from sibling tools like ssh_download_file, which downloads files.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use the tool (deploy configs, binaries, large files) and advises calling ssh_get_local_info first if unsure about local path format. However, it does not explicitly state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_write_fileADestructiveIdempotent
Write content to a file on a remote SSH host (create or overwrite).
Uses SFTP to transfer the content. Missing parent directories are created automatically. For binary content, base64-encode it and set encoding='base64'.
Args: alias: Host alias. remote_path: Absolute destination path on the remote host. content: Text to write. For binary content, encode as base64 and set encoding='base64'. encoding: 'utf-8' to write the content as text (default), 'base64' to decode first.
Returns: Success message with byte count, or an error description.
Examples: - Write a cron job: remote_path='/etc/cron.d/myjob', content='...' - Deploy a config: remote_path='/etc/myapp/config.yaml', content='...' - Write a script: remote_path='/usr/local/bin/deploy.sh', content='...'
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | ||
| remote_path | Yes | ||
| content | Yes | ||
| encoding | No | utf-8 |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate destructiveHint=true and idempotentHint=true. The description adds useful behavioral details: uses SFTP, automatically creates missing parent directories, and handles two encoding modes. This provides context beyond annotations without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized: summary sentence, mechanism, args in list format, return type, and examples. All information is front-loaded and every sentence adds value, resulting in an efficient and readable definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity, the description covers all necessary aspects: purpose, parameters, encoding handling, return format, and examples. Combined with annotations and schema, an agent has sufficient information to invoke the tool correctly without ambiguity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the burden. It provides terse but sufficient descriptions for all four parameters (alias, remote_path, content, encoding) and clarifies the default encoding. While not exhaustive, it adds meaning beyond the schema itself.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb+resource statement: 'Write content to a file on a remote SSH host (create or overwrite).' This immediately distinguishes it from siblings like ssh_read_file (read) and ssh_download_file (download from remote), making the tool's unique role obvious.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides concrete examples (cron job, config, script) and explains how to handle binary content via base64 encoding. It does not explicitly contrast with similar tools like ssh_upload_file, which slightly reduces guidance, but the examples and encoding details still offer solid usage direction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
11 tool updates
v0.1.0- First observed
ssh_add_host - First observed
ssh_download_file - First observed
ssh_get_local_info - First observed
ssh_list_directory - First observed
ssh_list_hosts - First observed
ssh_read_file - First observed
ssh_remove_host - First observed
ssh_run_command - First observed
ssh_test_connection - First observed
ssh_upload_file - First observed
ssh_write_file
TDQS
Each tool targets a distinct operation: host inventory management, file transfer, command execution, directory listing, and connection testing. There is no functional overlap between any two tools.
All tools follow the 'ssh_verb_noun' pattern consistently (e.g., ssh_add_host, ssh_list_hosts, ssh_run_command). The naming is uniform and predictable.
With 11 tools, the set covers all essential SSH operations without being excessive. The count is well within the typical 3-15 range for a focused server.
The tool surface provides CRUD for hosts, remote command execution, file read/write/upload/download, directory listing, and connection testing. The only minor gap is the lack of an update host tool, but this can be worked around.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
An MCP server that gives your AI access to the source code and docs of all public github repos
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI assistants full SSH/SFTP remote operations — session management, command execution, interactive shells, file transfers, port forwarding, and system diagnostics.2MIT
- AlicenseAqualityAmaintenanceMCP server giving AI agents full SSH access with persistent sessions, structured command output, SFTP file transfer, and port forwarding.1810MIT
- AlicenseAqualityCmaintenanceAn MCP server that gives AI agents SSH access to remote machines through your local OpenSSH client, enabling remote command execution, file transfer, persistent shell sessions, and port forwarding.1716MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI agents SSH capabilities to execute commands, transfer files, and inspect remote systems through a preconfigured host list.84MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/muradmalik23/sshand'
If you have feedback or need assistance with the MCP directory API, please join our Discord server