A remote Model Context Protocol server that exposes a
single tool, run_command, allowing Claude (via the Claude Desktop Connectors feature)
to execute shell commands on the machine running this server.
The server implements OAuth 2.1 (Dynamic Client Registration + PKCE) because Claude Desktop connectors require an OAuth handshake — a connector with no auth server is rejected at sign-in. This server auto-approves authorization (no user prompt), which is fine for a personal, tunnel-exposed server.
⚠️ Security warning: Anyone who can reach this server and complete the (automatic) OAuth flow can run arbitrary commands as the user running it. Only expose it through a private tunnel (cloudflared / ngrok) and never on a public network.
- Transport: Streamable HTTP (the transport Claude Desktop connectors require).
- Stateless: every request spins up a fresh server/transport instance (no session store needed).
- Endpoint:
POST /mcp - Health check:
GET /health
Execute a shell command. On Windows the default shell is cmd.exe (use dir, cd, not ls/pwd) unless you pass shell: "powershell".
| Parameter | Type | Required | Description |
|---|---|---|---|
command |
string | yes | The shell command to run. |
cwd |
string | no | Working directory. |
timeout_ms |
number | no | Hard timeout (max 600000). Default 120000. |
shell |
string | no | "cmd" (default) or "powershell". |
Returns OS, architecture, default shell (cmd.exe on Windows), and current working directory — call once so the client knows which shell to use.
Read a text file (optional offset/limit line range). path is absolute or relative to cwd.
List a directory. recursive: true walks the tree (depth-limited to 4).
Exact string replace — the preferred way to edit code (no shell escaping needed).
path, old_text, new_text, optional replace_all. old_text must be unique unless replace_all is set. Returns a short diff.
Apply a unified diff via git apply (with a --3way fallback). cwd = repo root, patch = diff text.
Write full content to a file (overwrite, or append).
Read several files in one call. files: array of { path, offset?, limit? }.
Apply many exact-text edits across one or more files in a single call. Transactional: every old_text is validated before any file is written, so a missing/ambiguous match aborts the whole batch (nothing changes). Each edit: { path, old_text, new_text, replace_all? }.
git_status (-sb), git_diff (staged + paths options), git_log (max_count, revision), git_show (revision). Each takes an optional cwd.
All file paths are resolved on the host running the server — they point at this machine, not Claude's sandbox.
npm install
npm start
# server listens on http://localhost:3000/mcpOptional env vars: PORT, CMD_TIMEOUT_MS, CMD_MAX_BUFFER.
npm start runs start.js, which frees port 3000 (kills any process holding it)
before launching the server, so you never hit EADDRINUSE.
A small local agent keeps a WebSocket open to a Cloudflare Worker, which becomes
your stable public *.workers.dev MCP endpoint. The Worker only relays to your
machine while the agent (authenticated with PROXY_SECRET) is connected.
wrangler login(free Cloudflare account).- Deploy:
wrangler deploy→ note your URL, e.g.https://command-line-mcp.<subdomain>.workers.dev. - Set the secret (same value already in your gitignored
.dev.vars):wrangler secret put PROXY_SECRET(paste thePROXY_SECRETfrom.dev.vars). - Point the agent at the deployed Worker: set
WORKER_URLin.dev.varsto that URL. - On your machine, run both:
npm start # the MCP server on :3000 npm run agent # connects to the Worker with the secret
- In Claude Desktop: Customize → Connectors → Add custom connector, paste
https://command-line-mcp.<subdomain>.workers.dev/mcp.
Local testing without deploying: wrangler dev --port 8787 (Worker on :8787),
then npm run agent — the agent reads WORKER_URL from .dev.vars.
Claude Desktop connectors need an HTTPS URL, so run the server locally and tunnel it:
cloudflared tunnel --url http://localhost:3000 --protocol http2
# or: ngrok http 3000Copy the generated https://… URL and append /mcp, e.g.
https://abc-123.trycloudflare.com/mcp.
- Open Customize → Connectors → Add custom connector.
- Paste the URL (with
/mcp), e.g. the Worker URL from Option A. - Name it Command-Line.
- Save. Claude discovers the OAuth metadata, registers a client, and opens a browser to the (auto-approving) authorize endpoint, then redirects back and is ready. No "OAuth Client ID" needs to be entered manually — DCR handles it.
Use the MCP Inspector:
npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP, URL: http://localhost:3000/mcpOAuth is already implemented (see oauthProvider.js). For anything beyond a personal
tunnel you should:
- Replace the auto-approve
authorize()with a real consent screen, or at least an allow-listed set of redirect URIs / clients. - Persist clients/tokens (currently in-memory — they reset on restart).
- Host behind a stable HTTPS domain (e.g. a VPS, Cloudflare Workers, Fly.io).
- Consider an allow-list of permitted commands to limit blast radius.
MIT