Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
作者による説明(英語の原文)
The safety gate between AI agents and your servers: typed, policy-checked, audited ops tools.
この3つを1つのスコアにまとめることはしません。意見、測定、人気はそれぞれ別のものだからです。
このサーバーのやさしい解説はまだ書いていません。以下はレジストリから読み取った事実です。
使っているAIアプリを選んで、設定をコピーしてください。
必要なもの
どのアプリを使っていますか?
上でアプリを選んでください。選んだアプリを覚えておくので、次からはボタンに「…にインストール」と表示されます。
Claude Desktop → Settings → Developer → Edit Config を開き、claude_desktop_config.json にこれを貼り付けます
{
"mcpServers": {
"opsgate": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/polymatx/opsgate:0.1.3"
]
}
}
}ターミナルでこれを実行します
claude mcp add opsgate -- docker run -i --rm ghcr.io/polymatx/opsgate:0.1.3プロジェクトの .cursor/mcp.json、または ~/.cursor/mcp.json に貼り付けます
{
"mcpServers": {
"opsgate": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/polymatx/opsgate:0.1.3"
]
}
}
}ブラウザがアプリを開いてよいか確認し、そのあとアプリが設定を表示して確認を求めます。キーの値は空欄のままなので、自分で入力してください。
プロジェクトの .vscode/mcp.json に貼り付けます
{
"servers": {
"opsgate": {
"type": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/polymatx/opsgate:0.1.3"
]
}
}
}ブラウザがアプリを開いてよいか確認し、そのあとアプリが設定を表示して確認を求めます。キーの値は空欄のままなので、自分で入力してください。
これはパソコンで動くサーバーですが、ChatGPTがつなげるのはインターネット上のサーバーだけです。ほかのアプリを選んでください。
選んだアプリは、この端末のCookieに保存されます。アカウントは不要です。
この設定は、作者のレジストリ登録情報から作成しています。READMEと違う場合は、READMEに従ってください。
パソコンで動くサーバーです。インストールすると、ツール一覧がAIアプリに表示されます。
誰でも見られる情報をチェックしており、コードは読んでいません。当てはまらない項目は数に入れていません。 · 2026年11月6日にチェック
レジストリ名は、リポジトリを所有するGitHubアカウント polymatx で確認済みです
このリポジトリはまだGitHubから読み取っていません
このリポジトリはまだGitHubから読み取っていません
これはパソコンで動くため、インターネット上のエンドポイントはありません
パソコンで動き、代わりにローカルの設定を使います
パソコンで動くサーバーです。ツール一覧を読み取れるのは、インターネット上のエンドポイントがある場合だけです
レジストリでこのカテゴリにある有効なすべてのサーバーと比較しています。スターは、公開者がリポジトリの所有者である場合だけ数えます。
当てはまる基本チェック1項目のうち1項目に合格しました。コードは読んでいません。キーやトークンを追加する前に、サーバーがどんなアクセスを求めているかを確認し、必要な権限だけを持つキーを使ってください。
レジストリには必須のキーは載っていません。ただし、最初に使うときにサインインを求めるサーバーもあります。
あなたのパソコンで動きます。必要なときにAIアプリが起動し、データは作者のサーバーを通りません(サーバー自体がオンラインサービスを呼び出す場合を除きます)。
GitHubリポジトリの最終更新は–、最新バージョン(0.1.3)がレジストリに届いたのは18 日前です。
Claude Desktop、Claude Code、Cursor、VS CodeなどのMCP対応アプリで使えます。パソコンにDockerも必要です。
同じカテゴリには:Spec Driven Development, Remote Desktop Commander, LickityClick
同じカテゴリで、ほかの作者が作ったサーバー。スターの多い順。
カードの見方:★ GitHubスターi3つの柱:レビュー · チェック · 利用状況i更新=最新バージョンをレジストリに公開した日i
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Create, edit and track LickityClick short links, campaigns and click stats from your AI assistant.
作者のリポジトリから書かれたとおりにコピーしたものです。PopNx MCPは内容を確認・推奨していません。HTML、画像、スクリプトは取り除いています。リンクはこのサイトの外へ移動します。
The safety gate between AI agents and your servers.
An MCP server that lets Claude, Cursor, or any AI agent operate real infrastructure — without handing it a root shell.
What it looks like · 30-second try · Break it yourself · Tools · Security model · Contributing
You ask a question. The agent investigates with read-only tools and answers.
You: Something's listening on port 8000 on my server and I don't remember what. Check it.
▸ listening_ports(host: "hetzner")
Netid State Recv-Q Send-Q Local Address:Port Process tcp LISTEN 0 511 0.0.0.0:443 users:(("nginx",pid=249619),…) tcp LISTEN 0 511 0.0.0.0:80 users:(("nginx",pid=249619),…) tcp LISTEN 0 4096 0.0.0.0:22 users:(("sshd",pid=237597),…) tcp LISTEN 0 151 127.0.0.1:3306 users:(("mysqld",pid=237695),…) tcp LISTEN 0 5 127.0.0.1:8000 users:(("python3",pid=237608),…)
▸ service_list(host: "hetzner", state: "running")
docker.service loaded active running Docker Application Container Engine mysql.service loaded active running MySQL Community Server nginx.service loaded active running A high performance web server… php8.3-fpm.service loaded active running The PHP 8.3 FastCGI Process Manager pm2-root.service loaded active running PM2 process manager
Claude: Port 8000 is a bare
python3process (pid 237608) bound to localhost only, so
it isn't reachable from outside. It's not managed by systemd — no unit owns it — which
means it won't survive a reboot. It's most likely something started by hand in a shell.
Now something that changes state. In operate mode the agent cannot just do it:
You: OK, nginx config changed. Reload it.
▸ nginx_test(host: "hetzner")
nginx: configuration file /etc/nginx/nginx.conf test is successful
▸ nginx_reload(host: "hetzner")
┌─ opsgate: approval required ─────────────────────────────────┐ │ allow nginx_reload on host "hetzner"? │ │ │ │ The nginx configuration passed validation. │ │ This will reload nginx. │ │ │ │ Command: 'nginx' '-s' 'reload' │ │ [ Approve ] [ Deny ] │ └─────────────────────────────────────────────────────────────┘
You click Deny, and nothing happens — the agent is told the operator refused. Approve, and it runs and lands in the audit log. Either way you saw the exact command first.
*(Tool output above is real output from a live server; the prompt is rendered by your MCP client, so it looks however that client draws it.)*
opsgate runs with no config file at all — local machine, read-only:
go install github.com/polymatx/opsgate/cmd/opsgate@latest
opsgate serve
opsgate v0.1.3 | mode=observe | default_host=local | audit=~/.opsgate/audit.jsonl
That's it. No config, no SSH, and it defaults to observe — so it can read your disk usage and journal, and it will refuse to change anything:
▸ disk_usage() → works
▸ service_restart(...) → Refused by opsgate policy: host is in observe mode
Point your client at it and ask "what's using disk space on this machine?" When you're ready for real hosts, opsgate init writes a starter config to ~/.opsgate/config.yaml.
opsgate runs on your machine (or on the server itself) and holds your SSH key, so the transport you need depends on where your client actually executes:
| Client | Transport | Works because |
|---|---|---|
| Claude Code (terminal) | stdio | Runs natively on your machine and launches the binary |
| Claude Desktop, Cursor, VS Code | stdio | Same — a local process |
| Cloud or sandboxed surfaces (web chat, VM-backed agents) | HTTP | They cannot launch a local process, or even see your SSH key — they can only reach a URL |
If your client reports it has no access to your servers, it is almost certainly one of the sandboxed kind. Serve over HTTP instead of stdio.
Before publishing that endpoint, read the warning there: it puts an infrastructure-management API on whatever network you expose it to.
Claude Code
As a plugin: the MCP server, a server-ops skill that teaches the agent to diagnose before it acts and to treat refusals as final, and a read-only /opsgate:health-check [host] command.
/plugin marketplace add polymatx/opsgate
/plugin install opsgate@opsgate
The plugin runs opsgate serve, which reads ~/.opsgate/config.yaml (opsgate init writes a starter one). It never reads a config from the project you have open. Or add just the MCP server:
claude mcp add opsgate -- opsgate serve --config ~/.opsgate/config.yaml
Claude Desktop / Cursor ( claude_desktop_config.json or .cursor/mcp.json )
{
"mcpServers": { "opsgate": { "command": "opsgate", "args": ["serve", "--config", "/absolute/path/to/opsgate.yaml"] } } }
</details>
Docker ( ghcr.io/polymatx/opsgate )
For hosts reached over SSH. Inside a container, local is the container itself.
docker run -i --rm \
-v ~/.opsgate:/root/.opsgate \ -v ~/.ssh:/root/.ssh:ro \ ghcr.io/polymatx/opsgate serve
The image is also how opsgate is listed in the MCP Registry as io.github.polymatx/opsgate.
Any client, over HTTP (needed for cloud/sandboxed clients)
http:
addr: "127.0.0.1:8080" auth_token: "a-long-random-token" # sent as: Authorization: Bearer
opsgate serve --http 127.0.0.1:8080
The tidiest deployment is to run opsgate on the server it manages — then it needs no SSH key at all (default_host: local) — behind a TLS-terminating reverse proxy:
client ──HTTPS──▶ nginx / Caddy ──▶ 127.0.0.1:8080 opsgate
opsgate refuses to serve HTTP on a non-loopback address unless http.auth_token is set.
[!WARNING]
Exposing this endpoint puts an infrastructure-management API on that network, and the
bearer token becomes the only thing protecting it. If you must publish it: keep
mode: observe, generate 32+ bytes of randomness for the token, require TLS, and add an
IP allowlist at the proxy. Binding it to a VPN interface (WireGuard, Tailscale) instead of
the public internet is strictly safer — your own devices reach it, nobody else can.
Note also that
operatemode expects a human who can answer an approval prompt. An
unattended HTTP deployment usually means
observe.
Letting an AI agent run your servers today means giving it exec(" ") over SSH. The usual mitigation is a denylist of commands that *look* dangerous.
That does not work. Denylists are guesswork against an adversary with infinite phrasings:
rm -rf / → blocked
r''m -rf / → not blocked echo cm0gLXJmIC8K | base64 -d | sh → not blocked
And "dangerous-looking" isn't even the hard part. systemctl restart nginx is a perfectly ordinary command that is catastrophic at 3pm on Black Friday.
opsgate does not filter strings. It never gives the agent a string to fill in.
The agent gets 25 typed tools — service_restart(name), docker_logs(container), journal_tail(unit, since, priority) — and opsgate builds the command itself as an argv array. There is no shell to inject into, because no shell string is ever assembled from agent input.
// What opsgate does — the name is one argv element, whatever it contains
argv := []string{"systemctl", "restart", in.Name}
// What it never does argv := []string{"sh", "-c", "systemctl restart " + in.Name}
Send nginx; rm -rf / as the service name and systemctl receives it as a single literal argument and fails as an unknown unit. Nothing else runs.
Every call then passes through four gates:
flowchart LR
A[Agent calls a tool] --> B{Policy mode + allowlist} B -->|deny| X[Refused
hash-chained]
| Layer | What it does |
|---|---|
| Structured tools | Commands are built as argv, never string-concatenated. Injection is impossible by construction, not by filtering. |
| Deny-by-default policy | Three modes — observe (read-only), operate (writes need approval), full. Per-host overrides, per-tool target allowlists. |
| Human approval gates | Mutating calls pause and ask you, showing the exact command. Needs a client that supports MCP elicitation; if it cannot ask, the call is refused rather than run. |
| Tamper-evident audit | Every call — including every refusal — appended to a hash-chained JSONL log. Editing or deleting any line is detectable. |
The default mode is observe. Out of the box, an agent can diagnose your servers and change nothing.
Don't take the claims on faith. Start opsgate in observe mode with files.allow_paths: [/var/log] and try these — every one is refused, and the refusal is logged:
| Try this | What happens |
|---|---|
service_status(name: "nginx; touch /tmp/pwned") | Refused — invalid characters in a target name |
file_read(path: "/var/log/../../etc/shadow") | Refused — outside the allowed paths |
file_read(path: "/root/.ssh/id_rsa") | Refused — outside the allowed paths |
file_grep(pattern: "x$(touch /tmp/pwned)") | Searched as a *literal string*; nothing executes |
service_restart(name: "nginx") | Refused — observe mode cannot mutate |
journal_tail() with allow_targets set | Refused — omitting the unit would read the whole journal |
shell_exec(command: "id") | Refused — the tool isn't even registered unless you opt in |
Then confirm nothing landed:
$ ls /tmp/pwned
ls: cannot access '/tmp/pwned': No such file or directory
$ opsgate audit verify audit chain OK: 6 records verified in ~/.opsgate/audit.jsonl
(Six, not seven: shell_exec isn't a registered tool at all when it's disabled, so that call is rejected by the protocol before it reaches a gate.)
Found something that *does* get through? That's a security bug — please [report it](SECURITY.md
冒頭だけを表示しています。続きはリポジトリで読めます