The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Termcp listing page.
English | 中文
Termcp is an AI-native terminal platform: many hosts and many sessions at once, fully visualized. Those sessions are managed and maintained by humans and AI together, each side free to take over from or hand back to the other at any time; connection profiles are maintained independently by the platform, so an Agent can use a connection without ever reading its credentials.
On the platform layer, long-lived sessions with read-only replay, parallel multi-host / multi-session orchestration, and a full SSH connection lifecycle keep the whole loop observable, programmable, and easy to hand off between human and AI. Cross-platform and cloud-native, written in pure Go with no CGO: it ships as a single lightweight binary that runs persistently with low overhead, and goroutine concurrency keeps it high-throughput and low-latency.
https://github.com/user-attachments/assets/d06a3c36-250a-4eeb-aefa-e80d13d1551c
A powerful Web UI manages many hosts and many sessions in one place: start it locally with a single command or deploy the container to the cloud — the browser gets the same interface either way.

htop's live display, vim's editing process, or an installer's prompts in the browser, just like a local terminal.Seamless human–agent interaction and pair operation: the Agent is a standing user of the terminal, alongside you and your scripts.
An Agent natively runs only one-shot commands, while real work is largely multi-turn interaction — SSH login needs a password first, a Python REPL is debugged line by line, an installer asks [Y/n], tools like top/htop/impacket need a terminal. Termcp hands the Agent a real terminal: one session stays alive and gets reused, so TUIs, REPLs, GDB, msfconsole and vim can be driven continuously the way a human would — through MCP or through the instance's own Agent Skill over plain curl.


