exec-mcp
Provides ChatGPT-native artifact transfer, enabling bidirectional file exchange between ChatGPT and a remote environment via SSH, with SHA-256 verification and atomic commits.
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 strict TypeScript Node.js gateway with no runtime npm dependencies 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.
ChatGPT-native bidirectional artifact transfer using file references, binary SSH streaming, SHA-256 verification, and atomic remote commits.
Embedded-resource export that materializes verified remote files into ChatGPT without model-authored Base64.
Unified Exec Job Manager with synchronous exec, asynchronous start_exec, queued admission, recent status lookup, and idempotent cancellation.
Incremental retained job logs with independent stdout/stderr cursors and bounded long-polling.
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.
Strict TypeScript source compiled to JavaScript for production.
No runtime npm dependencies.
Related MCP server: SSH Real MCP Server
MCP tools
Tool | Purpose |
| Run one bounded non-interactive command synchronously through the Job Manager. |
| Submit one bounded background command and immediately return a queryable |
| List queued and running remote executions plus sync/async/global admission capacity. |
| Read status plus incremental redacted stdout/stderr with independent cursors and bounded long-polling. |
| Idempotently cancel a queued or running execution; terminal states are immutable. |
| Transfer a current ChatGPT file into the remote environment with SHA-256 verification and atomic commit. |
| Transfer one verified remote file to ChatGPT as an embedded resource for host-side materialization into |
The control-plane tools are operator-wide. They assume one trusted tenant and are intentionally available even when command capacity is full.
Choosing exec vs start_exec
Use exec for short, deterministic commands when the next reasoning step needs the result immediately. A useful rule of thumb is roughly five seconds or less: pwd, ls, cat/grep, git status/git diff, kubectl get, and similar probes.
Use start_exec when runtime is uncertain, may exceed a few seconds, or useful work can continue in parallel. Typical examples are dependency installation, test suites, builds, image builds, scans, migrations, and long scripts. Keep the returned exec_id, continue independent work, and use get_exec_status at the synchronization point (optionally with bounded wait_seconds) rather than busy-polling immediately after submission.
Do not emulate background execution inside exec with nohup, disown, or shell &. Pass the real foreground command to start_exec so timeout, cancellation, status, retained logs, and remote process-group cleanup remain owned by the Job Manager. Use concise label values when several independent jobs run concurrently.
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.6.2The example binds only to loopback. Add authentication and TLS at the surrounding transport layer before making the service reachable from another machine.
Container tags follow the repository release model:
vX.Y.Zis the versioned release tag; treat release tags as immutable by policy.sha-<short-commit>identifies an exact commit build; an image digest is the strongest immutable deployment reference.maintracks the latest successful default-branch build and should not be treated as an immutable production version.
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.
Synchronous exec keeps bounded final stdout/stderr tails for compatibility.
get_exec_status reads bounded retained Job Manager logs incrementally with independent stdout/stderr cursors. has_more_* means another retained page is available; *_log_truncated means older bytes were permanently discarded.
Runtime timeout starts only when a queued job enters execution; queue waiting time does not consume timeout_seconds.
V1 Job Manager state and retained logs are process-local; service restart does not recover prior queued or running records.
Cancellation boundary
cancel_exec, MCP cancellation notifications, HTTP disconnects, and timeouts request termination of the isolated remote command process group. A short secondary SSH control request writes a per-job cancellation marker under /tmp/exec-mcp-runtime, and the remote wrapper terminates the command PGID with SIGTERM followed by bounded SIGKILL escalation. Queued jobs can be cancelled before a process is spawned, and terminal job states are immutable. Running cancellation records remote_exit_confirmed=true only after the remote wrapper acknowledges cleanup; an unconfirmed remote termination finalizes as failed rather than claiming cancelled.
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. |
|
| Legacy/default concurrency value used as the fallback for sync, async, and global limits when their dedicated variables are omitted. |
|
| Maximum running synchronous |
|
| Maximum running asynchronous |
|
| Maximum running jobs across both admission classes. |
|
| Maximum jobs waiting for an admission slot. |
|
| Maximum retained Job Manager stdout bytes per stream and stderr bytes per stream. Older retained bytes are discarded. |
|
| In-memory retention period for finalized Job Manager log buffers. |
|
| Default combined incremental stdout/stderr bytes returned by one status query. |
|
| Hard ceiling for one incremental status-output page. |
|
| Maximum long-poll duration accepted by |
|
| Retained tail capacity per stream. |
|
| SSE heartbeat interval. |
|
| Delay between termination and forced kill. |
|
| Maximum MCP request body size. |
|
| Absolute artifact size ceiling. Imports may use the full value; exports are additionally capped by |
|
| Configurable remote-export ceiling, hard-capped at 1.45 MB (1,450,000 bytes). Lower values are allowed; larger files are rejected with no URL fallback. |
|
| Maximum concurrent artifact imports and exports. |
|
| Local temporary/cache directory for artifact transfer. |
|
| Identifier base placed in embedded-resource URIs. It is metadata only; the host receives bytes from the MCP |
|
| End-to-end artifact transfer timeout. |
| empty | Optional comma-separated exact hosts or suffix rules. A leading dot matches the suffix and all subdomains, for example |
|
| Allow HTTP file-reference URLs. Intended only for local tests. |
|
| 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.
ChatGPT artifact transfer
Use import_chatgpt_file for files attached to or generated in the current ChatGPT conversation. The tool declares _meta["openai/fileParams"], so ChatGPT replaces the conversation-local file path with a temporary { download_url, file_id, mime_type?, file_name? } reference. exec-mcp downloads the binary bytes to a bounded local spool, computes SHA-256, streams the bytes over SSH, verifies the remote hash, and commits the destination atomically. Retried calls are idempotent when the existing destination has identical bytes.
Use export_remote_file for the reverse direction. It streams the remote file into a bounded local spool, verifies size and SHA-256, reads the verified bytes, and returns exactly one MCP embedded binary resource plus structured metadata (bytes, sha256, file_name, embedded=true, and delivery_mode=embedded_resource). A compatible ChatGPT host can materialize that resource as a real file in /mnt/data while preserving file_name.
Exports larger than ARTIFACT_EMBED_MAX_BYTES are rejected with file_too_large; the service deliberately provides no resource_link, public download URL, or large-file fallback. The embedded blob is Base64 at the MCP protocol layer, so the practical ceiling must account for Base64 expansion, JSON framing, tunnel limits, host materialization limits, and gateway memory. The hard maximum and default are 1.45 MB (1,450,000 bytes). This conservative ceiling is covered by backend boundary tests and intentionally leaves margin below the platform-sensitive range observed during ChatGPT host ingestion/materialization testing.
Secure MCP Tunnel carries the embedded bytes inside MCP JSON-RPC, so remote-to-ChatGPT export requires no public artifact ingress. Keep /mcp, /exec, /metrics, and /healthz private behind the authenticated transport.
Development
npm test # strict build and regression tests
npm run build # strict type-check and compile to dist/
npm run test:memory # bounded-output and RSS smoke test
npm run validate # tests, HTTP/SSE, and memory smoke testsRuntime source is organized by responsibility: server.ts for HTTP composition and lifecycle, mcp-handler.ts for JSON-RPC dispatch, tool-schemas.ts for stable schemas, artifact-transfer.ts for verified bidirectional file transfer, and metrics.ts for Prometheus rendering.
CI 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.
Related MCP Servers
- FlicenseBqualityDmaintenanceEnables safe execution of system shell commands with real-time streaming output and rich metadata capture. Provides configurable command execution with timeout controls, environment management, and extensible plugin architecture for monitoring command lifecycles.4
- AlicenseAqualityDmaintenanceEnables SSH remote command execution on any host using the system ssh binary, supporting existing configurations like ssh-agent, ProxyCommand, and jump hosts.2501MIT
- AlicenseBqualityCmaintenanceEnables secure remote and local command execution via SSH, with session management and environment variable support.1323MIT
- FlicenseNot gradedqualityDmaintenanceProvides secure execution of terminal commands (PowerShell, CMD, shell) with configurable security policies including command blocking, path restrictions, and timeout.1
Related MCP Connectors
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Execute PowerShell commands securely with controlled timeouts and input validation. Retrieve syste…
Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.
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