SelfHostDesktopMCP
Click on "Deploy 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., "@SelfHostDesktopMCPrun the tests on my-laptop and show me the failures"
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.
SelfHostDesktopMCP
A self-hosted remote MCP gateway: let MCP clients (ChatGPT connectors, Claude, Hermes, …) use tools on your own machines without handing a third party the relay, the accounts or the logs.
MCP client ──HTTPS /mcp──▶ gateway ◀──WSS /device── agent ──stdio──▶ local MCP server (e.g. Desktop Commander)Devices only make outbound connections. No inbound ports on your laptops, and the local MCP server is never exposed.
One gateway, many devices (Linux, macOS, …). The gateway is not coupled to Desktop Commander; any stdio MCP server can be the backend.
Node ≥ 22.13, two runtime dependencies (
ws,@modelcontextprotocol/sdk), SQLite via the built-innode:sqlite.
⚠️ A device agent can do whatever its OS user can do. Authentication is not a sandbox. Read docs/SECURITY.md before exposing anything.
Features
MCP Streamable HTTP at
/mcp(JSON responses), tool calls relayed to the chosen device; per-device tool lists.Auth: static bearer tokens for clients; OAuth 2.1 (dynamic client registration, PKCE S256, refresh rotation, RFC 8414/9728 metadata) for connectors such as ChatGPT. Consent needs an admin ("owner") token.
Web admin console at
/admin/(mobile-friendly): devices, clients/OAuth grants, activity log, pairing approval. Sign-in is TOTP only (any authenticator app), enrolled by scanning a QR in your own browser.MCP servers per device, managed in the console: pick a template (Chrome DevTools, Playwright) or, if the device opted in, a custom command; per-tool on/off switches; tools appear as
prefix_toolnamenext to the built-in Desktop Commander tools.Per-device secrets, stored only as SHA-256 hashes; instant revocation (live connection is dropped).
Pairing:
/pair/*does not exist until you open a 3-minute window from the CLI; approve the request in the terminal.Routing that works with stateless clients: pass
device_idon any call;device_selectsets a per-client default.Device-side policy (guardrail): blocked paths (symlink-aware), read-only paths, blocked commands/patterns, disabled tools.
Audit log (SQLite): client, device, tool, request id, duration, status — never arguments or output.
Keepalive (20 s ping / 60 s timeout), 30 s call timeout, explicit
DEVICE_OFFLINE, auto-reconnect with backoff.
Related MCP server: ChatGPT Gateway MCP Nyan
Quick start (one machine, localhost)
npm install
node gateway/admin.js client-create me # prints a client token once
node gateway/admin.js device-create my-box # prints a device secret once
node agent/agent.js init --gateway ws://127.0.0.1:8765/device --device-id my-box --secret <device-secret>
node gateway/server.js # 127.0.0.1:8765
node agent/agent.js # spawns Desktop Commander over stdio (use --mock to try without it)
RMCP_URL=http://127.0.0.1:8765 RMCP_TOKEN=<client-token> node test/smoke.js my-box
npm testGateway flags / env: --host, --port, --db, --public-url (issuer for OAuth; required behind a proxy), --call-timeout, --offline-timeout
(or RMCP_HOST, RMCP_PORT, RMCP_DB, RMCP_PUBLIC_URL, …).
Going public
Put a TLS reverse proxy in front (443 only), forward
/mcp,/device,/health,/.well-known/*,/authorize,/token,/register,/pair/*to the gateway. WebSocket support must be on, no proxy-level basic auth. See docs/reverse-proxy.md.Linux host:
PUBLIC_URL=https://mcp.example.com bash deploy/install-linux.sh(systemd user units, fresh secrets in./secrets).Test from a network outside your LAN/VPN:
curl https://mcp.example.com/health, thentest/smoke.js.If your ISP uses CGNAT you cannot port-forward; run the gateway on a VPS instead (devices connect outbound to it).
Connect clients
ChatGPT / OAuth clients: add
https://mcp.example.com/mcpas a custom MCP server with OAuth. Approve on the consent page with the owner token (secrets/owner-token).Static token clients:
Authorization: Bearer <token>fromadmin.js client-create <name>.Always pass
device_idin tool arguments (some clients open a new MCP session per call).
Add a device (pairing)
# on the gateway host: opens a 3-minute window, shows requests, asks for approval
node gateway/admin.js pair
# on the new machine (checkout + npm install):
node agent/agent.js pair https://mcp.example.com --device-id my-laptop
# compare the code shown on both sides, answer y on the gateway
node agent/agent.js # or: bash deploy/install-macos.sh (LaunchAgent) / deploy/*.service.in (systemd)Web console and two-step login
Open
https://mcp.example.com/admin/setup(only exists until two-step login is enabled), enter the owner token (secrets/owner-token).Scan the QR with an authenticator app, type the 6-digit code to confirm. The secret is shown once and never leaves your browser/phone.
Sign in at
/admin/with a code from the app. Lost the phone:node gateway/admin.js totp-reseton the gateway host, then enrol again.
Sign-in locks for 15 minutes after 5 wrong codes, a code can be used once, sessions expire after 30 min idle / 12 h, writes need a CSRF token.
Tool exposure (for clients that never refresh their tool list)
Some MCP clients (e.g. Hermes) call tools/list once and never again, so the list must not depend on runtime state. By default (per-client fixed mode):
tools/list=devices_list,device_select,tools_list,tool_call+ the built-in Desktop Commander tools (a snapshot stored on the gateway, so it is identical whether or not a device is online or selected).Tools of servers added in the console (
chrome_*, custom) are not listed. The model discovers them withtools_listand runs them withtool_call {name, arguments, device_id}; they work the moment they are added, with no reconnect. Audit records the inner tool name.Clients that do refresh can be switched to live mode in the console (Clients tab), where
tools/listshows everything the selected device exposes right now.?tools=all|stableon the MCP URL overrides the setting per connection.
Adding MCP servers to a device
Console → Devices → MCP servers → Add an MCP server. The gateway stores the list and pushes it to the agent whenever it connects or you change it.
Templates (
agent/backends.js) are chosen by the agent: the console sends only{template, params}, so it cannot inject flags like--executablePath. Risky tools (evaluate_script,upload_file, …) start disabled; tick them on per tool.Custom command runs any command as the device's user and is off by default. Enable it on a device you control by adding
{"allow_custom_backends": true}to that device's~/.config/remote-mcp/policy.json.The device policy (blocked paths,
file:URLs, …) applies to every added server's tools too. Packages are fetched withnpx -y <pkg>@latest; pin a version via a custom command if you need to.A failing server shows its error in the console and does not affect the others.
Revoke
node gateway/admin.js client-revoke <client_id> # static client token
node gateway/admin.js device-revoke <device_id> # device; a live agent is dropped and exits
node gateway/admin.js listOAuth grants live in oauth_tokens; revoke by setting revoked_at (see docs/SECURITY.md). If the owner token leaks: revoke it and create a new one.
Update
bash deploy/update.sh on any deployed host: git pull --ff-only, npm install, restarts the systemd units / LaunchAgent that are installed there.
Layout
gateway/ server.js (HTTP/MCP/WS) oauth.js pairing.js web.js + ui/ (admin console) totp.js db.js admin.js (offline CLI)
agent/ agent.js (device side) policy.js (guardrails) backends.js (MCP server templates)
deploy/ install-linux.sh install-macos.sh update.sh *.service.in
test/ e2e, OAuth, pairing and policy tests (node --test)Policy
~/.config/remote-mcp/policy.json extends the defaults in agent/policy.js:
{ "blocked_paths": ["~/Library/Keychains"], "readonly_paths": ["~/src/this-repo"], "allowed_paths": [], "blocked_commands": ["curl"] }Set "replace_defaults": true to replace instead of extend. It is a guardrail, not a security boundary.
Limits / not done
Single gateway instance (no HA), SQLite only, no per-operation human approval, OAuth is single-owner, Windows has no service installer.
License
MIT — see LICENSE. Provided as is; running an agent that executes commands on your machine is your responsibility (see docs/SECURITY.md).
This server cannot be deployed
Maintenance
Related MCP Connectors
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
- gatewayOAuthai.sealgate
MCP gateway with runtime security policy, tool-call-level control, and audit of agent actions.
An authenticated remote MCP server for user-owned devices and one-shot capability invocation.
Remote MCP for A2A caller identity, scope policy, verdict receipts, and audit history.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables cloud agents to securely operate local machine resources (files, commands, screenshots) via standard MCP protocol.MIT
- AlicenseNot gradedqualityBmaintenanceEnables agent clients to safely connect to tools and execution resources through MCP with authorization, approvals, audit, chat-context isolation, SSH/Docker access, and long-running command session tracking.MIT
- FlicenseAqualityBmaintenanceEnables MCP clients to manage a self-hosted RPort fleet by listing and inspecting connected devices, opening and closing SSH tunnels, and executing commands through RPort agents.10-
- AlicenseNot gradedqualityBmaintenanceEnables AI clients to securely reach and control tools running on your own computers through a relay and outbound device agent.MIT