Skip to main content
Glama

Linux SSH MCP

Independent Docker deployment of tufantunc/ssh-mcp, pinned to npm version 2.16.0. This project wraps the unchanged upstream MCP/SSH engine; it is not a new SSH implementation.

Quick Start (New Operator)

Clone the repo, then create your own private config — you never copy anyone's keys. Full detail and the VM-side steps are in ONBOARDING.md.

git clone <repo-url>
cd Linux-SSH-MCP
.\start.ps1 -Action Initialize     # creates your private folder %LOCALAPPDATA%\LinuxSSHMCP (empty profiles + token)
.\start.ps1 -Action Build          # builds and pins the secret-free image
# Prepare the VM and add a profile (see ONBOARDING.md sections 4-5):
#   - create a dedicated non-root SSH user, authorize a dedicated key, pin the SHA256 host key
#   - fill %LOCALAPPDATA%\LinuxSSHMCP\config\stdio.toml from profiles.example.toml
.\start.ps1 -Action RegisterClaude # register with Claude Desktop, then fully quit/reopen it

Code is shared and safe to commit; your profiles, keys and token live outside the repo and must never be committed. See ONBOARDING.md for the step-by-step.

Related MCP server: mcp-shell-server

Current State

  • Two transports: per-client stdio for Claude/VS Code, and authenticated HTTP at http://127.0.0.1:8814/.

  • Both configurations start with zero VM profiles. Tool discovery works; VM operations are refused.

  • Up to 32 explicitly named profiles. Multiple VMs can use one deployment; separate inspection and administrative accounts/profiles are recommended.

  • No Ubuntu VM has been contacted. No account, SSH key, authorized_keys entry, sudoers rule or firewall rule has been changed remotely.

  • AD, WinRM, Microsoft 365, n8n and the Addis-Nearby workspace are independent and were not modified by this deployment.

Local Commands

Run in this project using Windows PowerShell 5.1 with Docker Desktop running Linux containers:

.\start.ps1 -Action Initialize
.\start.ps1 -Action Test
.\start.ps1 -Action Build
.\start.ps1 -Action Http
.\start.ps1 -Action Status
node .\verify.mjs --http http://127.0.0.1:8814/

Initialize creates private configuration without overwriting existing profiles or tokens. Build saves the built image's immutable SHA256 ID; launchers refuse an absent/invalid pin and never pull a substitute. Test runs 25 checks in a non-root, read-only, network-isolated Linux container. The SSH fixture generates temporary keys, only returns fixed text and never executes a shell command.

verify.mjs tests the actual Windows launcher, two concurrent stdio containers, 14-tool discovery, absent-profile refusal, persistent audit writes, resource/mount/port restrictions, EOF cleanup and optionally deployed HTTP authentication/token confidentiality. It only runs when both profile lists are empty, so it cannot accidentally contact a real VM. Its generated evidence is in verification/report.json.

npm test runs the 16 platform-independent configuration tests. Actual Ubuntu/OpenSSH interoperability and each client's approval dialog remain separate onboarding checks.

.\start.ps1 -Action StopHttp
.\start.ps1 -Action Http -HttpPort 8815

Changing the port recreates only this project's HTTP service. Match the new URL in clients. Localhost remains the default; no automatic Docker restart policy is configured.

Optional WSL HTTP Test

For a client on a separate Docker engine inside WSL, explicitly bind to the Windows WSL-facing IPv4 address. Find that address from ip -4 route show default in WSL and confirm it belongs to the Windows WSL interface. For example, if it is 172.25.160.1:

.\start.ps1 -Action Http -HttpBindAddress 172.25.160.1

The launcher rejects wildcard/public addresses and addresses not assigned to Windows. The same exact IP is added to the MCP Host allowlist; other private IPs are not automatically trusted. This is a bind restriction, not per-client authorization. Keep Windows Firewall enabled and restrict any required inbound rule to the intended WSL source address or subnet. Never disable the firewall to make the test pass. WSL addresses may change after a restart.

Plain HTTP does not encrypt bearer tokens or tool output. Use this opt-in mode only for a trusted, restricted local test; it is not a public/LAN production deployment. Add only explicitly approved read-only profiles to the private config\http.toml. Stdio profiles are not copied automatically. An empty HTTP configuration supports discovery but refuses VM tools.

