exec-mcp
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., "@exec-mcprun kubectl get pods -A"
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.
exec-mcp
A dependency-free Node.js gateway that gives trusted MCP clients bounded remote command execution and file transfer over SSH.
exec-mcp deliberately stays small: it validates paths and resource limits, runs a non-interactive remote shell, streams or returns bounded output, and exposes execution lifecycle controls. Higher-level behavior remains in tools already installed on the remote host.
This service is a remote command execution gateway. It has no built-in user authentication or TLS termination and is designed for a trusted, single-tenant connection. Never expose it directly to an untrusted network. Put it behind an authenticated transport or reverse proxy, restrict network access, use a dedicated low-privilege SSH account, and review thethreat model before deployment.
Features
MCP Streamable HTTP and an HTTP/SSE execution endpoint.
Configurable command timeout, output limit, concurrency limit, and bounded tail buffers.
Remote working-directory allowlist with realpath and symlink-escape checks.
Base64 file upload and download with path and size validation.
Active execution listing, recent status lookup, and idempotent cancellation.
Process-group cleanup, timeout escalation, disconnect cancellation, and emergency circuit breaking.
Secret-pattern redaction for streamed output and retained tails.
Prometheus-compatible metrics and health endpoints.
Execution-capacity gauges and duration histograms for latency percentiles.
No runtime npm dependencies.
Related MCP server: SSH Real MCP Server
MCP tools
Tool | Purpose |
| Run one bounded non-interactive command on the configured remote host. |
| List active executions without consuming an execution slot. |
| Read an active execution or a record from bounded recent history. |
| Idempotently request cancellation of an active execution. |
| Read one allowed remote file and return base64-encoded bytes. |
| Write or append base64-encoded bytes to one allowed remote file. |
The control-plane tools are operator-wide. They assume one trusted tenant and are intentionally available even when command capacity is full.
Quick start
Requirements
Node.js 20 or newer, or Docker.
An SSH-reachable remote host with
/bin/shand Python 3.A dedicated SSH key and a pinned
known_hostsfile.
Run with Docker
docker run --rm \
--name exec-mcp \
-p 127.0.0.1:8080:8080 \
-p 127.0.0.1:9090:9090 \
-e REMOTE_HOST=remote-host \
-e REMOTE_USER=execmcp \
-e REMOTE_KEY_PATH=/run/secrets/id_ed25519 \
-e REMOTE_KNOWN_HOSTS_PATH=/run/secrets/known_hosts \
-e REMOTE_STRICT_HOST_KEY_CHECKING=yes \
-e ALLOWED_CWDS=/workspace,/tmp \
-e DEFAULT_CWD=/workspace \
-v "$PWD/id_ed25519:/run/secrets/id_ed25519:ro" \
-v "$PWD/known_hosts:/run/secrets/known_hosts:ro" \
ghcr.io/3011/exec-mcp:v0.3.0The example binds only to loopback. Add authentication and TLS at the surrounding transport layer before making the service reachable from another machine.
Run from source
git clone https://github.com/3011/exec-mcp.git
cd exec-mcp
npm ci
npm run validate
REMOTE_HOST=remote-host \
REMOTE_USER=execmcp \
REMOTE_KEY_PATH="$HOME/.ssh/id_ed25519" \
REMOTE_KNOWN_HOSTS_PATH="$HOME/.ssh/known_hosts" \
REMOTE_STRICT_HOST_KEY_CHECKING=yes \
ALLOWED_CWDS=/workspace,/tmp \
DEFAULT_CWD=/workspace \
npm startInterfaces
GET /healthzGET /metricsPOST /execwithAccept: text/event-streamPOST /mcpfor MCP Streamable HTTP / JSON-RPCOptional separate metrics listener on
METRICS_PORT
MCP initialization
curl -fsS http://127.0.0.1:8080/mcp \
-H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"example-client","version":"1.0.0"}}}'Execute a command
{
"command": "git status --short",
"cwd": "/workspace",
"timeout_seconds": 120,
"max_output_bytes": 5242880,
"env": {
"NO_COLOR": "1"
},
"label": "inspect repository status"
}Commands are evaluated by /bin/sh -c on the configured remote host. The caller is intentionally allowed to supply arbitrary shell text; authorization must therefore happen before requests reach this service.
Output semantics
/execemits SSE lifecycle events.MCP
tools/callreturns bounded final text plus structured content; it does not stream live command events.The final execution summary is authoritative for exit code, signal, timeout, duration, byte counts, and truncation.
Stderr output alone does not indicate failure. A non-zero exit code, signal, or timeout does.
Once the forwarding limit is reached, output is still drained so the child process cannot block on a full pipe.
Only bounded stdout and stderr tails are retained.
Cancellation boundary
cancel_exec, MCP cancellation notifications, HTTP disconnects, and timeouts request termination of the local SSH transport process group. Capacity is released only after runner finalization. A confirmed local SSH transport exit does not prove that every independently detached remote descendant has exited.
Configuration
Variable | Default | Description |
|
| Main HTTP listen address. |
|
| Main HTTP port. |
|
| Optional separate metrics/health port. |
|
| SSH-compatible executable. |
| empty | Additional arguments passed before generated SSH arguments. |
| empty | Required remote host. |
|
| Remote SSH port. |
|
| Remote SSH user. |
| empty | Required private-key path. |
|
| Pinned SSH host-key file. |
|
| SSH host-key checking mode. |
|
| Comma-separated remote directory allowlist. |
| first allowed path | Default remote working directory. |
|
| Default command timeout. |
|
| Hard command timeout ceiling. |
|
| Default combined forwarded-output limit. |
|
| Hard forwarded-output ceiling. |
|
| Maximum active commands. |
|
| Retained tail capacity per stream. |
|
| SSE heartbeat interval. |
|
| Delay between termination and forced kill. |
|
| Maximum downloaded file size. |
|
| Maximum decoded upload size. |
|
| Maximum MCP request body size. |
|
| Number of finalized executions retained in memory. |
|
| Expose a redacted command preview in operator status. |
|
| Emit structured execution lifecycle logs. |
For all lifecycle and circuit-breaker settings, see DESIGN.md.
Development
npm test # 53 tests
npm run build # syntax checks
npm run validate # tests, syntax checks, and local HTTP/SSE smoke testCI runs the test suite and builds the container. CodeQL and Dependabot configuration are included in the repository.
Documentation
Versioning
The project uses Semantic Versioning. The version in package.json, MCP serverInfo, Git tags, GitHub Releases, and published container tags must match. Historical internal architecture labels are not part of the public version scheme.
License
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/3011/exec-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server