Server data from the Official MCP Registry
Shared SSH PTY runtime for MCP clients with viewer, input lock, async tracking, and policy rules.
Shared SSH PTY runtime for MCP clients with viewer, input lock, async tracking, and policy rules.
ssh-session-mcp is a well-structured SSH PTY session manager with appropriate authentication handling and reasonable permissions for its purpose. Security is generally strong with input validation, policy-based command filtering, and user-aware access controls. However, some code quality concerns around error handling, broad exception catches, and potential sensitive data exposure in logs warrant attention. Supply chain analysis found 3 known vulnerabilities in dependencies (0 critical, 3 high severity). Package verification found 1 issue.
3 files analyzed · 11 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
This plugin requests these system permissions. Most are normal for its category.
Set these up before or after installing:
Environment variable: SSH_MCP_CONFIG
Environment variable: SSH_MCP_INSTANCE
Environment variable: VIEWER_PORT
Environment variable: VIEWER_HOST
Environment variable: AUTO_OPEN_TERMINAL
Environment variable: SSH_MCP_MODE
Environment variable: SSH_MCP_LOCAL
Environment variable: SSH_MCP_DEBUG
Environment variable: SSH_HOST
Environment variable: SSH_PORT
Environment variable: SSH_USER
Environment variable: SSH_PASSWORD
Environment variable: SSH_KEY
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-zw-awa-ssh-session-mcp": {
"env": {
"SSH_KEY": "your-ssh-key-here",
"SSH_HOST": "your-ssh-host-here",
"SSH_PORT": "your-ssh-port-here",
"SSH_USER": "your-ssh-user-here",
"VIEWER_HOST": "your-viewer-host-here",
"VIEWER_PORT": "your-viewer-port-here",
"SSH_MCP_MODE": "your-ssh-mcp-mode-here",
"SSH_PASSWORD": "your-ssh-password-here",
"SSH_MCP_DEBUG": "your-ssh-mcp-debug-here",
"SSH_MCP_LOCAL": "your-ssh-mcp-local-here",
"SSH_MCP_CONFIG": "your-ssh-mcp-config-here",
"SSH_MCP_INSTANCE": "your-ssh-mcp-instance-here",
"AUTO_OPEN_TERMINAL": "your-auto-open-terminal-here"
},
"args": [
"-y",
"ssh-session-mcp"
],
"command": "npx"
}
}
}From the project's GitHub README.
中文 | English
Persistent shared-terminal runtime for MCP clients over SSH.
ssh-session-mcp gives the user and the AI the same SSH PTY session, adds a browser viewer, tracks who typed what, and makes long-running remote work manageable instead of stateless.

