termdesk
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., "@termdeskrun 'df -h' on all production hosts"
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.
TermDesk
A cross-platform SSH + SFTP + VNC + RDP desktop client โ multi-tab terminals, streaming file transfers and remote desktops (VNC over SSH, plus native RDP), in one window.
Status: shipping โ SSH terminal, SFTP, VNC over SSH, native RDP, MCP agent access, SSH tunnel manager, fleet automation, local terminals with a choice of terminal program (login shell / tmux / Zellij / screen / alternate shells), Prompt Book + scheduled Routines, settings, command palette and packaging.
โจ Features
๐ฅ๏ธ Flexible hosts โ create SSH-only, VNC-only, or combined hosts at creation time. SSH and VNC are independently optional. Pure VNC hosts support direct TCP or SSH-tunneled connections (with clear error if you try to tunnel without SSH creds). UI actions, command palette, and test buttons adapt per host kind.
๐ฅ๏ธ Multi-tab SSH terminal โ xterm.js (WebGL) with search, copy/paste, resize; many parallel sessions to the same or different hosts. Early output is buffered until the terminal attaches, so MOTD/banners aren't lost.
๐ SFTP with a streaming transfer queue โ remote file browser, drag & drop (recursive folder upload), chunk-by-chunk streaming with constant memory, cancel/retry, edit-in-place with auto-upload on save.
๐ VNC over SSH โ noVNC rendered in-app, fed through a loopback WebSocket bridge guarded by single-use, 30 s tokens; default transport is an SSH
forwardOuttunnel, so port 5900 is never exposed. Direct mode also supported for VNC-only hosts.๐ฅ๏ธ RDP โ connect to Windows/RDP hosts in-app via an IronRDP WASM client fed through an in-process RDCleanPath proxy (same single-use, 30 s-token, origin-checked, TLS-terminating shape as the VNC bridge), with trust-on-first-use server-certificate pinning.
๐ Encrypted vault โ hosts, groups, snippets and known hosts in SQLite; passwords/passphrases encrypted with OS-keychain-backed
safeStoragethe moment they arrive in the main process.๐ก๏ธ Host-key verification โ SHA256 fingerprint approval dialog on first connect, hard block on a same-type key change, and a distinct loud "possible man-in-the-middle" warning when a known host presents a key/type never trusted for it.
๐ช ProxyJump โ multi-hop chains (
user@jump:port,next) via chainedforwardOut.๐ SSH tunnel manager โ define, persist and start/stop local (
-L) port forwards and dynamic SOCKS5 (-D) proxies from a sidebar panel, with a live status dot + throughput. Reuses an open terminal's connection when possible; destructive-free, owner-scoped, logged. Seedocs/TUNNELS.md.๐ช Split panes โ view two sessions side by side or stacked (Alt-click a tab or the split toolbar), with a draggable divider. Great paired with multi-host automation for a live monitoring grid.
๐ป Local terminals โ
node-ptyshell tabs on your own machine, with saved working directories โ no SSH required. Per-host default remote path is honored on connect.๐งฉ Choose your terminal program โ pick what runs when a terminal opens (Settings โ General): your default login shell, a multiplexer (tmux, Zellij, GNU Screen โ persist across restarts/disconnects) or an alternate shell (bash, zsh, fish, PowerShell, Nushell). Only programs detected on your machine are selectable; the choice applies to both local terminals and SSH sessions (exec'd on the remote when present, else a plain shell).
๐ Open in external terminal โ hand the current directory off to your favorite GUI terminal โ Ghostty, Warp, iTerm2, kitty, Alacritty, WezTerm, GNOME Terminal, Konsole, Windows Terminal โ detected on your machine, with a saved default (Settings โ General), from โK or a one-click button on any local terminal tab.
โก Fleet automation โ run a snippet or command across a whole group of SSH hosts at once, with live per-host streaming output.
๐ Activity log โ a local timeline of connects/disconnects, SFTP/VNC opens and automation runs. Metadata only: secret-bearing command tokens are redacted and entries purge after 90 days.
๐จ Terminal color schemes โ built-in Dracula, Solarized Dark, Gruvbox, One Dark, Nord (plus the default), per your preference.
๐งฑ Customizable sidebar โ show or hide each left-sidebar section (Hosts, Local terminals, Workspaces, Tunnels, Snippets, Prompt Book, Routines) from Settings โ General or the sidebar's Customize button. Your choice persists; everything is visible by default.
๐งฉ Prompt Book โ reusable, templated prompts for AI agents. Prompts are plain text with
{{variable}}placeholders (optionally{{name:default}}/{{name|description}}); running one asks for the values, shows a live preview, and either sends the rendered text to the active terminal (SSH or local) or launches it in an AI agent โ Claude Code, Aider, OpenCode, Codex or Gemini โ in a directory you pick. Templating is pure substitution (no eval) and the prompt is passed as a single quoted argument, so shell metacharacters in it are inert.๐ Routines โ saved "run this prompt through this agent in this directory" jobs, run on demand or on a schedule (interval / daily / cron), each with its own run history. Routines fire while TermDesk is open, and a run missed while the app was closed catches up once on next launch instead of stampeding every missed slot. Unattended autonomy is opt-in and off by default; stored run summaries are secret-redacted.
๐ Snippets โ saved commands sent to the active session.
โจ๏ธ Command palette โ
โ/Ctrl+Kfuzzy host search + every command (terminals, SFTP, VNC, automation, logs); press?for the shortcuts cheat-sheet.โ๏ธ Settings โ theme (dark / light / System, following the OS), terminal font + color scheme, right-click-paste behavior, SSH keepalive; plain JSON, never secrets.
๐ค AI agent access (MCP) โ let Claude, Cursor, Grok or any MCP client use TermDesk: list hosts, run commands, fan out across a fleet. The agent gets hands, never keys โ credentials stay in the main process, every action is per-host opt-in + approval-gated + shown live in an AI Activity log. Off by default. See
docs/MCP-INTEGRATION.md.๐ Local-first โ your hosts, keys and history never leave your machine; no mandatory cloud account, no telemetry.
Related MCP server: SentryFrogg MCP Server
๐ Architecture
All SSH/SFTP/VNC logic lives in the main process. The renderer is fully sandboxed (contextIsolation: true, nodeIntegration: false, sandbox: true) and talks to it only through a typed, Zod-validated IPC contract (src/shared/ipc.ts).
flowchart LR
subgraph R["Renderer (sandboxed, no Node)"]
UI["React 19 UI<br/>zustand stores"]
XT["xterm.js<br/>terminal"]
NV["noVNC<br/>RFB client"]
end
subgraph M["Main process"]
IPC["Typed IPC<br/>Zod-validated contracts"]
SM["SessionManager<br/>(ssh2: shell, agent,<br/>keys, ProxyJump)"]
SF["SftpManager +<br/>TransferManager<br/>(streaming queue)"]
WB["ws-bridge<br/>127.0.0.1, random port,<br/>single-use 30s tokens"]
VA[("Vault<br/>better-sqlite3 + Drizzle<br/>secrets via safeStorage")]
end
RH[("Remote host<br/>sshd ยท sftp ยท vncserver")]
UI <-->|"invoke/handle"| IPC
XT <-->|"ssh:data:#lt;sessionId#gt; stream"| IPC
IPC <--> SM
IPC <--> SF
SM --- VA
SF --- SM
NV -->|"ws://127.0.0.1:#lt;port#gt;/#lt;token#gt;"| WB
WB -->|"variant B (default):<br/>ssh2 forwardOut โ :5900"| SM
WB -.->|"variant A (opt-in):<br/>direct TCP โ :5900"| RH
SM <-->|"SSH"| RHTwo deliberate exceptions to "secrets stay in main": the stored VNC password (for the RFB credentials handshake) and the stored RDP password (for the IronRDP client) are decrypted in main and returned to the renderer. Neither is currently bound to an open tab or rate-limited โ see Known limitations.
๐ Quick start
TermDesk is currently run from source. konraddzbik/termdesk has no tags
and no GitHub Releases, so there is nothing to download from the Releases page
yet โ see INSTALL.md.
You need Node.js >=22.12.0 and npm 10+, plus a C/C++ toolchain (Xcode
Command Line Tools / build-essential / Visual Studio C++ build tools) so the
native modules can compile. On Linux you also need an unlocked OS keyring
(gnome-libsecret or kwallet) โ TermDesk fails closed rather than use Electron's
insecure basic_text fallback, so without one you cannot save a host password
(SECURITY.md).
npm install # also applies the better-sqlite3 patch + rebuilds native deps
npm run dev # electron-vite dev server + Electron window, HMRFirst run:
npm install && npm run devopens a usable app โ there is no licence check, no seat and no account. SeeCONTRIBUTING.mdfor the full development setup, including thebetter-sqlite3ABI step you need beforenpm testwill run.
Script | What it does |
| electron-vite dev server + Electron window |
| preflight: Node, toolchain, better-sqlite3 ABI, Linux keyring |
| typecheck (TS strict) + production build to |
| biome check (format + lint) |
| vitest (unit + integration) |
| vitest with v8 coverage |
| build + electron-builder (dmg / NSIS / AppImage) |
npm run dist produces an unsigned installer for the host OS (dist/TermDesk-<version>-arm64.dmg on Apple Silicon macOS, plus a cross-built x64 .dmg). It does not publish anything. See INSTALL.md for from-source setup, local artifact names, and how a future GitHub Release would be cut.
๐งช Dev test environment
A single Docker container provides SSH + TigerVNC for local testing โ no real servers needed:
npm run test:keys # throwaway SSH keys (untracked)
docker compose -f docker-compose.test.yml up -dService | Endpoint | Credentials |
SSH |
|
|
VNC |
|
|
E2E smoke harnesses
Five self-contained end-to-end suites run inside the real Electron app (the first four against the docker container; the MCP suite needs no external services). Each prints an *_OK marker on success:
Command | Proves | Verified result |
| Secrets are |
|
| Real logins with password, key, and key + encrypted passphrase |
|
| 1 GB upload + download with RSS monitoring (peak 259 MB โ under the 300 MB budget) and a 500-file folder upload |
|
| RFB handshake both direct and over the SSH tunnel; forged-token connections rejected; tokens are single-use |
|
| Token-gated MCP server starts on loopback; a forged bearer token is rejected |
|
โ Testing
npm test # unit + integration suite (vitest) โ all green on main
npm run test:coverage # v8 line coverage report into coverage/Vitest runs renderer tests under jsdom (Testing Library) and everything else under node. Coverage is concentrated where it counts โ pure logic is high, while process-glue and UI shells are covered by the five e2e smokes instead of unit tests (percentages below are indicative, not pinned โ run npm run test:coverage for current numbers):
High (lines):
shared97.1% โ the Zod IPC contract every process depends on โrenderer/lib97.5%,main/store81.4% (db, hosts-repo, settings and snippets-repo at or near 100%),ssh-util100%, the~/.ssh/configparser and itsIncluderesolver in the high 90s.Low, and covered by the smokes instead:
main/ipchandler glue 9.8%,session-managerandsftp-manager/transfer-manager0%,vnc-managerand the UI shells (SftpTab,TerminalView,VncTab, layout and the sidebar panels) low or zero. These are process glue and Electron/DOM shells; theTERMDESK_SMOKEharnesses exercise them end to end against a real server. Entry points and*-smoke.tsfiles are excluded from coverage by config.
Build + typecheck + lint are clean, and a headed dev launch logs zero error lines.
โจ๏ธ Keyboard shortcuts
Shortcut | Action |
| Command palette (fuzzy host search + commands) |
| New session (opens the palette) |
| Close active tab |
| Search in terminal |
| Copy/paste in terminal |
โ๏ธ Settings
Theme (dark, light, or System โ following the OS prefers-color-scheme), terminal font size/family, SSH keepalive interval โ stored as plain JSON in userData/settings.json (never secrets).
๐ก Feature details
SSH terminal
All ssh2 logic lives in the main process (
src/main/ssh/session-manager.ts); the renderer only sees a typed stream API. Output streams onssh:data:<sessionId>; early output is buffered until the terminal attaches.Auth: vault password (with keyboard-interactive fallback), private key (+ encrypted passphrase), or SSH agent (
SSH_AUTH_SOCK, Windows OpenSSH pipe). Secrets are decrypted only at connect time.Host keys: first connect shows a SHA256 fingerprint approval dialog and persists to the
known_hoststable; a mismatch hard-blocks the connection.ProxyJump chains (
user@jump:port,next) via chainedforwardOut; keepalive 15 s.
SFTP
SFTP sessions reuse the live SSH connection of an open terminal to the same host (no second login) and fall back to a dedicated shell-less connection.
Transfers stream chunk-by-chunk (constant memory โ 1 GB verified under 300 MB RSS) and are cancellable/retryable; folder drops upload recursively with structure preserved.
Edit-in-place downloads to a temp file, opens the OS editor and auto-uploads on save.
VNC
noVNC renders in the renderer; it speaks WebSocket to a main-process bridge bound to
127.0.0.1on a random port. Every connection requires a single-use, 30 s token โ other local processes can't ride the bridge (hencews://127.0.0.1:*in the CSP).Default transport is an SSH
forwardOutchannel (variant B โ port 5900 never exposed), reusing a live terminal connection to the host when one exists; plain TCP (variant A) is opt-in per host.The stored VNC and RDP passwords are decrypted in main and returned to the renderer for their respective clients โ the two documented exceptions to "secrets stay in main".
Toolbar: local scaling / remote resize toggles, clipboard paste, Ctrl+Alt+Del, fullscreen, reconnect (each reconnect provisions a fresh token + tunnel).
Vault
Hosts, groups, snippets and known_hosts live in SQLite (better-sqlite3 + Drizzle) under the app's
userDatadirectory.Passwords/passphrases are encrypted with
safeStorage(OS keychain key) in the main process the moment they arrive; only ciphertext blobs touch the database, and secrets are never sent back to the renderer.~/.ssh/configimport parses Host/HostName/Port/User/IdentityFile/ProxyJump, resolvesIncludedirectives (tilde/relative/absolute paths and globs, with cycle/depth/size guards) and mergesHost */wildcard defaults into concrete hosts using OpenSSH first-obtained-wins ordering.
๐ Security checklist
contextIsolation: true,nodeIntegration: false,sandbox: trueโ the renderer and preload never touch Node.Strict CSP via meta tag:
script-src 'self', nounsafe-eval, no remote module;connect-srcallows only'self'and loopback WebSockets (the token-gated VNC bridge). Dev-only relaxation happens through a Vite plugin that fails loudly on drift.webSecurity: true; navigation origin-checked;window.opendenied (externalhttps:โ system browser);<webview>blocked; permission requests denied by default.Every IPC payload is validated with Zod (
src/shared/ipc.tsis the single contract); invoke results are typed end-to-end viaIpcInvokeMap.Secrets (SSH/VNC passwords, key passphrases) are encrypted with
safeStoragein the main process the moment they arrive; only ciphertext is persisted; decryption happens at connect time and references are dropped immediately. Verified byTERMDESK_SMOKE=vault. Two documented exceptions: the VNC and RDP passwords cross to the renderer for their protocol clients (see Known limitations).Errors crossing IPC are sanitized (first line only, no stacks/paths); secrets are never logged.
Sessions, SFTP handles, transfers and host-key prompts are owner-scoped to the WebContents that created them and torn down when it is destroyed.
Host keys: SHA256 fingerprint approval on first connect, hard block on mismatch โ for the final target and every ProxyJump hop.
Activity log is metadata-only: OS username, secret-bearing command tokens redacted, 90-day purge.
Optional master password (Argon2id-derived second encryption layer) โ deferred, tracked as a future enhancement.
See SECURITY.md for the full security model, the known limitations, and the vulnerability-disclosure policy.
๐ Project structure
src/
main/ # Electron main process โ all SSH/SFTP/VNC logic lives here
ipc/ # IPC handlers per domain (ssh, sftp, vnc, hosts, โฆ)
ssh/ # session-manager, ssh-config-parser, ssh-util (+ smoke)
sftp/ # sftp-manager, transfer-manager, edit-watch (+ smoke)
vnc/ # ws-bridge, vnc-manager (+ smoke)
store/ # Drizzle + better-sqlite3, secrets (safeStorage), settings
preload/ # contextBridge โ typed, minimal API surface (window.api)
renderer/ # React UI (no Node access)
components/{layout,hosts,terminal,sftp,vnc,snippets,ui}/
hooks/ stores/ lib/
shared/ # IPC channel contracts + Zod schemas shared by all processes๐บ Known limitations / roadmap
Master password โ optional Argon2id-derived second encryption layer over the vault; deferred.
Playwright smoke test (launch app โ add host โ mock connection) โ deferred; the five
TERMDESK_SMOKEharnesses cover e2e today.~/.ssh/configimport โMatchblocks and token expansion (%h/%p) are still skipped; cloud-provider inventory import (AWS/GCP) is not implemented.
See SECURITY.md for the hardening roadmap.
๐ง Native-module notes
better-sqlite3 needs a source patch to compile against Electron's V8 (no upstream prebuilds yet) โ applied automatically via
patch-packageonnpm install(patches/better-sqlite3+12.10.0.patch).cpu-features (optional ssh2 native dep) is overridden to
noop2inpackage.jsonto avoid an unnecessary native build; ssh2 falls back to its pure-JS path.
๐ค Contributing
Issues and pull requests are welcome. The delivery plan for outsider install,
packages, and contributor setup is docs/OSS-DELIVERY-PLAN.md.
CONTRIBUTING.md has the development setup
โ including the two native-module traps that will otherwise cost you an afternoon โ the invariants new
code has to keep, and the pre-PR checklist. Contributions are inbound=outbound MIT: no CLA, no
copyright assignment.
Participation is governed by the Code of Conduct. Security problems go through
the private channel in SECURITY.md, never a public issue.
๐ License
The source in this repository is released under the MIT License โ see
LICENSE. You may use, modify, and redistribute it, including
commercially, as long as the copyright notice and licence text travel with it.
When prebuilt installers exist on the Releases page they are additionally
covered by EULA.txt, which governs those binaries. There are
currently no GitHub Releases, so a build you make yourself from this source is
governed by LICENSE alone.
There is no licence check, seat activation or account in this client: the commercial licensing subsystem was removed when the source was published, so what you build from this repository is the whole application.
That boundary is enforced in code, not just described here: the first-run EULA
prompt is compiled into the project's own release builds only, so a fork's
npm run dist, a distro package or your own installer never shows it.
Third-party dependencies keep their own licences, listed in
THIRD-PARTY-NOTICES.md. One carries an obligation
worth naming: @novnc/novnc is MPL-2.0 and this repository patches it
(patches/@novnc+novnc+1.7.0.patch), so that modification is published here
under MPL-2.0 along with the rest of the source.
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 Connectors
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Scoped, audited SSH exec, sessions, and SFTP on your saved servers without exposing credentials
An authenticated remote MCP server for user-owned devices and one-shot capability invocation.
Authenticated MCP server for ClearPolicy policy and compliance workflows.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables SSH interactive session management through MCP, supporting commands, menus, and session lifecycle operations.1
- AlicenseAqualityDmaintenanceProvides a governed command plane for MCP agents to manage PostgreSQL databases, SSH sessions, and HTTP APIs with encrypted credential storage and prescriptive workflows.3MIT
- AlicenseNot gradedqualityBmaintenanceMCP server that provides SSH tools (read-only probes and arbitrary exec) to a fleet of hosts outside Kubernetes, with an inventory-based allowlist and key-based authentication.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI clients to securely execute remote commands and transfer files over SSH through standardized MCP tools, with multi-host management, flexible authentication, and an admin console.ISC
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/konraddzbik/termdesk'
If you have feedback or need assistance with the MCP directory API, please join our Discord server