Open WebUI 0.6.31+ supports native MCP Streamable HTTP. In Admin > Integrations > External Tool Servers, add a connection with Type MCP (Streamable HTTP), URL http://172.25.160.1:8814/ (using your actual address), and Auth Bearer. Enter the existing token privately in the Key field, not in a URL, chat, command argument or source file. Restrict Access Control to the intended test user; the token grants access to every profile in this HTTP instance. Do not use OpenAPI, Session or OAuth authentication for this deployment.

Check /health from inside the Open WebUI container first, then verify authenticated tool discovery. A health response is not an MCP handshake or a live SSH test. No MCPO bridge is needed for native Streamable HTTP. See the Open WebUI MCP documentation.

To stop the test, use .\start.ps1 -Action StopHttp. To return to localhost-only access, use .\start.ps1 -Action Http without the address option and remove any test-only firewall rule you created. Existing Claude stdio connections are unaffected; changes to HTTP profiles require restarting only the HTTP service.

Client Setup

For packaged Claude Desktop, RegisterClaude adds only mcpServers.linux-ssh-mcp, preserves other settings after a structured round-trip check, and creates a timestamped backup alongside the existing configuration:

.\start.ps1 -Action RegisterClaude

The entry uses Windows PowerShell with this project's start.ps1 -Action Stdio. It contains no credentials. Registration does not restart Claude. Fully quit/reopen Claude when ready, then check Settings > Developer for linux-ssh-mcp. A launcher handshake is not proof that Claude has reloaded the entry.

This project's .vscode/mcp.json is ready when this folder is opened in VS Code. Start/trust the server explicitly there. It does not modify any other workspace's MCP configuration.

The optional HTTP endpoint uses MCP Streamable HTTP at /, not an SSE-only endpoint. Use a client that supports bearer authentication, entering the token privately in the client's protected secret setting. Do not place it in a URL, command-line argument, source-controlled JSON or chat. Claude's configured connection uses stdio and needs no HTTP token.

Private Files

Default directory: %LOCALAPPDATA%\LinuxSSHMCP.

File or directory

Purpose

config\stdio.toml

Inspection and explicitly approved administrative profiles

config\http.toml

Read-only profiles only

keys\

Dedicated SSH private keys, read-only mounted at /run/keys

http-token

Random 32-byte bearer secret, stored as 64 hexadecimal characters

image-id

Immutable local runtime image pin

Initialization restricts directory ACLs to the current Windows user, SYSTEM and Administrators. Keep inherited ACLs on added key files and review them after importing a key. Do not mount your entire .ssh directory. Profiles/keys are excluded from the image build context and are not stored in this repository.

This initial bootstrap supports dedicated unencrypted key files protected by Windows ACLs and read-only mounts. Encrypted-key passphrases, SSH-agent forwarding, passwords and sudo-password delivery are not wired up. Do not remove encryption from an existing personal key to make it fit; agree on a dedicated deployment key or add a reviewed secret-delivery mechanism first.

First Ubuntu VM

Supply the hostname/IP, SSH port, dedicated non-root username, local key-file path and independently verified host-key fingerprint. A key-file path and public host fingerprint are safe to share; private keys, passwords and passphrases are not.

From the VM console or another already trusted administrative channel, obtain the server's public fingerprint:

ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

Use the complete SHA256:... fingerprint. Do not trust an unverified ssh-keyscan result as identity proof. The verified pin must match the host-key algorithm actually presented. A rebuilt VM or rotated host key requires fresh out-of-band verification, never disabling strict checking.

profiles.example.toml is an intentionally non-runnable inspection template. Replace its documentation-only address, username, key filename and invalid fingerprint placeholder using approved VM details. Place the completed profile in the private stdio configuration; add it separately to HTTP only when desired. Repeat [[profiles]] with unique names for more VMs. Restart only the affected MCP connection after edits.

Start with a dedicated inspection user without sudo, access only to the files/logs needed, and a narrowly authorized SSH key. VM-side account/key provisioning is an administrator action and has not been performed here. SSH access also needs reachability from Docker Desktop, not just from the Windows host.

