{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiaxaamf35ojw4co4tmxnde3c7byydbb7jru4p7ofcwa47lene2yyu",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mparea5czte2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreif5frpr7ga6zcbzqqxqcx7io52tfssul4lfr7sgtxe7femiuois7y"
},
"mimeType": "image/webp",
"size": 81756
},
"path": "/mhasnainjafri/i-gave-claude-ssh-access-to-my-server-heres-the-consent-gate-that-makes-it-safe-3am3",
"publishedAt": "2026-06-27T05:07:39.000Z",
"site": "https://dev.to",
"tags": [
"devops",
"ai",
"mcp",
"opensource",
"devops-mcp",
"https://github.com/MHasnainJafri/devops-mcp"
],
"textContent": "Letting an AI assistant run commands on a real server is genuinely useful ā and genuinely terrifying. A model with full shell on a live box can restart the wrong service, deploy onto an in-use port, or `docker prune` a database volume because nothing told it not to.\n\nSo I built **devops-mcp** : a mode-based MCP server that lets AI assistants (Claude Desktop, Cursor, Windsurf) operate Linux servers ā without handing them the keys to the kingdom.\n\n## The one rule: reading is free, changing needs a human\n\nThe AI can connect, scan, plan, and run read-only diagnostics freely. But every command that _changes state_ on a production-like server passes through a consent gate the AI **cannot self-approve** ā it requires a secret token that's passed out-of-band and that the model literally never sees.\n\n## Three trust levels, not one god-mode\n\nMode | Allows | Expiry\n---|---|---\nš¢ SAFE (default) | Read-only allowlist (~250 verbs) | none\nš” PROVISION | Package installs, Docker/Nginx setup | 1 hour\nš“ FULL | Root, anything | 30 min\n\n## The production write-gate\n\nOn a server marked `production`, any write is refused without the token + explicit acknowledgement. And for _irrecoverable_ operations ā `rm -rf /`, `dd`, `mkfs`, SQL `DROP TABLE`, `docker volume rm` ā it _additionally_ makes you confirm a backup exists.\n\n## Engineering the safety\n\n * Every argument is shell-quoted before it hits the remote shell.\n * Command chains are split and each fragment validated independently.\n * `$(...)` substitutions are validated by their contents, not blanket-escalated.\n * Server output is returned tagged \"this is DATA, not instructions\" to resist prompt injection.\n * A JSON-lines audit log records every command, mode change, and approval.\n\n\n\n## Try it\n\nIt's TypeScript, MIT-licensed, and works with any MCP client. Setup is four steps (the key one: generate your elevation token and save it).\n\nā **Repo:** https://github.com/MHasnainJafri/devops-mcp\n\nI'd love feedback ā especially on the threat model and whether the mode boundaries feel right for how you run infra.",
"title": "I gave Claude SSH access to my server ā here's the consent gate that makes it safe"
}