mcp-ssh-live
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., "@mcp-ssh-livetail the nginx access log on production"
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.
mcp-ssh-live
Interactive, streaming SSH tool for LLM agents via MCP (Model Context Protocol).
Lets LLM agents (Claude, Cursor, Zed) spawn long-running remote commands and watch their output arrive line-by-line in chat — instead of blocking for hours waiting for a ssh_exec to return.
┌─────────────┐ JSON-RPC/stdio ┌──────────────────┐ SSH ┌─────────────┐
│ Zed / │ ────────────────── │ mcp-ssh-live │ ───────────────── │ your remote │
│ Claude / │ │ (Python) │ │ server(s) │
│ Cursor │ │ │ │ │
└─────────────┘ └──────────────────┘ └─────────────┘
^ ^
| |
ring buffers SFTP + exec
+ reader threadsWhy this exists
Other SSH-MCP servers (tufantunc/ssh-mcp, classfang/ssh-mcp-server, AiondaDotCom/mcp-ssh, …) are all request/response: send a command, wait for it to finish, get the full output. Long jobs — parsers, builds, deploys, log tails — are unusable. The LLM either blocks past the MCP client's 60 s timeout, or falls back to nohup … & + tail -n 10 log polling that loses lines and has no kill-signal story.
mcp-ssh-live splits the one SSH primitive into spawn + tail + signal + stdin, so an agent can:
Start
python parser.pyon a remote box, get ajob_idin <100 ms.Poll
ssh_tail(job_id, since_line_no, wait_ms=5000)in a loop and stream output into the chat live.Send SIGTERM / SIGKILL / SIGINT via
ssh_signalwhen the user wants to stop.Feed
sudopasswords or REPL input viassh_send_stdin.Upload / download files via SFTP (
ssh_upload/ssh_download) — binary-safe, with sha256 on both sides.Manage jobs across multiple hosts simultaneously.
Related MCP server: OctSSH
Features
10 MCP tools covering synchronous exec, streaming spawn+tail, signals, stdin, SFTP, host/job registry management.
OpenSSH-compatible signal delivery. Captures the remote PID at spawn time, falls back to
kill -SIG <pid>on a fresh exec channel when paramiko's in-channelsend_signalis ignored by the server (which is the common case).Ring-buffered output (default 10 000 lines per stream) with condition-variable-backed blocking
wait_mssossh_tailis near-live without busy-polling.Multi-host: one process manages several SSH targets; each tool call can pick a host by alias.
Auto-reconnect on transient network / sshd hiccups (configurable retries + delay).
Graceful shutdown: TERM → 5 s grace → KILL for every running remote process when the server exits.
Opt-in disk log mirror (
--log-dir) — every streamed line also written to<log_dir>/<job_id>.logso full output survives ring-buffer eviction and server restarts.Secrets never leak into MCP responses: passwords live in env vars, only variable names appear in
ssh_list_hosts.
Quick start
1. Install
pipx install mcp-ssh-liveOr, for a project-local editable install:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .Requires Python 3.10+.
2. Try it from the command line
# Start the MCP server — no SSH credentials needed at startup
mcp-ssh-liveThe server starts empty and waits for an MCP client to connect. Credentials are provided at runtime by the agent via ssh_connect (see step 4). If you prefer to pre-configure a host at startup, you can still pass --host, --user, --password-env flags — see Configuration.
3. Register with an MCP client
Zed
Edit ~/.config/zed/settings.json (or .zed/settings.json for per-project).
Windows (use the Python executable directly — Zed doesn't pick up PATH the same way as a terminal):
Find your Python path: open a terminal and run
where python.
{
"context_servers": {
"ssh-live": {
"enabled": true,
"command": "C:/path/to/python.exe",
"args": ["-m", "mcp_ssh_live"],
"env": {
"FASTMCP_DISABLE_VERSION_CHECK": "1",
"PYTHONUNBUFFERED": "1"
},
"settings": {}
}
},
"agent": {
"always_allow_tool_actions": true,
"always_allowed_tools": {
"ssh_connect": true, "ssh_disconnect": true,
"ssh_exec": true, "ssh_spawn": true, "ssh_tail": true,
"ssh_signal": true, "ssh_send_stdin": true,
"ssh_list_jobs": true, "ssh_remove_job": true,
"ssh_upload": true, "ssh_download": true, "ssh_list_hosts": true
}
}
}macOS / Linux (if installed via pipx — binary is in PATH):
{
"context_servers": {
"ssh-live": {
"source": "custom",
"enabled": true,
"command": "mcp-ssh-live",
"args": [],
"env": {
"FASTMCP_DISABLE_VERSION_CHECK": "1",
"PYTHONUNBUFFERED": "1"
}
}
},
"agent": {
"always_allow_tool_actions": true,
"always_allowed_tools": {
"ssh_connect": true, "ssh_disconnect": true,
"ssh_exec": true, "ssh_spawn": true, "ssh_tail": true,
"ssh_signal": true, "ssh_send_stdin": true,
"ssh_list_jobs": true, "ssh_remove_job": true,
"ssh_upload": true, "ssh_download": true, "ssh_list_hosts": true
}
}
}Save the file. No restart needed in most cases, but if the indicator stays red — close and reopen Zed.
3b. Enable in the Agent Panel
Open the Agent Panel (right sidebar).
Click
···(top-right of the panel) → Settings → MCP Servers.Find
ssh-liveand toggle it ON.The indicator next to
ssh-liveshould turn green within a few seconds.
If it stays red — check
Zed.log: Command Palette →zed: open logand look forssh-live. The most common fix: thecommandpath in settings is wrong. Runwhere python(Windows) orwhich python(macOS/Linux) to get the correct path.
There's a full config template at .zed/settings.json.example.
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"ssh-live": {
"command": "mcp-ssh-live",
"args": [],
"env": { "FASTMCP_DISABLE_VERSION_CHECK": "1" }
}
}
}Cursor
~/.cursor/mcp.json — same shape as Claude Desktop.
4. Ask your agent
Once registered, just tell the agent your SSH details in chat. It calls ssh_connect(...) automatically — credentials never touch the config file.
Stream a live log:
Connect to 1.2.3.4 as root, password is
···. Runtail -f /var/log/syslogfor 30 seconds, then stop.
The agent picks ssh_spawn → loops ssh_tail(wait_ms=5000) → ssh_signal("TERM") → ssh_remove_job, streaming lines into the chat the whole time.
Upload a local file to the server:
Connect to 1.2.3.4 as root, password is
···. UploadC:/Users/me/builds/app-1.0.zipto/opt/app/releases/app-1.0.zipon the server.
The agent calls ssh_connect(...) then ssh_upload(local_path="C:/Users/me/builds/app-1.0.zip", remote_path="/opt/app/releases/app-1.0.zip") — binary-safe, atomic, with sha256 verification printed in the reply.
Key auth instead of password:
Connect to 1.2.3.4 as deploy using key
~/.ssh/id_ed25519. Upload./dist/bundle.jsto/var/www/html/bundle.js.
Multiple servers at once:
Connect to prod at 1.2.3.4 and stage at 10.0.0.5 (both as root, password
···). Deploy./release.tar.gzto/srv/app/on both.
Staged deploy — save credentials once, deploy step by step:
First, register both servers by alias so you don't repeat credentials every time:
Save SSH connection to 1.2.3.4 as
deploy_test— userdeploy, password···. Save SSH connection to 5.6.7.8 asdeploy_production— userdeploy, password···.
The agent calls ssh_connect(host="1.2.3.4", user="deploy", password="...", alias="deploy_test") and the same for deploy_production. Both aliases stay active for the rest of the session.
Then deploy to test first:
Upload everything from the local
./deploydirectory to/srv/app/ondeploy_test. Run/srv/app/healthcheck.shafterwards and show me the output.
The agent calls ssh_upload for each file in ./deploy, then ssh_exec(command="/srv/app/healthcheck.sh", host="deploy_test") and streams the result.
If the output looks good, deploy to production:
Looks good. Now upload the same
./deploydirectory to/srv/app/ondeploy_production.
The agent reuses the saved deploy_production alias — no need to re-enter credentials — and repeats the upload.
Tools
Tool | What it does |
| Add and open an SSH connection at runtime. Returns an |
| Close a connection and remove it from the registry. |
| Run a short command synchronously; returns full stdout/stderr/exit_status. Timeout triggers SIGTERM → SIGKILL. |
| Start a background job, return |
| Stream new lines incrementally. Blocks up to |
| Send TERM/KILL/INT/… via paramiko + |
| Write to a running job's stdin — sudo passwords, REPL input. |
| List every job the server knows about: id, cmd, label, status, exit_status, line counts. |
| Drop a job from the registry. Running jobs need |
| Run a command via |
| Check status and tail output of a persistent job. |
| SFTP upload, atomic |
| SFTP download, atomic |
| Enumerate configured hosts (alias, address, auth method, connected, active_jobs, is_default). Never leaks passwords. |
Full JSON schemas and examples: SPEC.md.
Configuration
Credential-free mode (recommended)
Start the server with no SSH arguments. The agent receives credentials from the user in chat and connects via ssh_connect:
mcp-ssh-liveNo credentials in any config file. The agent connects on demand:
> "Connect to 1.2.3.4 as root, password is …"
Agent → ssh_connect(host="1.2.3.4", user="root", password="…")
Agent → ssh_exec(command="hostname")Pre-configured hosts (optional)
If you prefer hosts to be available immediately without an ssh_connect call, pass them via CLI flags or a TOML file. Credentials go into env vars — never on the command line.
export SSH_PASSWORD='your-password'
mcp-ssh-live \
--host prod=1.2.3.4:22 \
--host stage=10.0.0.5 \
--user root \
--password-env SSH_PASSWORD \
--default-host prod \
--insecure-auto-add \
--log-dir ~/.cache/mcp-ssh-live/logs \
--log-level INFORun mcp-ssh-live --help for the full flag list.
TOML config file
~/.config/mcp-ssh-live/config.toml:
default_host = "prod"
[hosts.prod]
host = "1.2.3.4"
port = 22
user = "root"
password_env = "PROD_PASS"
[hosts.stage]
host = "10.0.0.5"
user = "deploy"
key = "~/.ssh/stage_key"
[limits]
ring_buffer_lines = 10000
max_jobs_per_host = 50
reap_finished_after_sec = 600
reconnect_retries = 3
reconnect_delay_sec = 2.0
[server]
insecure_auto_add = false
known_hosts = "~/.ssh/known_hosts"
log_dir = "~/.cache/mcp-ssh-live/logs"Full reference: docs/CONFIG.md.
Auth
Password:
--password-env SSH_PASSWORD— name of the env var. Never pass passwords on the command line (visible inps).Key:
--key ~/.ssh/id_ed25519. Optional passphrase via--key-passphrase-env NAME.Agent: omit both — paramiko will try the SSH agent and default key locations.
Common gotchas
"I ran python parser.py and see no output for minutes"
Python (and many other programs) block-buffer stdout when stdin is not a TTY. The output is fine — it's sitting in a buffer on the remote side. Three fixes, pick one:
# Option 1: pty=True
ssh_spawn(command="python parser.py", pty=True)
# Option 2: unbuffered Python
ssh_spawn(command="python -u parser.py")
# Option 3: env var
ssh_spawn(command="python parser.py", env={"PYTHONUNBUFFERED": "1"})For non-Python tools: stdbuf -oL <cmd> or unbuffer <cmd> (from expect).
"ssh_signal returns sent=True but the job keeps running"
OpenSSH's sshd typically ignores channel.send_signal (the RFC 4254 in-channel way). mcp-ssh-live falls back to kill -<SIG> <pid> on a fresh exec channel, using the PID captured from the PID=<n> wrapper at spawn time. The response tells you which path worked:
{"sent_via_channel": false, "sent_via_pid": true, ...}If both are false, the spawn wrapper couldn't capture a PID (extremely rare — only happens if capture_pid=False was forced or the remote shell is very non-standard).
"Host key verification failed"
By default mcp-ssh-live respects ~/.ssh/known_hosts and refuses unknown keys. For the first connection either:
SSH into the host once by hand so the key is trusted, or
Pass
--insecure-auto-add(INSECURE — vulnerable to MITM on first connect), orPoint at a custom file via
--known-hosts /path/to/file.
"Tool calls hang for 60 seconds then error out"
If you see csp request task for "initialize" took over 60s in Zed's log: FastMCP tries to fetch its latest version from PyPI on startup. On air-gapped networks or slow DNS, this can time out. Fix with the env var:
FASTMCP_DISABLE_VERSION_CHECK=1More in docs/TROUBLESHOOTING.md.
Security
Passwords pass through env vars only, never command-line arguments (which would show up in
ps).ssh_list_hostsnever returns the password itself — only the env var name and the key file path.Known-hosts is enforced by default.
Sandboxing is at the MCP client level: whichever client you use (Zed, Claude Desktop, Cursor) is the thing asking the user to approve each tool call. Once approved,
mcp-ssh-liveruns whatever the LLM supplied verbatim — there is no command-level sanitization. This is the whole point of the tool.The LLM gets a shell on your server. If that bothers you, use a dedicated restricted user, jailed with
ForceCommand/ rbash / containers.
Documentation
Quick start — install, register, run in 4 steps (this file).
SPEC.md— full technical specification, all tool contracts with JSON shapes.docs/CONFIG.md— complete CLI + TOML reference.docs/USAGE.md— real-world examples per tool.docs/TROUBLESHOOTING.md— common failure modes with fixes.docs/DEVELOPMENT.md— how to hack on the server itself.CHANGELOG.md— release history.
Project status
v0.1.0 — phases 1-5 complete.
Phase | Feature | Status |
1 | Skeleton + | ✅ |
2 | Jobs + streaming tail | ✅ |
3 | Signals + stdin | ✅ |
4 | SFTP + multi-host | ✅ |
5 | Auto-reconnect + graceful shutdown + disk log mirror | ✅ |
6 | Docs + CI + PyPI publish | in progress |
68 unit tests. Acceptance-tested end-to-end against a real Ubuntu 24.04 sshd: streaming, signals, SFTP round-trip, disk mirror, multi-host.
Contributing
Design feedback on SPEC.md and docs/DEVELOPMENT.md is always welcome. Before opening a code PR:
pip install -e ".[dev]"pytest— all 68 tests must pass.ruff check .andblack --check .for style.If the change touches a tool schema, update
SPEC.mdfirst and explain why in the PR.
License
MIT. See LICENSE.
Related projects
Reviewed before building this; all of them are request/response only, which is why this project exists:
Project | Stars | Language | Streaming? |
392 | TS | ❌ | |
332 | TS | ❌ | |
159 | JS | ❌ | |
63 | JS | ❌ |
Available Tools
14 toolsssh_connectA
Add a new SSH host at runtime and open the connection. Call this first before using ssh_exec / ssh_spawn / ssh_upload / ssh_download on a new server.
Returns an alias (e.g. 'h-3f8a1b2c') that you pass as the 'host' argument to all other tools. If you supply your own alias it will be used instead.
Auth: provide exactly one of password or key_path. If neither is given, paramiko tries the SSH agent and default key files (~/.ssh/id_*).
Calling ssh_connect again with the same alias updates the credentials and reconnects.
| Name | Required | Description | Default |
|---|---|---|---|
| host | Yes | IP address or hostname of the remote server. | |
| user | No | SSH username. Default ``root``. | root |
| password | No | SSH password (plain text). Mutually exclusive with ``key_path``. Passing it here is safe — the value is never logged (only the byte count appears in DEBUG logs), and it lives only in the server process's memory for the duration of the connection. | |
| key_path | No | Path to a private key file on the LOCAL machine running the MCP server. Mutually exclusive with ``password``. | |
| port | No | SSH port. Default 22. | |
| alias | No | Optional short name for this host. When omitted, a stable alias is auto-generated from (host, port, user) so the same credentials always produce the same alias. Callers can pass a memorable name like ``"prod"`` or ``"mybox"`` to make subsequent tool calls more readable. | |
| insecure_auto_add | No | Accept unknown host keys automatically (paramiko AutoAddPolicy). Default True because this tool is typically called in interactive sessions where the user is actively choosing a target — production pinned deployments should use a TOML config with a known_hosts path instead. Set to False to require the host key to already be in the system known_hosts. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It discloses the return alias, authentication fallback, reconnection behavior, and security notes about password logging. It also explains the insecure_auto_add parameter's default and rationale.
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?
Four sentences, each serving a distinct purpose: purpose, alias usage, auth options, reconnection. No redundant or irrelevant information. The description is front-loaded with the most critical information.
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, the presence of an output schema, and 13 sibling tools, the description is comprehensive. It explains how to use with siblings, covers authentication, alias generation, security, and reconnection.
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 100%, so baseline is 3. The description adds value by integrating parameters into usage flow (e.g., 'provide exactly one of password or key_path') and explaining alias auto-generation. This goes beyond the schema's individual parameter 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's purpose: 'Add a new SSH host at runtime and open the connection.' It also explicitly positions it as a prerequisite for other tools (ssh_exec, ssh_spawn, ssh_upload, ssh_download), making its role unambiguous.
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 explicit guidance: 'Call this first before using ssh_exec / ssh_spawn / ssh_upload / ssh_download on a new server.' It also explains alias usage, authentication options (exactly one of password or key_path), and the behavior when calling again with the same alias.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_disconnectA
Close the SSH connection to a host and remove it from the registry. Any running jobs on that host are terminated (their channels are closed). After this call the alias is no longer valid for other tools.
Returns removed=false (not an error) if the alias wasn't registered — useful for idempotent cleanup.
| Name | Required | Description | Default |
|---|---|---|---|
| alias | Yes | The alias returned by ``ssh_connect`` or listed in ``ssh_list_hosts``. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. It explains termination of running jobs, alias invalidation, and idempotent return behavior. Could mention if any prerequisites or side effects, but covers key behavioral traits.
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?
Two concise sentences plus a note about return. Every sentence serves a purpose. Front-loaded with main action, no wasted words.
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 (single parameter, no nested objects, output schema present), the description covers all necessary context: termination behavior, idempotency, alias validity. No gaps for an agent to misuse.
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 100%, but description adds value by specifying the source of the alias parameter (returned by ssh_connect or listed in ssh_list_hosts) and stating that after call the alias is no longer valid, going beyond schema description.
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 the action: close SSH connection and remove from registry. It distinguishes from sibling tools like ssh_connect, which establishes connections, and other ssh tools. Specific verb-resource pair with clear scope.
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?
Implied usage: use this when finished with a host. Mentions idempotent cleanup and alias invalidation, which guide when to call. No explicit exclusion of alternatives, but not needed due to tool's straightforward purpose.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_downloadA
Download a file from the remote host to a local path via SFTP. Binary-safe.
Writes to .partial first, then atomically replaces the final path on success. A failed or cancelled download never leaves a truncated file where downstream tooling would pick it up.
Size cap: max_bytes (default 100 MiB, hard ceiling 1 GiB). Remote size is checked via sftp.stat BEFORE the transfer starts; files that exceed the cap fail fast. Caps are enforced streaming-side too, so a misreported remote size can't trick us into filling the local disk.
| Name | Required | Description | Default |
|---|---|---|---|
| remote_path | Yes | Absolute path to the file on the remote host. Tilde expansion is NOT performed (paramiko's SFTP doesn't know about the remote user's ``$HOME``). | |
| local_path | Yes | Where to write the file locally. Parent directories are created automatically. | |
| host | No | Alias of the configured host, or a raw address. | |
| max_bytes | No | Hard cap. Default 100 MiB, clamped to 1 GiB. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully carries behavioral transparency. It details atomic write behavior via .partial file, size cap enforcement, pre-check via sftp.stat, and streaming-side protection. This thoroughly discloses failure modes and guarantees.
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 well-structured with efficient front-loaded main action. Each sentence adds value: download, binary-safe, atomic write, size cap, fail-fast. No unnecessary filler.
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 presence of an output schema, the description appropriately focuses on behavior and input details. It covers key aspects: download mechanism, atomicity, size constraints, and error prevention. No critical 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 100%, so the schema already documents all parameters. The description adds minimal extra meaning beyond the schema (e.g., 'Binary-safe' context) but does not significantly enhance parameter understanding beyond what is already in 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?
Description specifies 'download a file from remote host to local path via SFTP. Binary-safe.' Clearly identifies verb, resource, and distinguishes from siblings like ssh_upload. Provides specific context (SFTP, binary-safe) that enhances understanding.
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?
No explicit guidance on when to use this tool versus alternatives among the 13 sibling tools. The description focuses solely on the tool's mechanics without contextualizing use cases or providing exclusion criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_execA
Run a shell command on the remote host and return its full stdout/stderr/exit status. Blocks until the command finishes or the wall-clock timeout expires (SIGTERM then SIGKILL).
WHEN TO USE ssh_exec:
Short commands that finish in under ~60s.
Fire-and-forget background tasks that must SURVIVE a disconnect (use nohup or screen so the process keeps running even if the MCP server restarts or Zed closes).
SURVIVING DISCONNECT — nohup pattern: ssh_exec('nohup python -u train.py > /tmp/train.log 2>&1 &')
process keeps running after disconnect; check later with:
ssh_exec('tail -n 50 /tmp/train.log') ssh_exec('ps aux | grep train.py')
SURVIVING DISCONNECT — screen pattern: ssh_exec('screen -dmS myjob bash -c "python train.py > /tmp/out.log 2>&1"')
reattach later: screen -r myjob
WARNING: ssh_spawn ties the remote process to the MCP channel — it dies on disconnect. Use ssh_exec+nohup/screen for tasks that must outlive the current session.
For real-time streaming output of a command you will watch to completion without disconnecting, use ssh_spawn + ssh_tail instead.
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | The shell fragment to run on the remote. Passed to ``bash -c`` after a PID-capture prelude; your own shell metacharacters (``|``, ``&&``, ``$VAR``, etc.) work normally. | |
| host | No | Alias of the configured host, or its raw address. If omitted, the server's ``default_host`` is used. When only a single host is configured, this argument is optional. | |
| timeout_ms | No | Wall-clock budget. On overrun the remote process receives SIGTERM, then (after 5 s grace) SIGKILL. Set to 0 or a negative number to disable. | |
| cwd | No | Remote working directory (``cd`` before running). | |
| env | No | Additional environment variables for the remote process. Names must match ``[A-Za-z_][A-Za-z0-9_]*``. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses blocking behavior, timeout handling with SIGTERM/SIGKILL, and disconnect survival patterns. However, it could more explicitly state that without nohup/screen, the process is killed on disconnect (though implied). Overall, adds significant value beyond the schema.
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 bold headings, but slightly lengthy. It is front-loaded with purpose, then usage, then patterns. Every sentence adds value, but could be trimmed slightly. Still, it is concise enough and well-organized.
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 output schema exists (not shown), the description need not cover return values. It covers purpose, parameter details, behavior, and usage patterns comprehensively. With 5 parameters, it provides sufficient context for correct invocation, including patterns for common use cases like surviving disconnect.
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 100%, so baseline is 3. The description adds context for the 'command' parameter (bash -c) and 'timeout_ms' (signal details), enhancing understanding beyond the schema. The 'host' and 'env' descriptions are adequate. This extra context justifies a 4.
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: 'Run a shell command on the remote host and return its full stdout/stderr/exit status.' It distinguishes itself from siblings like ssh_spawn and ssh_tail by specifying when to use each, which is explicit and helpful.
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 'WHEN TO USE ssh_exec' section provides explicit guidance: short commands under 60s and fire-and-forget tasks that must survive disconnect. It contrasts with ssh_spawn for streaming and warns about disconnect behavior, including concrete nohup/screen patterns. This directly helps the agent choose correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_list_hostsA
List every SSH host this mcp-ssh-live server knows about, along with its runtime state (connected, active_jobs). Use this to discover available target aliases in a multi-host setup, to confirm that a --host flag or TOML entry landed as expected, and to check the per-host active-jobs count before spawning another job.
The response NEVER includes secrets — only the names of password environment variables and the paths of key files. The actual password / key contents are resolved only at SSH handshake time, not here.
Each entry has: alias, host, port, user, auth ('password_env' | 'key' | 'agent'), password_env (str or null), key (str or null), key_passphrase_env (str or null), connected (bool; whether we currently hold a live SSH transport), active_jobs (int; jobs registered on this host that are still running), is_default (bool).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses that the response never includes secrets, only references. It details the exact fields returned, providing complete behavioral transparency.
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 front-loaded with purpose and use cases, followed by a disclosure on secrets, then a field list. It is structured logically, though slightly verbose; could be more concise.
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 zero parameters and presence of an output schema (not shown but indicated), the description thoroughly covers the output fields and what is omitted, making the tool fully understandable.
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 tool has zero parameters, so baseline is 4. The description adds no parameter info, but that's appropriate. It instead adds value by explaining output structure beyond schema coverage.
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 starts with 'List every SSH host this mcp-ssh-live server knows about, along with its runtime state', clearly specifying verb and resource. It also distinguishes from sibling tools by mentioning discovery of targets, confirmation of config, and checking active jobs.
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?
It explicitly states use cases: to discover aliases, confirm config, and check active jobs before spawning. While it doesn't state when not to use, the context is clear and guides appropriate usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_list_jobsA
List every job registered on this mcp-ssh-live server — running, finished-but-not-yet-reaped, and crashed. Returns metadata only (job_id, cmd, host, label, started_at, finished_at, exit_status, status, pid, pty, stdout_lines, stderr_lines); use ssh_tail to fetch the actual output of a specific job.
Filter by host with the 'host' argument (alias or raw address). Pass include_finished=false to see only still-running jobs, which is typical when deciding whether to spawn a new one without hitting the max_jobs_per_host cap.
Finished jobs are kept in the registry for a grace window (reap_finished_after_sec, default 600 s) so you can still fetch their final tail output after they exit.
| Name | Required | Description | Default |
|---|---|---|---|
| host | No | If provided, only return jobs whose host equals this alias OR whose raw address matches. If omitted or null, return jobs across every host. We resolve the filter against the config so a user who types the raw IP still gets jobs tagged with the configured alias. | |
| include_finished | No | When True (default), return jobs in every lifecycle state. When False, skip jobs that have already exited / crashed / been killed — useful for capacity planning. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, description discloses key behaviors: returns metadata only, filter resolution against config, grace window for finished jobs (reap_finished_after_sec). Could mention auth or rate limits, but given the nature of a read-only list, this is solid.
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?
Three well-organized paragraphs: what the tool does, filtering, and reaping behavior. Front-loaded with key info. No unnecessary words.
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 the main aspects of the tool: listing, filtering, metadata-only return, and grace window. Output schema exists so return values are not needed. Slightly missing ordering or pagination, but these are minor for a local list 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?
Schema coverage is 100%, baseline 3. Description adds meaning beyond schema: explains host resolution (alias vs raw address) and the purpose of include_finished=false (capacity planning, avoiding max_jobs_per_host).
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 lists every job on the server, returns metadata only, and distinguishes from ssh_tail for fetching output. It is specific with verb 'list' and resource 'jobs'.
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?
Explicitly tells when to use this tool (to list jobs) vs when to use ssh_tail (to fetch output). Provides guidance on filtering by host and using include_finished=false for capacity planning.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_persistent_statusA
Check the status of a persistent job started by ssh_run_persistent. Returns whether the process is still running, the last lines of its stdout and stderr logs, and the exit code (if it finished).
You need the PID and the log paths returned by ssh_run_persistent. Example: ssh_persistent_status(pid=12345, out_log='/tmp/mcp-persistent/abc123/out.log', err_log='/tmp/mcp-persistent/abc123/err.log')
| Name | Required | Description | Default |
|---|---|---|---|
| pid | Yes | The remote PID returned by ``ssh_run_persistent``. | |
| out_log | Yes | Path to the stdout log file on the remote. | |
| err_log | Yes | Path to the stderr log file on the remote. | |
| host | No | Host alias. Defaults to the default host. | |
| tail_lines | No | How many lines to return from each log. Default 50. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description carries the full burden. It accurately describes the return values (running status, logs, exit code) and does not mention any destructive side effects. It is transparent about what the tool provides.
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 two sentences plus an example, all relevant. No wasted words, and the example concretizes usage.
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 output schema exists, the description still covers key return info. It also explains input dependencies and references the sibling. It is complete for the tool's purpose.
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 100%, so baseline is 3. The description adds value by emphasizing the required parameters and showing example values, but doesn't expand on optional parameters 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 the tool checks the status of a persistent job from ssh_run_persistent, specifying it returns running status, log lines, and exit code. It distinguishes itself from siblings by referencing the specific parent tool.
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 that the PID and log paths from ssh_run_persistent are needed, and provides an example. While it doesn't explicitly state when not to use it, the context is clear that it's for checking persistent jobs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_remove_jobA
Drop a job from the registry, freeing its slot against max_jobs_per_host and releasing its ring buffers.
Finished / crashed / killed jobs can always be removed. STILL-RUNNING jobs require force=True, which closes the SSH channel (sends EOF to the remote's stdin) but does NOT guarantee the remote process dies — for a hard kill call ssh_signal(KILL) FIRST and then ssh_remove_job.
Returns removed=True if a job was actually dropped, removed=False if the id wasn't present (a no-op, not an error — useful for idempotent cleanup scripts that don't know whether a job is still around).
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The id returned by ``ssh_spawn``. | |
| force | No | When True, remove the job even if it's still running. Closes the SSH channel as a side effect. When False (default), removing a running job returns an error with ``kind="still_running"``. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Describes side effects: force=True closes SSH channel and sends EOF but does not guarantee process death. Mentions return values (removed=True/False) and error kind for still-running without force. No annotation 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?
Five concise sentences, each adding essential information: core action, conditions, force behavior, return values. No filler, well-organized.
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 purpose, usage, behavioral traits, parameters, and return values comprehensively. Despite no annotations, the description fully equips an agent to invoke 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?
Adds meaning beyond schema: explains that force=True closes SSH channel and sends EOF, and that default behavior returns error with kind='still_running'. Schema already describes parameters, but description enriches context.
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 'Drop a job from the registry' with specific details about freeing slots and releasing ring buffers. It distinguishes from sibling tools like ssh_signal (for killing) and ssh_spawn (for creating).
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?
Explicitly explains when to use it (finished/crashed/killed jobs) and when not (still-running without force). Provides alternative: for hard kill, call ssh_signal(KILL) first. Also notes idempotency for cleanup scripts.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_run_persistentA
Run a command on the remote host that SURVIVES MCP disconnect, Zed restart, or any interruption to the current session. The process runs via nohup in the background; its stdout and stderr are saved to log files on the remote server.
Returns immediately with the remote PID and log file paths. Check progress later with ssh_persistent_status or manually: ssh_exec('tail -n 50 /tmp/mcp-persistent//out.log') ssh_exec('ps -p -o pid,stat,etime,cmd --no-headers || echo DEAD')
WHEN TO USE THIS (not ssh_spawn):
Multi-hour or overnight tasks (training runs, large parsers, database migrations, long builds).
Any task where losing the MCP connection would be catastrophic.
Tasks you want to start now and check on later.
WHEN TO USE ssh_spawn INSTEAD:
You need real-time streaming output in chat.
The task takes under ~10 minutes and you will stay connected.
NOTE: logs are NOT auto-deleted. Clean up manually with: ssh_exec('rm -rf /tmp/mcp-persistent/')
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | The shell command to run. Passed to ``bash -c``. Compound commands (``&&``, ``|``, subshells) work normally. | |
| host | No | Alias of the configured host, or its raw address. If omitted, the default host is used. | |
| label | No | Optional human-readable tag stored in the log dir alongside the command for future reference. | |
| work_dir | No | Base directory on the remote where per-job subdirectories are created. Default ``/tmp/mcp-persistent``. Use a path outside ``/tmp`` for jobs that must survive a server reboot. | /tmp/mcp-persistent |
| env | No | Optional environment variables set before the command. Names must match ``[A-Za-z_][A-Za-z0-9_]*``. | |
| cwd | No | Optional remote working directory (``cd`` before running). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so description fully covers behavior: nohup background execution, log files, immediate PID return, no auto-deletion, manual cleanup instructions. Includes example commands for status checks.
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 sections and bullets, no fluff. Every sentence serves a purpose, balancing thoroughness with conciseness.
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 tool complexity, description covers purpose, usage, behavior, all parameters, and cleanup. Output schema exists, so return values are already documented.
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?
Input schema coverage is 100%, so baseline is 3. Description adds some context (e.g., work_dir for reboot survival) but does not significantly enhance meaning beyond 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 runs a command that survives disconnects, using specific verbs ('Run') and resources ('remote host'). It distinguishes from sibling ssh_spawn by targeting long-running tasks.
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 'WHEN TO USE THIS' and 'WHEN TO USE ssh_spawn INSTEAD' sections provide clear context, including examples (multi-hour tasks, streaming needs).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_send_stdinA
Write bytes to the stdin of a running job (one spawned by ssh_spawn). Used for password prompts (sudo -S -p ''), REPL input (python, node), and any interactive CLI that expects keystrokes.
By default a trailing '\n' is appended so the remote program sees a complete line (this is what 'sudo -S' and most line-based REPLs need). Set newline=False to send raw bytes without the terminator — useful for control characters (e.g. data='\x03' to send ^C to a pty-mode job, or data='\x04' for ^D / EOF).
Set close_stdin=True to close the remote's stdin AFTER the write, which signals EOF to programs like 'cat', 'wc', 'sort'. Leaving it open (default) lets you make more writes to the same job.
IMPORTANT: for password input, pair this with ssh_spawn(pty=True) AND the command 'sudo -S -p ""'. Without pty=True, sudo refuses to read the password from a pipe on most distros. Without the -p "" flag, sudo writes its prompt to stdout, which mixes into ssh_tail output.
Payload size is capped at 64 KiB per call. For larger input use ssh_upload to place a file on disk, then have the command read from it.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The id returned by ``ssh_spawn``. | |
| data | Yes | The text to send, UTF-8 encoded on the wire. Can be empty; an empty ``data`` with ``newline=True`` sends just a single ``\n`` (useful for "press Enter" prompts). Control characters are passed verbatim, so ``"\x03"`` sends ^C to a pty-mode job's line discipline. | |
| newline | No | When True (default), append a ``\n`` to ``data`` unless it already ends with one. When False, send exactly the bytes in ``data`` — useful for control characters (``\x03`` for ^C, ``\x04`` for ^D) and for multi-write sequences that assemble a line progressively. | |
| close_stdin | No | When True, close the remote's stdin channel AFTER the write completes. Signals EOF to the remote program. When False (default), stdin stays open and you can make subsequent ssh_send_stdin calls. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses all key behaviors: trailing newline default, raw bytes with newline=False, EOF via close_stdin, 64 KiB payload cap. No annotations exist, so description fully covers behavioral disclosure.
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 paragraphs and bullets, but slightly long (multiple use-case examples). However, front-loaded with purpose and each sentence adds value, so not overly verbose.
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 prerequisites, alternatives, constraints, and behavior exhaustively. Output schema exists, so return values need not be detailed. No gaps given tool's 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?
Schema coverage is 100%, but description adds significant context: explains newline=False for control characters, close_stdin for EOF, and data for empty sends. Goes well beyond 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 the tool 'write[s] bytes to the stdin of a running job' and lists specific use cases (password prompts, REPL input, interactive CLI), distinguishing it from sibling tools like ssh_exec or ssh_upload.
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?
Explicitly tells when to use (e.g., for interactive input) and when not to (for large input, use ssh_upload). Provides crucial pairing instructions for password input (pty=True) and flags for sudo.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_signalA
Deliver a POSIX signal (TERM, KILL, INT, HUP, QUIT, USR1, USR2, STOP, CONT, and a few others) to a job started by ssh_spawn. Use TERM (polite, the default) for graceful shutdown; use KILL only when TERM fails to stop the job within a reasonable window.
Delivery strategy: we first try paramiko's in-channel send_signal (often ignored by OpenSSH sshd), then fall back to 'kill - ' on a fresh SSH session using the PID captured at spawn time. Both paths are attempted unless use_pid_fallback=False.
Pass wait_ms > 0 to block up to that many milliseconds waiting for the job to actually exit; the response then includes the real exit_status. Otherwise we return immediately with still_running indicating whether the signal took effect within the fire-and-forget window.
Common patterns:
Polite stop, wait up to 5 s:
ssh_signal(job_id, 'TERM', wait_ms=5000)
Hard kill, don't wait (verify via ssh_tail later):
ssh_signal(job_id, 'KILL')
Interrupt (like Ctrl-C):
ssh_signal(job_id, 'INT')
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The id returned by ``ssh_spawn``. | |
| signal | No | Signal name (``"TERM"``, ``"SIGTERM"``, ``"kill"``, …) or POSIX number (``9``, ``"9"``). Case-insensitive. Defaults to ``"TERM"`` — the polite choice. | TERM |
| wait_ms | No | If > 0, after delivering the signal we block up to this many milliseconds for the job's done_event to fire, and report the real ``exit_status`` in the response. Capped internally at 30000 ms. A value of 0 (default) returns immediately. | |
| use_channel | No | When True (default), try paramiko's in-channel ``send_signal`` first. Set to False to skip this path entirely — occasionally useful on a server known to reject it in ways that leave the channel in a bad state. | |
| use_pid_fallback | No | When True (default), open a fresh SSH session and run ``kill -<SIG> <pid>``. Requires the job to have been spawned with PID capture enabled (which is always the case via ssh_spawn). Set to False if you specifically want to verify whether ``channel.send_signal`` alone works on your server. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite no annotations, the description fully discloses the delivery strategy (paramiko send_signal then kill via SSH), wait behavior and cap, and the effect of boolean parameters. This is comprehensive and leaves no ambiguity.
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 with a summary, strategy, examples, and parameter details. Every sentence adds necessary information, and it front-loads the core purpose.
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 presence of an output schema, the description covers all needed aspects: purpose, usage, behavior, parameters, and common patterns. No missing context for an agent to correctly invoke the 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 100% schema coverage, the description still adds significant value by explaining job_id origin, signal naming flexibility, wait_ms blocking with cap, and the purpose of use_channel and use_pid_fallback. It goes 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 the tool delivers a POSIX signal to a job spawned by ssh_spawn. It lists specific signals, default behavior, and distinguishes from sibling tools like ssh_spawn and ssh_tail.
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 explicit guidance on when to use TERM vs KILL, explains fallback strategy, and gives common usage patterns with examples. It effectively tells the agent how to select and invoke the tool correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_spawnA
Start a shell command on the remote host IN THE BACKGROUND and return a short job_id immediately (<100 ms). Use ssh_tail to stream output line-by-line while the job runs.
WHEN TO USE ssh_spawn:
You need real-time output in chat (builds, deploys, log tails).
The command takes more than ~60s but you WILL stay connected.
WARNING — PROCESS DIES ON DISCONNECT: ssh_spawn ties the remote process to the MCP server's SSH channel. If the MCP server restarts, Zed closes, or the connection drops — the remote process receives SIGHUP and dies. Do NOT use ssh_spawn for tasks that must survive a disconnect (multi-hour training runs, overnight parsers, etc.).
FOR TASKS THAT MUST SURVIVE DISCONNECT — use ssh_exec with nohup or screen instead: ssh_exec('nohup python -u train.py > /tmp/train.log 2>&1 &') ssh_exec('screen -dmS myjob bash -c "python train.py"') Then check progress with: ssh_exec('tail -n 50 /tmp/train.log') ssh_exec('screen -r myjob')
BUFFERING GOTCHA: many programs block-buffer stdout when stdin is not a TTY, so you see nothing until exit. Fix: pass pty=True, or use 'python -u' / PYTHONUNBUFFERED=1 / 'stdbuf -oL'. pty=True merges stderr into stdout.
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | The shell fragment to run on the remote. Passed to an outer ``bash -c`` with a PID-capture prelude, then ``exec``'d into an inner ``bash -c "<command>"`` so your shell metacharacters (``|``, ``&&``, ``$VAR``, redirects) work normally. | |
| host | No | Alias of the configured host, or its raw address. If omitted, the server's default host is used. | |
| cwd | No | Remote working directory (``cd`` before running). | |
| env | No | Additional environment variables for the remote process. Names must match ``[A-Za-z_][A-Za-z0-9_]*`` (safe env name regex, enforced by the wrapper builder). | |
| pty | No | Allocate a remote pseudo-TTY. Needed for interactive programs (``sudo -S``, REPLs, ``passwd``) and for line-buffered output from programs that only flush to a TTY. Note: with pty=True the remote kernel merges stderr into stdout, and CRLF line endings may appear in the buffer (we strip the \r). | |
| label | No | Optional human-readable tag for ``ssh_list_jobs``. Useful when the LLM spawns several jobs on the same host and needs a memorable handle ("nightly-parser", "log-follow", …). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It transparently warns that the remote process dies on disconnect (SIGHUP), explains the buffering gotcha and its fix (pty=True, python -u), and clarifies pty behavior (stderr merge, CRLF stripping). All behavioral traits are disclosed.
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 headers and bullet points, front-loading the core purpose. It is comprehensive but slightly lengthy; however, every section adds value (usage, warning, alternatives, buffering tip). Minor conciseness improvements could be made, but overall it's effective.
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 all necessary aspects: core functionality, when to use vs avoid, lifecycle behavior, parameter context via schema, and output via schema. It is fully complete for an agent to correctly select and invoke this tool, given the sibling set and 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?
Schema coverage is 100% with detailed descriptions per parameter. The tool description adds context beyond the schema, notably the buffering gotcha and when to use pty. It does not restate all param details but provides practical guidance that enhances understanding.
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 starts a remote shell command in the background and returns a job ID immediately. It distinguishes from siblings like ssh_exec and emphasizes background execution with low latency, making the purpose unmistakable.
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 outlines when to use ssh_spawn (real-time output for long-running commands) and provides a warning against use for disconnect-survival tasks, with concrete alternatives using ssh_exec with nohup or screen. This is exemplary usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_tailA
Stream incremental output from a job started by ssh_spawn. Given a monotonic cursor 'since_line_no', return every buffered line with line_no > cursor (up to max_lines). If wait_ms > 0 and nothing new is available, block up to wait_ms milliseconds for new output. Returns still_running=False and exit_status when the job has finished.
Canonical polling loop (use this): cursor = 0 while True: r = ssh_tail(job_id, since_line_no=cursor, wait_ms=5000, max_lines=500, stream='both') for ln in r['lines']: show_to_user(ln) cursor = r['last_line_no'] if not r['still_running']: break
If r['buffer_truncated'] is True, older output was evicted from the ring buffer before you read it — either live with the gap or increase limits.ring_buffer_lines on the server. This tool does NOT open a new SSH connection per call; all data comes from in-memory buffers maintained by the spawn's reader threads.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The id returned by ``ssh_spawn``. | |
| since_line_no | No | Your cursor. 0 on the first call; on each subsequent call, pass the ``last_line_no`` from the previous response. Negative values are clamped to 0. | |
| wait_ms | No | If > 0 and no new lines are immediately available, block up to this many milliseconds waiting. Capped internally at 60000 ms so a misbehaving caller can't hold an MCP channel for 10 minutes. Values below 10 ms are treated as 0 (no wait) because condvar wakeup latency eats them anyway. | |
| max_lines | No | Maximum lines to return in this response. Capped internally at 10000. The cap is there to keep a single response payload small — if the buffer has more lines pending, just poll again with an updated cursor. | |
| stream | No | ``"stdout"``, ``"stderr"``, or ``"both"`` (default). "both" merges the two streams by line_no, which gives the true interleaved order a human would see. | both |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, but the description fully discloses blocking behavior, exit status, buffer cap, and polling mechanics. It explains internal caps and clamping, leaving no ambiguity about the tool's behavior.
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 a clear front-loaded purpose, followed by a concrete usage example and additional notes. While lengthy, every sentence adds essential context 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 (5 parameters, output schema), the description covers all aspects: usage pattern, parameter details, behavioral nuances, and error handling (buffer truncation). No gaps are apparent.
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 100% (all parameters described), but the description adds significant value by explaining the polling loop, cursor usage, and stream merging details. This goes beyond the schema's basic 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 specifies it streams incremental output from a job, using specific verbs like 'stream' and 'return'. It distinguishes from siblings like ssh_spawn by focusing on reading output rather than starting jobs.
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 a canonical polling loop with clear steps, explains when to use (incremental output) and handles edge cases like buffer truncation. Explicitly states it reuses existing SSH connections, differentiating from tools that open new connections.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_uploadA
Upload a local file to the remote host via SFTP. Binary-safe; preserves arbitrary bytes unlike 'cat > file <<EOF' over ssh_exec.
Writes to .partial first, then renames atomically on success — a crashed upload never leaves a half-written file at the target path. Parent directories are auto-created (mkdir -p). If remote_path points at an existing directory, the local filename is appended (same as scp).
Size cap: max_bytes (default 100 MiB, hard ceiling 1 GiB). Returns a sha256 of the bytes actually transferred so the caller can verify integrity.
Optional mode (octal, e.g. 493 for 0o755) runs chmod on the remote after the transfer. A chmod failure is non-fatal — the bytes are already there; mode_set=null in the response means the chmod was skipped or rejected.
| Name | Required | Description | Default |
|---|---|---|---|
| local_path | Yes | Absolute or relative path to a local file. Resolved against the server's current working directory; using an absolute path avoids surprises. | |
| remote_path | Yes | Destination path on the remote host. Can be a full file path (overwritten atomically) or a directory (local filename appended). Intermediate directories are created automatically. | |
| host | No | Alias of the configured host, or a raw address. If omitted, the server's default host is used. | |
| mode | No | Optional POSIX mode bits (int), e.g. 0o755 or 493. Applied via chmod after the upload. Pass None to skip. | |
| max_bytes | No | Hard cap on the transfer size. Default 100 MiB, clamped to 1 GiB. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully discloses behavioral traits: binary-safety, atomic rename, parent directory creation, directory target behavior, size cap, SHA256 return, optional chmod with non-fatal failure. This is comprehensive.
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 yet informative, starting with a clear summary followed by detailed bullet points. 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 no annotations and an existing output schema, the description covers edge cases (directory target, partial writes, chmod failure), return values, and constraints. It is fully complete for an agent to use effectively.
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 100%, but the description adds significant value: explains local_path resolution, remote_path directory behavior, host alias usage, mode as octal example, max_bytes default and cap. This goes well beyond the 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 explicitly states 'Upload a local file to the remote host via SFTP,' using a specific verb and resource. It immediately differentiates from sibling tools like ssh_exec by highlighting binary safety and atomic write, making the purpose unambiguous.
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 clearly indicates when to use this tool (for binary-safe uploads, atomic writes) and contrasts with ssh_exec. It does not explicitly state when not to use it, but the context is sufficient for an agent to decide.
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.
14 tool updates
v0.1.0- First observed
ssh_connect - First observed
ssh_disconnect - First observed
ssh_download - First observed
ssh_exec - First observed
ssh_list_hosts - First observed
ssh_list_jobs - First observed
ssh_persistent_status - First observed
ssh_remove_job - First observed
ssh_run_persistent - First observed
ssh_send_stdin - First observed
ssh_signal - First observed
ssh_spawn - First observed
ssh_tail - First observed
ssh_upload
TDQS
Every tool has a clearly distinct purpose: connect/disconnect manage sessions, list hosts/jobs provide introspection, exec/spawn/persistent cover execution modes, upload/download handle file transfer, and tail/signal/send_stdin/remove_job/status manage spawned jobs. No two tools could be confused.
All tools follow a consistent snake_case pattern with the 'ssh_' prefix and a verb_noun structure (e.g., ssh_connect, ssh_download, ssh_list_hosts). There are no deviations or mixed conventions.
14 tools cover the full lifecycle of SSH operations—connection, command execution (synchronous, async with streaming, persistent), file transfer, job monitoring, and signaling—without being excessive. Each tool serves a distinct and necessary purpose.
The tool set provides a comprehensive surface for remote host management: connection management, multiple execution modes (blocking, streaming, persistent), file upload/download, stdin interaction, signaling, and comprehensive job tracking. There are no obvious gaps for the intended purpose of an SSH live server.
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
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Scoped, audited SSH exec, sessions, and SFTP on your saved servers without exposing credentials
Remote shell and detached long-running jobs on your own machines — no SSH, open ports or VPN.
Develop, manage, and debug Railway projects, services, and deployments from within agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables LLMs to interact with remote servers via SSH, supporting command execution, file upload/download, and directory listing.7MIT
- FlicenseAqualityDmaintenanceProvides LLMs with safe, controllable, and stateful SSH access to shell environments, enabling remote command execution and file transfers with async support and security features.153444-
- FlicenseAqualityCmaintenanceEnables LLMs to securely SSH into remote servers, execute commands, and manage files via SFTP including listing, reading, writing, deleting, and renaming files.11-
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to have persistent, fully interactive SSH sessions into remote hosts, behaving like a local terminal.231MIT
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/pmboxbiz/mcp-ssh-live'
If you have feedback or need assistance with the MCP directory API, please join our Discord server