git clone this repository.npx -y ssh-session-mcp --viewerPort=autonpm install -g ssh-session-mcpdocker.io/zwawa/ssh-session-mcpgit clone is only for contributors, source builds, and local development.npx or a global npm install is still the lowest-friction path. Docker is mainly useful when you want a pinned runtime, container-based deployment, or registry-backed distribution.Most SSH-oriented MCP servers can execute commands, but they do not manage terminal state well enough for real collaboration.
ssh-session-mcp focuses on the missing runtime layer:
Key directories and files:
| Path | Purpose |
|---|---|
src/ | Core TypeScript implementation for the MCP server, SSH session runtime, viewer, tools, and config CLIs |
src/viewer-html/ | HTML page generators and browser-side scripts for the terminal viewer |
test/ | Vitest coverage for runtime behavior, viewer contracts, config loading, and repository validation |
docs/ | Supporting documentation such as contracts, failure taxonomy, platform notes, and Docker usage |
docs/examples/ | Example config files for normal and Docker-oriented setups |
scripts/ | Build, version sync, and local operator helper scripts |
deploy/helm/ | Helm chart for Kubernetes deployment in single-node or distributed v0 mode |
site/ | GitHub Pages landing page source |
dist/ | Generated static site output from npm run build:site |
build/ | Generated JavaScript output from npm run build |
Dockerfile | Container image build definition |
docker-compose.yml | Profile-based Docker Compose example |
docker-compose.env.yml | Legacy .env-style Docker Compose example |
server.json | MCP server metadata for marketplace-style distribution |
AGENT.md | Primary agent/operator playbook |
llms-install.md | Agent-focused installation and environment checklist |
.env.example | Legacy single-target environment variable template |
If the goal is to let Claude Code, Codex, or OpenCode install the server automatically, prefer npx -y ssh-session-mcp in the MCP command instead of a prior global install.
For Cline Marketplace and other agent installers, see llms-install.md. This repo is structured to be one-click installable through an npx -y ssh-session-mcp --viewerPort=auto command.
claude mcp add --transport stdio ssh-session-mcp -- npx -y ssh-session-mcp --viewerPort=auto
Windows note from the Claude Code docs: native Windows users should wrap npx with cmd /c for stdio MCP servers.
claude mcp add --transport stdio ssh-session-mcp -- cmd /c npx -y ssh-session-mcp --viewerPort=auto
codex mcp add ssh-session-mcp -- npx -y ssh-session-mcp --viewerPort=auto
OpenCode's opencode mcp add flow is interactive. Choose a local MCP server and use this command:
npx -y ssh-session-mcp --viewerPort=auto
If you prefer config instead of the interactive flow:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"ssh-session-mcp": {
"type": "local",
"command": ["npx", "-y", "ssh-session-mcp", "--viewerPort=auto"]
}
}
}
This is the closest thing to "automatic installation" for stdio MCP servers today: the MCP client stores the command, and npx -y downloads the package automatically the first time it runs.
npm install -g ssh-session-mcp
ssh-session-mcp-ctl launch --local --viewerPort=auto
This starts a local shell instead of SSH and opens the browser terminal, which is the easiest way to test the MCP runtime before touching a real server.
Use the MCP server binary directly when wiring a client:
# Global install
npm install -g ssh-session-mcp
# Server command used by MCP clients
ssh-session-mcp --viewerPort=auto
# Claude Code
claude mcp add --transport stdio ssh-session-mcp -- ssh-session-mcp --viewerPort=auto
# Codex CLI
codex mcp add ssh-session-mcp -- ssh-session-mcp --viewerPort=auto
If you prefer npx instead of a global install:
npx -y ssh-session-mcp --viewerPort=auto
Create .env from .env.example:
cp .env.example .env
SSH_HOST=YOUR_DEVICE_HOST
SSH_PORT=22
SSH_USER=YOUR_DEVICE_USER
SSH_PASSWORD=
SSH_KEY=
VIEWER_PORT=auto
AUTO_OPEN_TERMINAL=false
SSH_MCP_MODE=safe
Then launch:
ssh-session-mcp-ctl launch --viewerPort=auto
For multiple boards or named targets, create ssh-session-mcp.config.json:
{
"defaultDevice": "DEVICE_A_ID",
"devices": [
{
"id": "DEVICE_A_ID",
"host": "DEVICE_A_HOST",
"port": 22,
"user": "DEVICE_A_USER",
"auth": { "passwordEnv": "DEVICE_A_PASSWORD" },
"defaults": {
"term": "xterm-256color",
"cols": 120,
"rows": 40,
"autoOpenViewer": true,
"viewerMode": "browser"
}
}
]
}
Discovery order:
--config=/path/to/config.jsonssh-session-mcp.config.json.env fallbackImportant:
auth.password is intentionally unsupported. Use auth.passwordEnv or auth.keyPath..env or the parent environment, not in repo-tracked JSON.Public Docker images should be distributed through Docker Hub, with GitHub Container Registry as an optional secondary registry:
docker.io/zwawa/ssh-session-mcp:<version>
docker.io/zwawa/ssh-session-mcp:latest
ghcr.io/zw-awa/ssh-session-mcp:<version>
Recommended container launch for a real SSH target:
docker run --rm -i \
-p 8793:8793 \
-e VIEWER_PORT=8793 \
-e VIEWER_HOST=0.0.0.0 \
-e SSH_HOST=YOUR_DEVICE_HOST \
-e SSH_PORT=22 \
-e SSH_USER=YOUR_DEVICE_USER \
-e SSH_PASSWORD \
docker.io/zwawa/ssh-session-mcp:latest
Export the password in your shell first instead of placing it directly on the command line.
Recommended launch for profile-based config:
docker run --rm -i \
-p 8793:8793 \
-e VIEWER_PORT=8793 \
-e VIEWER_HOST=0.0.0.0 \
-e SSH_MCP_CONFIG=/workspace/ssh-session-mcp.config.json \
-v "$PWD/ssh-session-mcp.config.json:/workspace/ssh-session-mcp.config.json:ro" \
-v "/path/to/host/keys:/workspace/keys:ro" \
docker.io/zwawa/ssh-session-mcp:latest
Equivalent Compose example:
docker compose up -d
See docker-compose.yml for a ready-to-run example that mounts ssh-session-mcp.config.json, publishes the viewer on 8793, and uses SSH_KEY_DIR when set or falls back to a dedicated ./keys directory.
For the full Docker guide, including the legacy .env compose variant and MCP client config snippets, see docs/docker.md.
For a container-oriented profile example, see docs/examples/ssh-session-mcp.config.docker.example.json.
Container-specific notes:
VIEWER_PORT to 8793 when unset so the browser viewer can be published reliably.VIEWER_HOST to 0.0.0.0 inside the container so the mapped port is reachable from the host.AUTO_OPEN_TERMINAL defaults to false in the container because browser auto-open from inside a container is usually not useful.docker-compose.yml, SSH_KEY_DIR overrides the default key mount path. If it is unset, Compose falls back to ./keys, not the repo root..env, or --env-file.npx is still simpler unless your client explicitly prefers containerized commands.Docker-based MCP client command examples:
# Claude Code
claude mcp add --transport stdio ssh-session-mcp -- docker run --rm -i -p 8793:8793 -e VIEWER_PORT=8793 -e VIEWER_HOST=0.0.0.0 docker.io/zwawa/ssh-session-mcp:latest
# Codex CLI
codex mcp add ssh-session-mcp -- docker run --rm -i -p 8793:8793 -e VIEWER_PORT=8793 -e VIEWER_HOST=0.0.0.0 docker.io/zwawa/ssh-session-mcp:latest
For JSON-based MCP clients, the same pattern works by using docker as the command and passing the remaining run ... docker.io/zwawa/ssh-session-mcp:latest tokens as args.
This is useful when:
For many users, publishing to npm and recommending npx -y ssh-session-mcp --viewerPort=auto is still the lower-friction install path.
The browser viewer is not decorative. It is part of the workflow:
For users:
install -> launch viewer -> connect once -> keep the session alive -> let the AI help
For agents:
ssh-quick-connect -> ssh-run -> inspect output -> ssh-command-status if needed -> ssh-run again
Use AGENT.md when you want the AI to install, inspect config, connect devices, and help the user end-to-end. Compatibility notes for older agent setups remain in AI_AGENT_GUIDE.md.
--local for offline testing| Mode | Behavior |
|---|---|
safe | Default per session. Automatically blocks obviously dangerous, interactive, or never-ending commands. |
full | Per session. Relaxes the guardrails for advanced use, while still blocking a small set of clearly destructive abuse cases. |
Each session now owns its own safe / full mode. Switching one browser terminal to full does not change other sessions.
The default rule set can be customized if needed. Custom rules now support:
error: block the commandwarning: allow but surface a warninglog: allow and annotate onlyRule precedence is error > warning > log, and within the same level, earlier rules win.
The browser terminal UI lets the operator choose one of these input policies:
| Policy | What the operator experiences |
|---|---|
common | User and agent can both type into the shared terminal. |
user | Only the user can type. Agent write actions are blocked. |
auto | The user can start typing without fighting the agent. While the user is actively drafting input, agent writes are blocked. |
agent | Only the agent can type. User input is blocked until the policy changes. |
When the terminal is not available for agent input, tools such as ssh-run, ssh-session-send, and ssh-session-control return a blocked response instead of forcing input into the PTY.
| Tool | Purpose |
|---|---|
ssh-quick-connect | Connect or reuse the default target and optionally open the viewer |
ssh-run | Execute a command with completion detection and exit-code capture |
ssh-status | Inspect sessions, viewer state, and operation mode |
ssh-command-status | Poll async command progress |
ssh-retry | Retry flaky commands with backoff |
ssh-session-policy-list | Inspect inherited defaults and current session custom policy rules |
ssh-session-policy-upsert | Add or update a session-level custom policy rule |
ssh-session-policy-remove | Remove a session-level custom policy rule |
ssh-session-policy-reset | Reset session custom rules back to inherited defaults |
| Tool | Purpose |
|---|---|
ssh-session-open | Open a session with explicit SSH parameters |
ssh-session-send | Send raw PTY input |
ssh-device-list | List configured devices and defaults |
ssh-session-read | Read buffered terminal output by offset |
ssh-session-watch | Long-poll for output and dashboard changes |
ssh-session-history | Read line-numbered mixed terminal history |
ssh-session-control | Send control keys such as ctrl_c, arrows, or tab |
ssh-session-resize | Resize the PTY |
ssh-session-list | List tracked sessions |
ssh-session-diagnostics | Inspect lock state, warnings, running command state, and viewer health |
ssh-session-policy-list | Show inherited policy defaults and the current session rule set |
ssh-session-policy-upsert | Add or update a session-specific custom policy rule |
ssh-session-policy-remove | Remove a session-specific custom policy rule |
ssh-session-policy-reset | Restore inherited rules for the current session |
ssh-session-set-active | Choose the default session |
ssh-viewer-ensure | Open or reuse the local viewer |
ssh-viewer-list | List tracked viewer processes |
ssh-session-close | Close a session cleanly |
ssh-quick-connect | One-step connect flow for agents |
ssh-run | Main command execution tool |
ssh-status | Runtime overview |
ssh-command-status | Async poller |
ssh-retry | Retry executor |
These helpers are for humans on the workstation that owns the viewer:
ssh-session-mcp-ctl status
ssh-session-mcp-ctl devices
ssh-session-mcp-ctl launch --viewerPort=auto
ssh-session-mcp-ctl launch --local --viewerPort=auto
ssh-session-mcp-ctl logs --tail=60
ssh-session-mcp-ctl cleanup
Default rule library management for operators:
ssh-session-mcp-config policy list --scope=merged
ssh-session-mcp-config policy set error-kubectl-delete --pattern="\\bkubectl\\s+delete\\b" --category=dangerous --action=error --priority=0 --message="kubectl delete is blocked in safe mode"
ssh-session-mcp-config policy remove error-kubectl-delete
Equivalent repo-local commands also exist:
npm run launch
npm run status
npm run devices
npm run logs
npm run cleanup
Key environment variables:
| Variable | Meaning | Default |
|---|---|---|
SSH_HOST | Legacy single-target SSH host | required in legacy mode |
SSH_PORT | Legacy single-target SSH port | 22 |
SSH_USER | Legacy single-target SSH user | required in legacy mode |
SSH_PASSWORD | Password auth | empty |
SSH_KEY | Local private key path | empty |
SSH_PASSWORD_FILE | File containing the SSH password | empty |
SSH_KEY_FILE | File containing the SSH private key | empty |
SSH_MCP_INSTANCE | Runtime isolation key | proc-<pid> or helper-selected |
SSH_MCP_CONFIG | Explicit config file path | auto-discovery |
SSH_MCP_STATE_DIR | Runtime state root directory | platform default |
VIEWER_HOST | Viewer bind host | 127.0.0.1 |
VIEWER_PORT | Viewer port or auto | 0 unless configured |
VIEWER_ACCESS_MODE | Viewer IP filter mode | config-driven |
SSH_MCP_MODE | safe or full | safe |
SSH_MCP_LOCAL | Launch a local shell instead of SSH | false |
SSH_MCP_DEBUG | Enable debug browser actions | false |
AUTO_OPEN_TERMINAL | Auto-open browser terminal | false |
SSH_MCP_LOG_MODE | off, meta, or stderr logging | off |
SSH_MCP_LOG_DIR | Metadata log directory | platform default |
Distributed v0 intentionally implements a narrow boundary:
single-node and distributedREMOTE_OWNER, HTML pages render a remote-owner error page, and websocket attaches close with code 4009Distributed v0 requires Redis for real multi-node deployments. SSH_MCP_STORE=memory only exists for local skeleton testing and does not provide a shared store across replicas.
Distributed configuration:
| Variable | Meaning | Default |
|---|---|---|
SSH_MCP_RUNTIME_MODE | single-node or distributed | single-node |
SSH_MCP_STORE | memory or redis | redis in distributed mode, otherwise memory |
SSH_MCP_REDIS_URL | Redis connection URL | required when SSH_MCP_STORE=redis |
SSH_MCP_NODE_ID | Stable logical node id for this replica | runtime instance id |
SSH_MCP_PUBLIC_BASE_URL | Public viewer base URL advertised to other replicas | unset |
SSH_MCP_AUTH_MODE | off or proxy | off |
SSH_MCP_TRUST_PROXY | Whether to trust authenticated proxy headers | false |
SSH_MCP_AUTH_USER_HEADER | Authenticated user header name | x-ssh-session-mcp-user |
SSH_MCP_AUTH_ROLE_HEADER | Authenticated role header name | x-ssh-session-mcp-role |
Recommended distributed env example:
SSH_MCP_RUNTIME_MODE=distributed
SSH_MCP_STORE=redis
SSH_MCP_REDIS_URL=redis://redis:6379/0
SSH_MCP_NODE_ID=node-a
SSH_MCP_PUBLIC_BASE_URL=https://ssh-mcp.example.com
SSH_MCP_AUTH_MODE=proxy
SSH_MCP_TRUST_PROXY=true
SSH_MCP_AUTH_USER_HEADER=x-forwarded-user
SSH_MCP_AUTH_ROLE_HEADER=x-forwarded-role
Proxy auth is most useful in distributed mode behind a trusted reverse proxy. The built-in role mapping is:
viewer_read: pages, session list/read endpoints, history, diagnostics, health, readiness, metricsviewer_write: attach input, resize, controlsession_admin: mode changes, policy updates, close, set-active, debug-agent actions, local debug session creationUse these variables according to your installation path:
| Variable | Required When | Accepted Values / Example | Notes |
|---|---|---|---|
SSH_HOST | Legacy single-target SSH mode | YOUR_DEVICE_HOST | Required unless you use ssh-session-mcp.config.json or --local. |
SSH_PORT | Legacy single-target SSH mode | 22 | Optional in legacy mode; defaults to 22. |
SSH_USER | Legacy single-target SSH mode | YOUR_DEVICE_USER | Required unless you use device profiles. |
SSH_PASSWORD | Password-based auth | exported env var | Prefer env export over putting the password directly in the command line. |
SSH_PASSWORD_FILE | Password-based auth via secret file | /run/secrets/ssh_password | The file contents are used as the password. This is the preferred pattern for Docker and Kubernetes secrets. |
SSH_KEY | Key-based auth in legacy mode | /absolute/path/to/private/key | The path must exist on the host running the MCP server. |
SSH_KEY_FILE | Key-based auth via secret file | /run/secrets/ssh_private_key | The file contents are used as the private key. This works well with mounted container secrets. |
SSH_MCP_CONFIG | Profile-based mode or config outside cwd | /path/to/ssh-session-mcp.config.json | Use this when config auto-discovery is not enough. |
SSH_MCP_INSTANCE | Multi-agent / multi-client isolation | agent-a | Use different values when two agents should not share runtime state. |
SSH_MCP_STATE_DIR | Runtime state root override | /workspace/state | Controls where per-instance server info, viewer state, and default logs are stored. Mount it persistently in containers. |
SSH_MCP_RUNTIME_MODE | Distributed topology selection | single-node, distributed | Distributed v0 only shares control-plane state; it does not migrate PTYs across nodes. |
SSH_MCP_STORE | Distributed state backend | memory, redis | Use redis for any real multi-node deployment. memory is only for local distributed skeleton testing. |
SSH_MCP_REDIS_URL | Redis backend enabled | redis://redis:6379/0 | Required when SSH_MCP_RUNTIME_MODE=distributed and SSH_MCP_STORE=redis. |
SSH_MCP_NODE_ID | Stable distributed node id | node-a | Useful when multiple replicas share Redis and need durable owner ids. |
SSH_MCP_PUBLIC_BASE_URL | Public routing hint for this node | https://ssh-mcp.example.com | Used in REMOTE_OWNER payloads and cluster status output. |
SSH_MCP_AUTH_MODE | Viewer auth mode | off, proxy | proxy is recommended only behind a trusted reverse proxy. |
SSH_MCP_TRUST_PROXY | Trust viewer identity headers | true, false | Must be enabled together with SSH_MCP_AUTH_MODE=proxy. |
SSH_MCP_AUTH_USER_HEADER | Proxy-auth viewer user header | x-forwarded-user | Header names are normalized to lowercase internally. |
SSH_MCP_AUTH_ROLE_HEADER | Proxy-auth viewer role header | x-forwarded-role | Roles are comma-separated and mapped to viewer_read, viewer_write, session_admin. |
VIEWER_HOST | Custom viewer bind | 127.0.0.1, 0.0.0.0 | Use 0.0.0.0 inside containers; keep 127.0.0.1 on normal host installs unless you need remote access. |
VIEWER_PORT | Viewer enabled | auto, 0, 8793 | auto picks a free port, 0 disables the viewer, fixed ports are best for Docker. |
VIEWER_ACCESS_MODE | Viewer access control mode | allow_all, allowlist, denylist | Usually edited in the viewer home page. Keep allow_all only when you stay on localhost. |
AUTO_OPEN_TERMINAL | Auto-open viewer tab | true, false | Usually false in containers. |
SSH_MCP_MODE | Runtime safety mode | safe, full | safe is the recommended default. |
SSH_MCP_LOCAL | Local demo mode | true, false | Starts a local shell instead of SSH. |
SSH_MCP_DEBUG | Browser debug controls | true, false | Intended for demos and troubleshooting. |
SSH_MCP_LOG_MODE | Runtime metadata logging | off, meta, stderr | meta writes JSONL metadata logs without storing raw secrets. stderr is the preferred container mode because it preserves stdio MCP transport while exposing structured logs to the container runtime. |
SSH_MCP_LOG_DIR | Override metadata log directory | /workspace/state/instances/<instance>/logs | Mainly useful with SSH_MCP_LOG_MODE=meta; ignored for stderr. |
SSH_KEY_DIR | Docker Compose profile-based example | /path/to/host/keys | Optional in docker-compose.yml; when unset it falls back to ./keys. |
SSH_SESSION_MCP_IMAGE | Docker Compose image override | docker.io/zwawa/ssh-session-mcp:latest | Override this if you mirror the image or test another tag. |
Choose one of these minimum configuration sets:
SSH_MCP_LOCAL=true and VIEWER_PORT=autoSSH_HOST, SSH_USER, SSH_PASSWORDSSH_HOST, SSH_USER, SSH_KEYssh-session-mcp.config.json, plus any passwordEnv variables referenced by that configssh-session-mcp.config.json, optional SSH_KEY_DIR, optional SSH_SESSION_MCP_IMAGESSH_MCP_LOG_MODE=stderr so logs go to the container runtime without corrupting stdio MCP transport.SSH_MCP_STATE_DIR persistently when you want viewer policy, server info, and state files to survive container restarts.SSH_MCP_PUBLIC_BASE_URL per replica.REMOTE_OWNER./livez for process liveness/readyz for readiness checks/metrics for Prometheus text metricsdeploy/helm/ssh-session-mcpExample config file: docs/examples/ssh-session-mcp.config.example.json
.env is ignored by git and npm.See SECURITY.md for the full policy.
More detail: docs/platform-compatibility.md
Clone the repo only if you want to modify the source, run tests locally, or build release artifacts.
npm install
npm run build
npm run test
npm run validate:repo
npm run build:site
GitHub Actions included in this repo can:
dist/Apache-2.0. See LICENSE.
Be the first to review this server!
by Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
by Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
by mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.