SSH Host Configuration¶
Overview¶
The MCP server uses a TOML configuration file to store SSH host credentials and settings. This allows you to pre-configure hosts and connect to them by name or alias rather than providing credentials each time.
File Locations¶
The server looks for host configurations in this order:
- Explicit path via
--configcommand-line parameter - User home directory:
~/.mcp_ssh_hosts.toml - Current working directory:
./mcp_ssh_hosts.toml
The file is created automatically the first time the server starts, at whichever
of the paths above it resolves to (home directory takes priority if --config isn't
given). If none of these files exist yet, the server creates an empty one at the
home-directory path with a few commented-out example entries, and sets its
permissions to 0o600 (owner read/write only) immediately. You don't need to create
this file yourself before adding your first host - ssh_conn_add_host (or hand-editing
the file, if you prefer) both just work against whatever file already exists.
If a file already exists at the resolved path, it is never overwritten or reset -
auto-creation is skipped entirely in that case (if not self.config_path.exists()
in SshHostManager._ensure_config_file), so restarting the server, upgrading the
package, or reinstalling it can never wipe out hosts you've already configured.
File Format¶
The TOML file uses section names in user@hostname format:
[user@hostname]
password = "plaintext_password" # Password authentication
keyfile = "~/.ssh/id_rsa" # Key-based authentication (alternative to password)
key_passphrase = "passphrase" # Passphrase for encrypted keys (optional)
port = 22 # SSH port (default: 22)
sudo_password = "sudo_pass" # Password for sudo operations
alias = "shortname" # Friendly name for quick access
description = "Server description" # Human-readable description
Note: At least one of password or keyfile must be provided.
Authentication Methods¶
Password Authentication¶
Traditional password-based login:
[admin@server.example.com]
password = "mypassword"
port = 22
Key-Based Authentication¶
Using an SSH private key (recommended):
[deploy@server.example.com]
keyfile = "~/.ssh/id_ed25519"
port = 22
Key with Passphrase¶
For encrypted private keys:
[admin@secure.example.com]
keyfile = "~/.ssh/id_rsa"
key_passphrase = "my_key_passphrase"
port = 22
Both Methods (Key Preferred)¶
Key authentication is attempted first, with password as fallback:
[ops@server.example.com]
keyfile = "~/.ssh/id_rsa"
password = "fallback_password"
port = 22
Configuration Fields¶
| Field | Required | Default | Description |
|---|---|---|---|
password |
No* | - | SSH password for authentication |
keyfile |
No* | - | Path to SSH private key file |
key_passphrase |
No | - | Passphrase for encrypted SSH keys |
port |
No | 22 | SSH port number |
sudo_password |
No | Same as password |
Password for sudo operations |
alias |
No | - | Short name for quick connection |
description |
No | - | Human-readable server description |
*At least one of password or keyfile must be provided.
Example Configuration¶
# Development server (password auth)
[developer@dev.example.com]
password = "devpass123"
port = 22
alias = "dev"
description = "Development environment"
# Production server (key auth with sudo password)
[admin@prod.example.com]
keyfile = "~/.ssh/prod_key"
sudo_password = "sudoPassword"
port = 2222
alias = "prod"
description = "Production web server"
# Staging server (key with passphrase)
[deploy@staging.example.com]
keyfile = "~/.ssh/staging_ed25519"
key_passphrase = "staging_passphrase"
alias = "staging"
description = "Staging environment for testing"
# CI/CD deployment (key auth, passwordless sudo on server)
[deploy@ci.example.com]
keyfile = "~/.ssh/ci_deploy_key"
port = 22
alias = "ci"
description = "CI/CD deployment target"
Connecting to Hosts¶
By Full Key¶
ssh_conn_connect(host_name="admin@prod.example.com")
By Alias¶
ssh_conn_connect(host_name="prod")
Both methods work interchangeably. The server first checks if the identifier matches a user@host key, then searches by alias.
Tip: If a host is reachable both directly (its own configured alias) and indirectly by SSH'ing through another host, prefer connecting via its direct alias when one exists. Nesting SSH through an intermediate host adds an extra hop where key/password mismatches can surface confusingly (auth errors that look like they belong to the wrong host), even when the direct alias works fine.
Listing Configured Hosts¶
Use ssh_host_list() to view all configured hosts:
{
"hosts": {
"developer@dev.example.com": {
"port": 22,
"has_password": true,
"has_keyfile": false,
"has_sudo_password": false,
"alias": "dev",
"description": "Development environment"
},
"admin@prod.example.com": {
"port": 2222,
"has_password": false,
"has_keyfile": true,
"has_sudo_password": true,
"alias": "prod",
"description": "Production web server"
}
},
"count": 2,
"config_path": "/home/user/.mcp_ssh_hosts.toml"
}
Adding Hosts at Runtime¶
Hosts can be added programmatically without editing the config file:
With Password¶
ssh_conn_add_host(
host="server.example.com",
user="admin",
password="password123",
port=22,
sudo_password="sudopass",
alias="myserver",
description="My new server"
)
With SSH Key¶
ssh_conn_add_host(
host="server.example.com",
user="deploy",
keyfile="~/.ssh/deploy_key",
port=22,
alias="deploy-server",
description="Deployment server"
)
With Encrypted Key¶
ssh_conn_add_host(
host="secure.example.com",
user="admin",
keyfile="~/.ssh/id_rsa",
key_passphrase="my_passphrase",
sudo_password="sudopass",
port=22
)
This saves the host to the configuration file for future use.
Removing Hosts¶
ssh_host_remove(host_name="admin@server.example.com")
# or by alias
ssh_host_remove(host_name="myserver")
Updating Hosts¶
To change a field on an existing host (rotate a password, change the port, add a
description) without losing every other field, use ssh_host_update rather than
removing and re-adding. Only the fields you pass are changed; pass an empty string
"" to explicitly clear a field (e.g. dropping a password when switching a host to
key-only auth):
ssh_host_update(host_name="admin@server.example.com", password="new_password123")
ssh_host_update(host_name="myserver", port=2222, description="Moved to new port")
ssh_host_update(host_name="myserver", password="", keyfile="~/.ssh/id_ed25519")
Using an Alternate Host List¶
By default, every host tool (ssh_host_list, ssh_conn_connect,
ssh_conn_add_host, ssh_host_update, ssh_host_remove) operates against the
single config file the server resolved at startup (see File Locations above). If you
maintain more than one host list - e.g. separate files per project or environment -
use ssh_host_use_config to point all of these tools at a different file for the
rest of the session, without restarting the server or merging everything into one
file:
ssh_host_use_config(config_path="~/projects/client-a/hosts.toml")
# ssh_host_list, ssh_conn_connect, etc. now all use client-a/hosts.toml
ssh_host_use_config() # or config_path=""
# back to the server's default config file
The alternate file must already exist and be valid host configuration TOML - unlike
the server's own default file, this does not auto-create a missing path (an
LLM-supplied typo in a path should fail loudly, not silently create a stray file).
ssh_host_list's response always includes config_path, so it's easy to confirm
which file is currently active before running ssh_conn_add_host/ssh_host_update/
ssh_host_remove.
This is a session-wide switch, not a per-call argument - it stays in effect for
every subsequent host tool call until you switch again or the server restarts. It's
independent of any active SSH connection (ssh_conn_connect/ssh_host_disconnect),
which is unaffected by switching config files.
Sudo Operations with Key Authentication¶
When using key-based authentication without a password, sudo operations require special handling:
- Explicit sudo_password: Provide
sudo_passwordin the config - Passwordless sudo: Configure the server for passwordless sudo (NOPASSWD in sudoers)
If neither is available, sudo operations will fail at runtime with a clear error message.
# Key auth with explicit sudo password
[admin@server.example.com]
keyfile = "~/.ssh/id_rsa"
sudo_password = "sudo_password_here"
port = 22
# Key auth with passwordless sudo configured on server
[deploy@server.example.com]
keyfile = "~/.ssh/deploy_key"
port = 22
# No sudo_password needed if server has NOPASSWD configured
Security Best Practices¶
File Permissions¶
Set strict permissions on your config file:
# Linux/macOS
chmod 600 ~/.mcp_ssh_hosts.toml
# Windows (PowerShell)
icacls $env:USERPROFILE\.mcp_ssh_hosts.toml /inheritance:r /grant:r "$($env:USERNAME):(R,W)"
The server also sets these permissions itself (0o600) whenever it creates or
updates the file, so this is a defense-in-depth step, not something you need to
maintain manually.
Never Read the Config File Directly¶
Every host's password, sudo password, and key passphrase is stored here in
plaintext - that's the whole reason ssh_host_list, ssh_conn_add_host,
ssh_host_update, and ssh_host_remove exist as dedicated tools instead of asking
callers to edit the file themselves. None of these four tools ever return a stored
credential back out. If you're driving this server through an LLM agent, make sure
it's instructed to use these tools rather than reading/parsing
~/.mcp_ssh_hosts.toml directly (e.g. via a generic file-read or shell tool) - doing
so would expose every configured host's credentials to the LLM at once, not just the
one host it actually needed.
General Security¶
- Prefer SSH keys over passwords for authentication
- Use passphrases on SSH keys for additional security
- Never commit the config file to version control
- Add to .gitignore:
mcp_ssh_hosts.tomland*.toml - Use unique credentials per host when possible
- Restrict key permissions:
chmod 600 ~/.ssh/id_rsa
Example .gitignore Entry¶
# SSH host configurations (contain credentials)
mcp_ssh_hosts.toml
.mcp_ssh_hosts.toml
*.mcp_ssh_hosts.toml
Command Line Usage¶
# Use custom config location
python mcp_ssh_server.py --config /path/to/custom_hosts.toml
# Default locations are checked automatically
python mcp_ssh_server.py
Reloading Configuration¶
No reload step is needed. Host configuration is read fresh from the TOML file on
every access (ssh_conn_connect, ssh_host_list, etc.) - there is no in-memory
cache to invalidate. Edit the config file while the server is running and the
next call will see the change.