docs/mcp-tools.md); shell_output pages by tail/offset cursors so only the slices you ask for ever enter the context window; shell_notify sends a bare wake-up signal./api.md and /skills.md (no token needed) and registers them as MCP resources plus a learn-api prompt, so a fresh Agent can drive this exact instance straight away, using only these two files.ssh_config are stored only on the host, and the MCP read interface returns profile names only; the SSH-config write tools stay off unless the operator opts in with --mcp-manage-ssh-configs.termcp://<entry> starts a fresh session.notify_user reaches you directly, privileged prompts are meant to be typed by you in the Web UI, and writes to one shell are serialized, so a human and an Agent can type on the same terminal with their inputs applied in order.go install github.com/open-mcp-ai/termcp@latest; builds with CGO_ENABLED=0 and binds no system shared libraries, so one static binary runs anywhere and cross-compiles natively (ConPTY on Windows, POSIX PTY on macOS / Linux — same behaviour everywhere).sudo / password / MFA prompts for you to type in the Web UI; input is serialized so keystrokes never collide.ssh_config="internal") or any remote machine over SSH profiles; commands, file transfer (SFTP plus resumable HTTP URLs), and port forwarding (-L / -R / -D) all run over that single connection./api.html holds the API / MCP / SKILLS cheat sheet.termcp://<entry>) and carry on.shell_notify wakes the Agent on process exit, silence, or new output — signal only, no payload (pull the text when needed); channel="sampling" sends sampling/createMessage directly.ssh_config are never readable back, so plaintext never enters the Agent's context; config-writing tools stay off unless --mcp-manage-ssh-configs is set.The fastest way to install — one command, no clone, no build:
go install resolves the module through the Go proxy (use GOPROXY=https://goproxy.cn,direct in mainland China) and drops the termcp binary into $(go env GOPATH)/bin — make sure that directory is on your PATH. Termcp is written in Go, so install is go install or a prebuilt Release binary: there is no npx/uvx variant, and it needs no Node or Python runtime. Being a Go module, it also supports source-level integration: go get github.com/open-mcp-ai/termcp to bring it in as a dependency, or fork and build a customized binary from source. Then run:
Head to the Releases page and download the pre-built binary for your platform:
| Platform | File |
|---|---|
| Linux (x86_64) | termcp-linux-amd64 |
| Linux (ARM64) | termcp-linux-arm64 |
| macOS (Intel) | termcp-darwin-amd64 |
| macOS (Apple Silicon) | termcp-darwin-arm64 |
| Windows (x86_64) | termcp-windows-amd64.exe |
| Windows (ARM64) | termcp-windows-arm64.exe |
Open http://127.0.0.1:18765 in your browser to enter the Web UI.
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | HTTP bind address. 0.0.0.0 listens on all interfaces. A non-loopback bind requires an auth token/hash (startup fails otherwise). |
--port | 18765 | HTTP port. Shared by the Web UI, MCP SSE, MCP streamable HTTP, and the docs/skill endpoints (/api.md, /skills.md). |
--data-dir | ~/.termcp | Persistence directory (sessions, SSH configs). Auto-created. Default overridable via $TERMCP_DATA_DIR. |
--log-level | info | Log level: debug / info / warn / error. debug shows all MCP tool calls; failed tool calls and session-create errors log at warn/error regardless. |
--no-internal | false | Disable the built-in loopback SSH profile. |
--mcp-manage-ssh-configs | false | Enable MCP tools to create/edit/delete SSH configs (secrets are never exposed). |
--auth-token | (unset) | Static token for HTTP authentication (or $TERMCP_AUTH_TOKEN). Every client — API, MCP, browser — must present it. Mutually exclusive with --auth-hash. |
--auth-hash | (unset) | Salted SHA-256 hash of the token (sha256-<salt_hex>-<digest_hex>) so the server never holds the plaintext (or $TERMCP_AUTH_HASH). Generate with termcp --gen-auth-hash. Mutually exclusive with --auth-token. |
--disable-auth | false | Turn HTTP authentication off on purpose, including on a non-loopback bind (or $TERMCP_DISABLE_AUTH_TOKEN=1). Pair it with a loopback port so only local callers can reach the port. Combining it with --auth-token/--auth-hash is an error rather than a silently-won argument. |
--mcp-defer-tools | false | Tag low-frequency MCP tools (file_*, forward, shell_resize, …) with defer_loading so clients fetch their schemas on demand, shrinking the initial tools/list. Off by default: clients that ignore the marker — or talk to Termcp through a gateway that drops it — would otherwise never see those tools. See Deferred tool loading. |
--gen-auth-hash | (action) | Generate the salted SHA-256 hash of a token for --auth-hash, then exit (token from an argument, or from stdin without echo on a terminal). |
--version | (action) | Print version, commit, and build date, then exit. The version follows the git tag automatically (release builds inject it via -ldflags; plain go build / go install module@vX.Y.Z falls back to the module version embedded by the Go toolchain). |
These flags are your capability gates: --no-internal narrows Agents to remote hosts only, and --mcp-manage-ssh-configs is what opens SSH-config write access. Tighten or loosen what Agents can touch per scenario. See Authentication below.
A single static token protects the whole HTTP surface — the Web UI, REST API, MCP SSE, MCP streamable HTTP, and the browser WebSocket. (The read-only docs /api.md and /skills.md stay public, so an agent can fetch them before it has a token.) Configuring it is optional for loopback-only binds (127.0.0.1 keeps its no-setup default); exposing a non-loopback bind without a token is a startup error.
How each client presents the token:
| Client | Credential |
|---|---|
| API / MCP / curl | Authorization: Bearer <token> header |
| Browser (Web UI) | Native login prompt on 401 — the username is ignored (leave it empty), the token is the password. A termcp_token cookie is then set automatically so same-origin WebSocket handshakes authenticate too. |
Behavior notes:
--auth-token and --auth-hash are mutually exclusive; a flag value overrides the environment variable of the same setting.user:pass string when it equals the token, so clients that split at the first colon (e.g. curl -u user:pass) still authenticate. curl -u :<token> remains the canonical form.0.0.0.0, a LAN IP, or a hostname other than localhost), so an accidentally exposed instance can never run unauthenticated.--disable-auth (or TERMCP_DISABLE_AUTH_TOKEN=1) explicitly lifts that requirement. It is the escape hatch for loopback-only setups — demo videos, screen recordings, single-user workstations — where the token protects nothing. Because it is a deliberate override, combining it with --auth-token/--auth-hash is a startup error rather than a silently-won argument, and the startup log switches from the informational auth line to a warning.termcp_token cookie then gets the Secure flag automatically only when the request arrived over TLS.Zero setup: ssh_config="internal" drives the Termcp host itself. To reach a remote machine, create an SSH profile — in the Web UI's new-connection dialog (it ships a TOML template and a Test connection button), or via the REST API PUT /api/connections/<name> with a TOML body:
Profiles live in data-dir/ssh_configs/<name>/config.toml; list them with ssh_config(action=list). Credentials written this way are never readable back. Agents can create profiles too, but only when Termcp was started with --mcp-manage-ssh-configs.
The registry image runs as non-root termcp (uid/gid 1000) with /home/termcp declared a VOLUME — all state (sessions, SSH configs, transcripts) defaults to ~/.termcp. It carries only the binary: no baked-in entrypoint or exposed port, so the run command decides the bind address.
Shell examples are single-line on purpose: a
\continuation is valid bash but a syntax error in PowerShell, so every command pastes as-is into bash, zsh, and PowerShell.
--host 0.0.0.0 is reachable from outside the container, so an auth token is required. MCP endpoint: http://localhost:18765/stream. With a bind mount instead of a named volume, chown the host directory first: chown -R 1000:1000 /path/on/host.
For a throwaway demo, a screen recording, or a single-user workstation, the token is friction with no benefit. Publish the port on the host loopback only and tell Termcp explicitly that the missing credentials are intentional:
Two details make this safe rather than merely convenient. -p 127.0.0.1:18765:18765 binds the published port to the host's loopback, so the container stays reachable to this machine and invisible to the LAN — the container itself still listens on 0.0.0.0 because that is the only address routable from outside its network namespace. And --disable-auth is required precisely because Termcp refuses to start unauthenticated on a non-loopback bind: the flag is the operator taking responsibility, which is why it also downgrades the startup log to a warning. The equivalent environment form is -e TERMCP_DISABLE_AUTH_TOKEN=1 instead of the flag.
Drop this Dockerfile into your application project: the build stage installs Termcp with go install, then COPY --from copies the binary into the target image — no Go runtime needed there.
Swap GOPROXY or GO_IMAGE with --build-arg if you need another module proxy or base-image mirror.
Containers must bind 0.0.0.0, and a non-loopback bind requires authentication (TERMCP_AUTH_TOKEN / TERMCP_AUTH_HASH) or startup fails.
Append --mcp-manage-ssh-configs to open the SSH-config write tools to Agents.
If Termcp must share a container with another main process, start it from the existing entrypoint or process manager; otherwise run it as a separate service and reach it at http://termcp:18765/stream.
Termcp speaks both MCP transports on the same port (18765). Choose whichever your client supports — the tool surface is identical.
Termcp is a long-running service: the same port serves the Web UI, any number of MCP clients, and session persistence. It therefore offers HTTP transports only — Streamable HTTP and SSE — and does not support stdio (there is no local subprocess mode).
Alternative: the Agent Skill drives the same sessions over plain curl — the instance serves it at /skills.md. The MCP server is one interface layer of the platform, embeddable into any MCP-capable host — Claude Code, Cursor, Codex, Open WebUI, or your own client.
/stream)The modern MCP transport; a single endpoint, no separate message path. Use this for Claude Code, Open WebUI, and most current clients.
http://127.0.0.1:18765/stream.http://host.docker.internal:18765/stream (macOS/Windows), or the host's LAN IP.http://termcp:18765/stream./sse)The legacy SSE transport. Configure only /sse; the SDK posts JSON-RPC to /message automatically.
http://<host>:18765/streamhttp://<host>:18765/sse (JSON-RPC goes to POST /message)The Web UI's API / MCP / SKILLS page (/api.html) offers copy-ready config for both transports, plus the Agent-docs and skill-download addresses for this instance.
Don't want to configure an MCP client? The instance ships an installable
Agent Skill that teaches any agent to drive Termcp with curl alone —
including the termcp:// locators users paste from the Web UI.
Restart the agent session after installing (skills are loaded at session start).
Claude Code has no per-skill CLI command — adding is "drop the file in", removing
is rm -rf ~/.claude/skills/termcp (or claude plugin install/uninstall when the
skill ships as a plugin).
Once installed, a request as simple as "open termcp://rock64 and run uname -a"
works end to end: the skill resolves the locator via
GET /api/resolve?url=..., creates the session with that ssh_config, sends the
command, and polls the output. The same skill is registered as the MCP resource
<origin>/skills.md, and /api.html shows the exact install command for the
instance you are looking at.
Skip MCP and use the same session layer programmatically: the full REST API and live WebSocket channel.
Live terminal I/O runs over WebSocket /api/ui/ws; files support direct HTTP URLs with Range resume. Full endpoint list in docs/api.md.
When the server runs with --auth-token/--auth-hash, every MCP request needs the token as an Authorization: Bearer header:
Keep the token out of URLs and out of shared configs/screenshots. curl and scripts use the same header:
Termcp exposes 31 MCP tools. Full parameters, return shapes, and error codes live in docs/mcp-tools.md.
| Area | Tools |
|---|---|
| Sessions (connection containers) | session_start, session_list, session_info, session_terminate (close; keeps the DEAD entry readable), session_delete (permanent) |
| Shells (terminal channels) | shell_open, shell_list, shell_close, shell_input, shell_key, shell_output, shell_resize, shell_reader_register, shell_reader_unregister |
| Notifications | shell_notify (wakes the AI Agent), notify_user (toasts the human at the Web UI) |
| SSH profiles | ssh_config (list; create/edit/copy/delete with --mcp-manage-ssh-configs) |
| Port forwarding | forward (-L / -R / -D / list / close) |
| Files (SFTP) | file_read, file_write, file_stat, file_delete, file_rename, file_mkdir, file_urls, file_perm, file_link, file_fs, file_getwd |
| Transcript index | message (span list; bytes via shell_output) |
| Host discovery | shell_detect |
Run a command as shell_input + shell_key(key="enter") + shell_output. Failed tools return isError=true with a JSON body carrying a stable error_code.
An MCP client fetches every tool's JSON schema in tools/list, so tool-heavy servers pay for that in context budget. The MCP spec offers an escape hatch: mark low-frequency tools with defer_loading, and a client loads their schema on demand. Termcp's 31 tools split into a hot path of 12 (session lifecycle + shell input/output — always listed) and 19 wide, low-frequency surfaces (the 11 SFTP file_* tools, forward, shell_resize/shell_detect/shell_notify, shell_reader_register/shell_reader_unregister, message, ssh_config).
--mcp-defer-tools turns the marker on and is off by default, so:
defer_loading marker. With the marker lost, those tools are not reloadable on demand and would simply vanish from the model's view.--mcp-defer-tools — the 19 low-frequency tools carry defer_loading; the 12 core tools stay eager so the session_start → shell_input → shell_output loop never requires a search round trip. Clients that support on-demand loading (mcp-go based clients, Claude Code) pay only for the schemas they actually use.Same 31 tools either way: enabling the flag never removes tools, it only withholds schemas from the initial listing.
session_not_running; output reading still works via shell_output, and a session's port forwards are closed automatically when it goes DEAD.Released under the MIT License. You are free to use, modify, and distribute it, provided the copyright notice and permission notice are retained. Thanks to the linux.do community for the discussions and support.