First live verification should use an explicitly selected profile and benign commands such as id and uname -a. Review returned output before enabling further access. Logs, services, package tools and file visibility depend on Ubuntu permissions and the upstream command grammar.

Administration

Use a separate stdio-only profile with role = "admin", readOnly = false, an honest environment group and approvalPolicy = "ask-all". Reusable approval grants remain disabled. A client without MCP elicitation support fails closed; never change to automatic approvals to bypass this.

Upstream admin in prod allows read-only/safe/destructive classes but not privileged commands by default. Granting privileged access requires an explicitly reviewed policy and VM-side sudo permissions. Do not mislabel production as dev to gain privileges. This deployment has enabled no such grant.

The upstream privileged-command tool wraps a command in sudo ... sh -c. That is a shell grant and is incompatible with pretending a generic sudo shell is a narrow service-only permission. For least privilege, review exact sudo commands/wrappers and the upstream policy together before enabling them. No blanket NOPASSWD: ALL setup is supplied.

The service exposes all 14 upstream tools, but their presence is not permission to use them. Read-only policy denies writes, shell escapes, sudo and process signals. The local streaming file tools are disabled because transferRoot is unset. Remaining SFTP operations still obey profile policy and remote filesystem permissions; they have not been live-tested against Ubuntu here.

Security And Limits

  • Non-root UID/GID 1000, read-only container root, no added capabilities, no-new-privileges, 512 MiB RAM, 96 PIDs, 1.5 CPUs and bounded temporary storage. No Docker socket mount.

  • SSH host pins are mandatory in strict mode. Startup rejects missing/invalid configuration, root users, unbounded local transfer roots and unsafe administrative approval modes.

  • HTTP is localhost-only by default; the explicit private-address option above is for a restricted local test. Bearer holders share access to the same configured read-only profiles, connections and sessions: this is not multi-user isolation or per-user RBAC. Do not expose it to a LAN or the Internet without a separately reviewed TLS/authentication design.

  • The bearer token enters upstream only in in-process memory, not the OS command line or environment. Tests verify Docker metadata, logs and /proc arguments/environment. The server process, Windows administrator and Docker administrator can still access mounted secrets; Docker is not protection from its own administrator.

  • The wrapper removes inherited SSH_MCP_* overrides and SSH_AUTH_SOCK. No credentials are stored in Claude JSON.

  • Dedicated audit volumes: linux-ssh-mcp_stdio-audit and linux-ssh-mcp_http-audit. Per-container files live beneath /home/node/.local/share/ssh-mcp/instances/<container-hostname>/ssh-mcp/. Hashes are tamper-evident, not signatures or a guarantee against privileged tampering. Rotation is per instance; old instance directories require an approved retention/cleanup process. Logs can contain sensitive commands despite redaction. Never put secrets into commands.

  • Read-only command classification is defense in depth, not an OS security boundary. Remote Unix permissions remain essential, and readable confidential data can still leave the VM through tool output. Treat remote output as untrusted data, not instructions.

  • Stdio containers end when the client closes stdin. Multiple client-owned instances are normal. HTTP remains running until StopHttp or Docker shutdown. Stop/remove only this project's services, never use global Docker prune commands.

Sources And Rollback

  • Pinned upstream source, inspected revision b5fc51542a2b0f58410595a14b2a290ccbf42f54.

  • Official MCP TypeScript SDK, pinned package 1.30.0.

  • package-lock.json pins transitive packages; Dockerfile pins the Node base digest. Installed dependency licenses remain in their packages. Review upstream security advisories before updating pins; do not auto-upgrade production.

To disable: stop this project's HTTP service, remove only the linux-ssh-mcp client entry and restart that client when approved. Keep private configuration, audit volumes and timestamped Claude backups until a deliberate retention decision. No other MCP project's files or containers need to be removed.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables remote server administration via SSH, supporting command execution, SFTP file transfers, and multi-profile management. It features security safeguards like destructive command detection and audit logging to ensure safe interaction with remote Linux/Unix environments.
    17
    5 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to run shell commands on the host and retrieve exit code, stdout, and stderr, with hardened authentication and an encrypted audit log.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to manage remote Linux terminals over SSH, supporting multi-host and jump-host command execution, background task control, and SFTP file writes, edits, and verified uploads.
    MIT