Docker MCP Gateway: jalankan MCP server di container terisolasi
Coba lihat config MCP milik orang yang pakai Claude Desktop, Cursor, dan VS Code barengan. Empat server yang sama ditulis tiga kali. Semuanya dijalankan lewat npx, semuanya baca API key dari file JSON tanpa enkripsi, dan semuanya jalan sebagai child process dengan akses filesystem yang sama persis dengan editor yang melahirkannya. Mau ganti versi server? Edit tiga file. Mau rotate key? Ulang edit yang sama.
Docker MCP Gateway memindahkan semua itu ke dalam Docker. Server jadi OCI image, tiap server jalan di container sendiri dengan batas CPU dan memory, secret diambil dari keychain OS, dan semua client di mesin kamu ngobrol dengan satu proses gateway, bukan N server.
Yang perlu disiapkan
- Docker Desktop 4.62 atau lebih baru untuk perintah CLI
docker mcp, atau Docker Engine dengan satu langkah tambahan (Step 1 di bawah) - Docker engine yang jalan
- Client MCP: Claude Desktop, VS Code, Cursor, atau apa pun yang bisa stdio atau Streamable HTTP
- Nggak butuh Node.js. Justru itu salah satu alasannya.
Step 1: siapkan CLI plugin
Cek dulu yang ada di mesin kamu:
docker mcp version
Kalau jawabannya "docker: 'mcp' is not a docker command", berarti Docker Desktop kamu di bawah 4.62 atau kamu pakai Docker Engine polos. Di Engine, gateway dikirim sebagai binary terpisah yang dibaca Docker sebagai CLI plugin:
# download binary rilis terbaru dari github.com/docker/mcp-gateway/releases
chmod +x docker-mcp
mkdir -p ~/.docker/cli-plugins
mv docker-mcp ~/.docker/cli-plugins/docker-mcp
docker mcp version
Path-nya sama untuk Linux dan macOS. Di Windows plugin-nya ada di %USERPROFILE%\.docker\cli-plugins.
Step 2: lihat isi katalog
Gateway mengambil definisi server dari katalog OCI. Cek isi katalog resminya:
docker mcp catalog server ls mcp/docker-mcp-catalog
Tiap baris keluaran kasih nama server seperti playwright atau github-official. Nama itu yang dipakai sebagai server ID di langkah berikutnya. Di Docker Desktop, nilai yang sama ada di MCP Toolkit > Catalog > Server ID.
Step 3: bikin profile dan tambahkan server
Profile adalah kumpulan server beserta config-nya. Inilah yang dimuat gateway saat start, dan ini juga yang bikin "tools buat kerjaan web-dev" jadi sesuatu yang bisa dinyalakan sekali perintah.
docker mcp profile create --name web-dev
docker mcp profile server add web-dev \
--server catalog://mcp/docker-mcp-catalog/github-official \
--server catalog://mcp/docker-mcp-catalog/playwright
Referensi server bisa nunjuk ke empat jenis sumber:
| Bentuk URI | Sumber |
|---|---|
catalog://<catalog>/<server-id> |
Katalog OCI, kasus yang paling umum |
docker://<image>:<tag> |
Image Docker apa pun |
https://<registry>/v0/servers/<id> |
Registry komunitas MCP |
file://<path> |
Definisi lokal dalam YAML atau JSON |
Bentuk file itulah cara membungkus server yang belum dipublikasikan siapa pun. Minimal butuh nama, image, dan deskripsi:
name: my-server
title: My Server
type: server
image: myimage:latest
description: Description of the server
docker mcp profile server add web-dev --server file://./my-server.yaml
docker mcp profile show web-dev
Step 4: pasang config dan secret
Server yang butuh token atau endpoint diatur lewat config profile:
docker mcp profile config web-dev --set github-official.personal_access_token=<token>
docker mcp profile config web-dev --get-all
Perhatikan di mana nilai itu mendarat. Untuk apa pun yang sensitif, pakai secret store, yang menyimpan nilainya di keychain OS dan bukan di file profile:
docker mcp secret --help
Server yang butuh OAuth (contohnya server resmi GitHub) diotorisasi di Docker Desktop setelah ditambahkan, di bagian MCP Toolkit. Langkah consent ini nggak ada promptnya di terminal.
Step 5: jalankan gateway
Untuk satu client lokal, stdio sudah cukup. Client yang menjalankan gateway, lalu gateway yang menyalakan container server saat dibutuhkan:
docker mcp gateway run --profile web-dev
Dari situ pertanyaan soal lifecycle server terjawab: nggak ada yang start sampai ada tool call masuk. Gateway mengenali server mana yang punya tool itu, menyalakan container-nya, menyuntikkan credential, lalu meneruskan request. Lihat prosesnya dari terminal kedua:
docker ps
Kalau ada beberapa client sekaligus, atau kamu butuh satu endpoint yang hidup lama di mesin headless, pindah ke transport jaringan:
docker mcp gateway run --profile web-dev --port 8080 --transport streaming
--transport menerima stdio, sse, atau streaming. Tanpa --port, gateway tetap di stdio.
Flag yang berguna begitu basic-nya jalan:
--servers server1,server2dengan--tools server1:* --tools server2:tool2membatasi daftar tool ke allowlist tertentu. Tool lebih sedikit berarti deskripsi lebih sedikit di context model.--secrets=docker-desktop:./.envmenambah sumber secret cadangan setelah keychain.--verbose --log-callsmencetak trace pemanggilan, ini yang kamu butuh saat tool call balik dengan hasil nggak berguna.--dry-runmenyalakan gateway tanpa mendengarkan koneksi, jadi kamu bisa validasi profile sebelum disambungkan ke client.--watchmemuat ulang config saat file berubah, praktis waktu sedang iterasi profile.
Dua default keamanan sudah aktif: --block-secrets mencegah secret terkirim ke tool, dan --verify-signatures memeriksa tanda tangan image. Batas container default 1 CPU dan 2 GB per server, keduanya bisa diubah lewat --cpus dan --memory.
Step 6: arahkan client ke gateway
Claude Desktop baca claude_desktop_config.json. Satu entri menggantikan semua server yang tadinya ada di situ:
{
"mcpServers": {
"MCP_DOCKER": {
"command": "docker",
"args": ["mcp", "gateway", "run", "--profile", "web-dev"]
}
}
}
Untuk client yang sudah dikenal Docker, biar CLI yang menulis file-nya:
docker mcp client connect vscode --profile web-dev
Perintah itu bikin .vscode/mcp.json di direktori saat ini, yang sifatnya spesifik per user:
echo ".vscode/mcp.json" >> .gitignore
Step 7: jalan tanpa Docker Desktop
Di server atau di CI biasanya nggak ada Desktop sama sekali. Gateway juga tersedia sebagai image container:
services:
gateway:
image: docker/mcp-gateway
command:
- --servers=duckduckgo
volumes:
- /var/run/docker.sock:/var/run/docker.sock
docker compose up
Perhatikan apa yang diberikan mount itu. Unix socket setara akses root di host, jadi gateway bisa menyalakan container lain. Jalankan ini di mesin yang kamu kuasai, bukan di shared runner milik orang lain.
Kapan pakai, kapan nggak
| Setup | Isolasi | Titik config | Cocok kalau |
|---|---|---|---|
| Server via npx di config tiap client | Nggak ada, user-nya sama dengan editor | Satu per client | Satu client, dua server, masih coba-coba |
| Docker MCP Gateway | Satu container per server, batas CPU dan memory, secret di keychain | Satu | Beberapa client, beberapa server, ada server yang bukan buatanmu |
| Remote MCP server hosted dengan OAuth | Jalan di sisi vendor | Satu per client plus flow consent | Integrasi yang dikelola vendor, nggak mau runtime lokal |
Tradeoff jujurnya: gateway menambah satu dependensi (Docker) dan satu langkah build untuk apa pun yang belum ada di katalog. Kalau kamu cuma pakai satu MCP server di satu client, config stdio biasa lebih ringan dan sebaiknya dipertahankan.
Troubleshooting
docker: 'mcp' is not a docker command: Docker Desktop di bawah 4.62, atau binary plugin nggak ada di~/.docker/cli-plugins. Cekdocker infountuk melihat direktori plugin yang benar-benar dibaca Docker.- Gateway jalan tapi container server nggak muncul: bukan masalah. Container dibuat saat tool call pertama. Pastikan dengan
docker mcp gateway run --verboseplus prompt nyata. - Model mengabaikan tool-mu: kemungkinan gateway mengekspos lebih banyak dari yang perlu. Pakai
--toolsuntuk memangkas daftarnya sesuai tugas. - Server nggak bisa akses API-nya: cek nilai config di profile dulu, baru default
--block-networkdan--block-secrets. - Client nunjukin nol tool: jalankan
docker mcp profile show web-devdan pastikan server-nya memang terpasang di profile yang kamu kirim lewat--profile.
Langkah lanjut
Tambahkan allowlist --tools ke profile yang paling sering kamu pakai, lalu bandingkan berapa definisi tool yang dikirim client sebelum dan sesudahnya. Kalau yang kamu butuh kontrol akses per user di server remote, itu masalah berbeda: urusannya ada di layer OAuth spesifikasi MCP, bukan di gateway.