SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-29

Cara Jalankan MCP Server di VPS untuk AI Coding Agent

Ketahui cara memasang pelayan MCP menggunakan stdio dan HTTP di VPS. Panduan ini merangkumi konfigurasi systemd, penggunaan Nginx sebagai reverse proxy, serta langkah keselamatan.

Apa yang anda bina

Dua persediaan MCP yang berfungsi pada satu VPS. Pertama, pelayan stdio, iaitu alat sistem fail atau pangkalan data yang dilancarkan oleh Claude Code sebagai proses anak dan berkomunikasi melalui paip. Kedua, pelayan remote HTTP yang berjalan sebagai perkhidmatan rangkaian jangka hayat panjang di sebalik systemd dan reverse proxy nginx dengan TLS, yang boleh dicapai oleh mana-mana klien MCP yang anda hubungkan dengannya. Pemasangan untuk kedua-duanya adalah kecil. Kebanyakan panduan ini tertumpu pada dua perkara yang sering menjadi masalah: memastikan aliran JSON-RPC bersih, dan tidak sekali-kali meletakkan endpoint alat tanpa pengesahan di internet awam.

Apakah sebenarnya MCP

Model Context Protocol ialah cara standard untuk klien AI, Claude Code, Claude Desktop, Gemini CLI pada VPS, atau skrip anda sendiri, untuk memanggil alatan luaran dan membaca sumber luaran. Model itu sendiri tidak menjalankan apa-apa. Ia meminta klien, klien bercakap JSON-RPC 2.0 kepada server MCP, server menjalankan alatan tersebut dan menyerahkan hasilnya kembali. Klien itu ialah komponen yang dimaksudkan orang apabila mereka menyebut agent harness: gelung di sekeliling model yang memiliki senarai alatan, semakan kebenaran dan status sesi, dan MCP hanyalah cara anda mengembangkan bahagian alatan tersebut. Satu protokol, jadi server yang anda tulis sekali akan berfungsi dengan setiap klien yang menyokong MCP. Jika pembahagian ini baharu bagi anda, dan terutamanya persoalan tentang bagaimana model memutuskan untuk menggunakan alatan, laluan berperingkat melalui asas ejen berbaloi untuk dipelajari selama sejam sebelum anda memberikan kelayakan sebenar kepada salah satu server ini.

Terdapat dua pengangkutan, dan keseluruhan panduan ini dibahagikan mengikutnya:

  • stdio. Klien melancarkan server sebagai proses anak dan menukar mesej JSON-RPC yang dipisahkan oleh baris baharu melalui input standard dan output standardnya. Tiada rangkaian, tiada port, tiada pengesahan, sempadan kepercayaan ialah proses itu sendiri. Hampir setiap alatan tempatan menggunakan cara ini.
  • Streamable HTTP (dan sepupunya yang lebih lama, HTTP+SSE). Server ialah perkhidmatan web yang berjalan lama. Klien menyambung melalui HTTP dan server boleh menstrim respons kembali sebagai Server-Sent Events. Inilah cara anda berkongsi satu server dengan banyak klien, atau menjalankan alatan yang mesti kekal pada mesin secara berterusan.

Pilih stdio apabila alatan tersebut milik satu mesin dan satu pengguna. Pilih HTTP apabila ia merupakan perkhidmatan yang dikongsi.

Prasyarat dan perkara penting yang perlu diketahui

Andaikan anda mempunyai VPS KVM Ubuntu 24.04 yang baharu dengan akses root atau sudo. Selain itu:

  • Runtime untuk bahasa pengaturcaraan pelayan. Kebanyakan pelayan rujukan dibina menggunakan Node atau Python. Ubuntu 24.04 menyediakan Node 18, namun beberapa pakej MCP semasa memerlukan Node 20 atau lebih baharu. Oleh itu, pasang versi LTS terkini daripada NodeSource atau nvm dan jangan bergantung pada apt. Python 3.12 sudah tersedia.
  • Domain dan DNS A record, tetapi hanya untuk pelayan HTTP jauh. TLS memerlukan nama yang dapat diselesaikan (resolve) kepada VPS ini. Contoh stdio tidak memerlukan DNS sama sekali.
  • RAM 512 MB sudah mencukupi. Pelayan MCP adalah proses JSON-RPC yang ringan; penggunaan memori bergantung pada perkara yang dicapai oleh alat anda (pemacu pangkalan data, cache fail), bukan pada protokol itu sendiri.
  • Spesifikasi ini masih baharu dan sentiasa berubah. Semakan 2025-03-26 telah menggantikan HTTP+SSE dengan Streamable HTTP dan menandakan SSE sebagai deprecated. SSE masih berfungsi dan banyak pelayan masih menyokongnya, jadi anggap sebarang penetapan (pin) pengangkutan sebagai perkara yang perlu disemak semula berdasarkan nota keluaran pelayan, bukannya sebagai peraturan mutlak.

Langkah 1: sambungkan pelayan stdio ke Claude Code

Mulakan dengan pelayan sistem fail; ia adalah rasmi, diselenggara secara aktif, dan hanya memerlukan Node. Satu arahan di bawah mendaftarkannya dengan Claude Code dan mengehadkan skopnya kepada projek semasa supaya ia disimpan dalam fail yang boleh di-commit:

cd /home/matt/projects/api
claude mcp add --scope project --transport stdio filesystem \
  -- npx -y @modelcontextprotocol/server-filesystem /home/matt/projects/api

Pemisah -- adalah penting: segala-galanya selepas itu ialah arahan yang akan dijalankan oleh Claude Code, bukan flag untuk Claude Code. Ini menulis .mcp.json pada root projek:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/home/matt/projects/api"
      ]
    }
  }
}

Tiada apa-apa yang berjalan lagi. Apabila anda memulakan Claude Code dalam direktori ini seterusnya, ejen akan membaca .mcp.json, menjana npx -y @modelcontextprotocol/server-filesystem ... sebagai proses anak, dan melakukan jabat tangan MCP melalui stdin/stdout proses tersebut. Sahkan ia berjaya:

claude mcp list

Pelayan yang sihat akan mencetak arahannya dan tanda rait hijau, filesystem: npx -y @modelcontextprotocol/server-filesystem ... - ✓ Connected. Di dalam sesi, arahan slash /mcp menyenaraikan alatan yang didedahkan oleh pelayan (read_file, write_file, list_directory), dan ejen kini boleh memanggilnya pada laluan yang anda benarkan. Alatan pangkalan data mempunyai bentuk yang sama, tukar pakej dan hantarkan connection string sebagai argumen terakhirnya, tetapi semak repositori pelayan itu sendiri untuk nama pakej semasa, memandangkan pelayan Postgres rujukan telah bertukar tangan lebih daripada sekali.

Inilah tujuan utama menjalankan ejen pada mesin tersebut: sesi Claude Code kekal pada VPS di dalam tmux, dan pelayan stdio-nya berjalan tepat di sebelahnya dengan akses terus kepada fail projek dan servis tempatan, tanpa pusingan rangkaian. Apabila ejen memegang write_file serta read_file, adalah berbaloi untuk menggabungkan capaian tersebut dengan kemahiran yang mendorongnya ke arah perubahan terkecil yang berkesan, kerana alatan sistem fail menjadikan penulisan semula yang meluas semudah pembetulan dua baris. Pendawaian yang sama melangkaui fail tempatan: jika anda sudah menjalankan enjin carian pada VPS, anda boleh memberikan ejen instans SearXNG anda sendiri sebagai alatan carian, yang memastikan pertanyaan kekal pada mesin anda tetapi menarik teks halaman yang tidak dipercayai terus ke dalam konteks yang kemudiannya ditindaklanjuti oleh ejen.

Langkah 2: membina pelayan HTTP jauh

Pelayan stdio akan tamat apabila induknya ditamatkan, dan ia dijana sekali bagi setiap klien. Oleh itu, jika anda menjalankan dua sesi Claude Code pada mesin yang saling menghantar tugasan, setiap sesi akan mendapat salinan alatnya yang tersendiri. Apabila anda memerlukan alat yang kekal aktif untuk setiap klien, seperti alat operasi berkongsi, gerbang pangkalan data, atau sesuatu yang dipanggil oleh komputer riba dan CI anda, anda memerlukan pengangkutan HTTP dan servis sebenar. Berikut adalah pelayan Python minimum yang menggunakan SDK rasmi, yang mendedahkan satu alat:

# /opt/mcp-ops/server.py
from mcp.server.fastmcp import FastMCP
import subprocess

mcp = FastMCP("ops-tools", host="127.0.0.1", port=8000)

@mcp.tool()
def disk_free() -> str:
    """Return `df -h` for the server."""
    out = subprocess.run(["df", "-h"], capture_output=True, text=True)
    return out.stdout

if __name__ == "__main__":
    # Serves Streamable HTTP at /mcp on 127.0.0.1:8000
    mcp.run(transport="streamable-http")

Perhatikan host="127.0.0.1". Pelayan ini hanya mengikat kepada localhost, tiada apa-apa di luar mesin boleh mencapainya secara terus, yang merupakan perkara tepat yang anda perlukan sebelum pengesahan wujud. Pasang ia dalam virtualenv sendiri supaya systemd mempunyai laluan penterjemah yang stabil:

sudo useradd --system --home /opt/mcp-ops --shell /usr/sbin/nologin mcp
sudo install -d -o mcp -g mcp /opt/mcp-ops
sudo -H -u mcp python3 -m venv /opt/mcp-ops/.venv
sudo -H -u mcp /opt/mcp-ops/.venv/bin/pip install "mcp[cli]"

Langkah 3: pastikan ia terus berjalan dengan systemd

Alat yang tidak berfungsi apabila ejen cuba mencapainya adalah lebih buruk daripada tiada alat langsung. Ini paling penting apabila klien itu sendiri merupakan proses yang berjalan lama: ejen yang sentiasa aktif dan mengekalkan memori serta jadualnya merentasi but semula akan memanggil alat ini mengikut jadual tanpa pengawasan, jadi pelayan juga perlu kembali aktif dengan sendirinya. Tulis /etc/systemd/system/mcp-ops.service:

[Unit]
Description=MCP ops-tools server
After=network.target

[Service]
Type=simple
User=mcp
WorkingDirectory=/opt/mcp-ops
ExecStart=/opt/mcp-ops/.venv/bin/python /opt/mcp-ops/server.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target

Laluan mutlak ke Python venv dalam ExecStart adalah wajib, halakan ia ke /usr/bin/python3 dan proses akan bermula dengan ModuleNotFoundError: No module named 'mcp', kerana interpreter sistem tidak pernah melihat pip install anda. Aktifkan dan semak:

sudo systemctl daemon-reload
sudo systemctl enable --now mcp-ops
sudo systemctl status mcp-ops
curl -si -H 'Accept: application/json, text/event-stream' \
  -H 'Content-Type: application/json' \
  -X POST http://127.0.0.1:8000/mcp

status sepatutnya memaparkan active (running). curl akan kembali dengan HTTP/1.1 400 Bad Request berserta ralat JSON-RPC dalam badan respons, permintaan tersebut tidak membawa sesi dan tiada payload JSON yang sah, dan itulah yang anda mahukan: ia membuktikan port tersebut menjawab dan menggunakan protokol yang betul. Connection refused atau balasan kosong bermakna proses tidak terikat di tempat yang anda sangkakan; baca journalctl -u mcp-ops -n 50.

Langkah 4: letakkan TLS dan reverse proxy di hadapan

Pelayan mendengar pada localhost. Untuk mencapainya dari mana-mana lokasi, anda perlu menamatkan TLS pada nginx dan melakukan proksi ke dalam. Pasang nginx, dapatkan sijil dengan Certbot dan Let's Encrypt pada nginx, kemudian tulis blok location. Bahagian kritikal adalah menyahdayakan penimbalan (buffering), kerana kelakuan lalai nginx menyimpan respons sehingga ia lengkap, yang akan menyekat aliran SSE selama-lamanya:

server {
    listen 443 ssl;
    server_name mcp.example.com;

    # ssl_certificate lines managed by Certbot

    location /mcp {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;

        # The four lines that make SSE work through nginx:
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
        chunked_transfer_encoding off;
    }
}

Muat semula dengan sudo nginx -t && sudo systemctl reload nginx. Jika anda sudah menjalankan sekumpulan kontena, tugas yang sama dilakukan untuk anda oleh Traefik reverse proxy dengan TLS automatik, ia mengeluarkan sijil dan menghalakan trafik mengikut hostname, dan anda hanya perlu menambah label pada kontena MCP. Walau apa pun caranya, reverse proxy kini menjadi satu-satunya entiti pada port awam, dan ia menghala ke servis yang belum anda selamatkan. Selesaikan perkara itu sebelum anda mendaftarkan URL tersebut di mana-mana.

Langkah 5: peraturan keselamatan yang menguasai topik ini

Jangan sekali-kali mendedahkan endpoint MCP tanpa pengesahan. Pelayan MCP bukanlah API baca sahaja. Ia memberikan akses alat kepada fail, pangkalan data, dan kadangkala shell anda. /mcp yang terbuka di internet awam bermakna orang asing mempunyai capaian yang sama seperti ejen AI anda: mereka menyenaraikan alat anda, kemudian memanggilnya. Layari ia sama seperti soket pentadbir tanpa pengesahan, kerana itulah fungsinya. Sejauh mana token yang dicuri boleh merugikan anda juga bergantung pada pelayan di belakangnya: pelayan MCP baca sahaja yang disertakan bersama penjejak senaman openGym hanya boleh memberikan data latihan, manakala alat sistem fail atau shell boleh menyerahkan keseluruhan pelayan.

Tiga pertahanan, mengikut keutamaan:

  1. Jangan terbitkan ia. Kekalkan pelayan pada 127.0.0.1 dan capai ia dari komputer riba anda dengan terowong SSH: ssh -L 8000:127.0.0.1:8000 matt@vps, kemudian halakan klien ke http://127.0.0.1:8000/mcp. Tiada apa-apa yang terdedah.
  2. Letakkan ia pada rangkaian peribadi. Ikat alamat terowong bagi VPN WireGuard yang dihoskan sendiri dan benarkan hanya rakan setara VPN mencapainya. Internet awam hanya melihat port yang tertutup.
  3. Jika ia mesti bersifat awam, perlukan token. Jawapan yang betul ialah aliran OAuth MCP yang disokong secara natif oleh pengangkutan HTTP. Tahap minimum yang pragmatik ialah token pembawa (bearer token) dikongsi yang diperiksa di proksi; ia murah dan menghentikan serangan rambang sepenuhnya:
location /mcp {
    if ($http_authorization != "Bearer REPLACE_WITH_LONG_RANDOM") {
        return 401;
    }
    proxy_pass http://127.0.0.1:8000;
    # ...buffering-off block from above...
}

Jana token dengan openssl rand -hex 32, dan jangan sekali-kali ikat pelayan itu sendiri ke 0.0.0.0 tanpa salah satu daripada langkah ini di hadapannya. Klien kemudian menghantar token sebagai pengepala (header). Dalam Claude Code:

claude mcp add --scope project --transport http ops-tools https://mcp.example.com/mcp \
  --header 'Authorization: Bearer ${MCP_TOKEN}'

Tetapkan MCP_TOKEN dalam shell anda supaya rahsia tersebut tidak tersimpan dalam .mcp.json dalam bentuk teks biasa; Claude Code mengembangkan ${MCP_TOKEN} daripada persekitaran semasa waktu baca.

Setiap pertahanan di atas mengawal endpoint dan bukannya ejen yang sudah memegang token, yang merupakan separuh lagi masalah tersebut: jika klien anda ialah DeepSeek Harness, pemalam yang mengehadkan alat mana yang boleh dipanggil oleh ejen dan mengimbas output alat untuk arahan yang disuntik melindungi bahagian tersebut.

Langkah 6: nyahpepijat dengan MCP Inspector

Apabila pelayan tidak berfungsi dengan betul, jangan meneka dari dalam ejen. Gunakan Inspector, iaitu klien ujian rasmi berasaskan web, untuk mengawalnya secara terus. Bagi pelayan stdio, berikan arahan yang sama seperti yang dijalankan oleh ejen:

npx @modelcontextprotocol/inspector \
  npx -y @modelcontextprotocol/server-filesystem /tmp

Ia akan memulakan UI pada http://localhost:6274 (versi terkini mencetak URL dengan rentetan pertanyaan MCP_PROXY_AUTH_TOKEN; gunakan pautan tepat tersebut atau UI akan menolak akses anda) dan proksi pada 6277. Klik Connect, kemudian List Tools, dan seterusnya Call Tool dengan argumen sebenar. Jika ia berfungsi dalam Inspector tetapi gagal dalam ejen, pepijat tersebut berada dalam konfigurasi klien anda, bukan pada pelayan. Bagi pelayan HTTP jauh, pilih pengangkutan Streamable HTTP, masukkan https://mcp.example.com/mcp, tambah pengepala Authorization, dan sambungkan. Ini adalah cara terpantas untuk mengesahkan bahawa pengesahan dan proksi adalah betul sebelum melibatkan sebarang ejen.

Memastikan pelayan sentiasa dikemas kini

MCP berkembang dengan pantas, jadi lakukan tampalan (patch) mengikut jadual. Pelayan Node yang dilancarkan dengan npx -y akan mengambil versi terkini setiap kali ia dimulakan; ini memudahkan tetapi tidak boleh dihasilkan semula (non-reproducible). Tetapkan (pin) versi tepat yang telah anda uji, baca versi tersebut daripada npm view @modelcontextprotocol/server-filesystem version dan tambahkannya pada nama pakej dalam .mcp.json (@modelcontextprotocol/server-filesystem@<version>) sebaik sahaja pelayan tersebut menjadi penting, dan lakukan peningkatan versi secara sengaja. Pelayan Python di bawah systemd dikemas kini dengan sudo -H -u mcp /opt/mcp-ops/.venv/bin/pip install -U "mcp[cli]" diikuti oleh sudo systemctl restart mcp-ops. Perhatikan semakan spesifikasi yang disasarkan oleh SDK anda apabila anda melakukan peningkatan; peralihan merentasi sempadan SSE-ke-Streamable-HTTP boleh mengubah pengangkutan (transport) yang perlu diminta oleh klien anda.

Mod kegagalan, berserta rentetan yang akan anda lihat

Ejen menunjukkan pelayan gagal. claude mcp list mencetak ✗ Failed to connect, dan TUI melaporkan MCP server 'filesystem' failed to start. Jalankan claude --debug dan anda biasanya akan melihat Error: spawn npx ENOENT, arahan tersebut tiada dalam PATH ejen. Runtime hilang atau tidak berada di lokasi yang dicari oleh ejen: Node tidak dipasang, npx tiada, atau Python virtualenv dirujuk dengan nama ringkas. Betulkan arahan kepada laluan mutlak atau pasang runtime, kemudian sambung semula.

Pelayan stdio bersambung, kemudian terputus serta-merta. Log klien menunjukkan ralat penghuraian JSON, seperti Unexpected token 'S', "Server sta"... is not valid JSON atau Failed to parse message. Puncanya sentiasa sama: pelayan menulis baris log ke stdout. Pada stdio, stdout adalah saluran JSON-RPC, jadi sebarang teks asing akan merosakkan aliran tersebut dan jabat tangan (handshake) gagal. Dalam Node, console.log pergi ke stdout, gunakan console.error. Dalam Python, print() ringkas pergi ke stdout, tulis log dengan logging yang dikonfigurasikan ke sys.stderr, atau hantar file=sys.stderr. Peraturannya mutlak: pada stdio, hanya JSON-RPC pada stdout, semua maklumat untuk manusia pada stderr.

Pelayan jauh tamat masa atau ditutup semasa jabat tangan. Klien gagal dengan MCP error -32000: Connection closed, atau Inspector tergantung pada Connect dan tidak menyenaraikan alatan. Di sebalik nginx, ini disebabkan oleh penimbalan (buffering): proksi menahan aliran SSE dan bukannya menyalurkannya, jadi klien menunggu respons yang tidak pernah tiba. Tambahkan proxy_buffering off; (dan baki blok dalam Langkah 4) ke location. Sahkan dengan curl -N terhadap URL awam, anda sepatutnya melihat data acara tiba secara berperingkat, bukan sekaligus pada penghujungnya.

Pengesahan ditolak. Klien melaporkan Error POSTing to endpoint (HTTP 401) atau secara jelas 401 Unauthorized. Sama ada pengepala (header) hilang, token salah, atau pemboleh ubah shell kosong apabila klien membaca konfigurasi, satu perangkap biasa, kerana ${MCP_TOKEN} berkembang menjadi tiada apa-apa jika pemboleh ubah tidak ditetapkan dan nginx kemudian melihat Bearer tanpa nilai. Echo pemboleh ubah tersebut, tambah semula pengepala, dan sahkan bait yang tepat sepadan dengan token dalam if nginx.

Servis tidak mahu bermula di bawah systemd. journalctl -u mcp-ops menunjukkan ModuleNotFoundError: No module named 'mcp', ExecStart menghala ke Python sistem dan bukannya penterjemah venv. Atau Address already in use, proses lain memegang 8000; cari proses tersebut dengan sudo ss -ltnp | grep 8000.

FAQ

Apakah sebenarnya pelayan MCP?

Ia merupakan atur cara yang mendedahkan alatan dan sumber kepada klien AI melalui Model Context Protocol, menggunakan JSON-RPC 2.0. Model AI tidak pernah menjalankan alatan tersebut secara terus; ia meminta kliennya, klien memanggil pelayan MCP, dan pelayan tersebut melaksanakan serta mengembalikan hasil. Oleh kerana protokol ini adalah standard, satu pelayan boleh berfungsi dengan mana-mana klien yang mematuhi protokol, sama ada Claude Code, Claude Desktop, atau Gemini CLI.

Apakah perbezaan antara pengangkutan stdio dan HTTP?

Pelayan stdio dilancarkan oleh klien sebagai proses anak dan berkomunikasi melalui stdin/stdout, jadi ia hidup dan mati bersama satu klien pada satu mesin serta tidak memerlukan rangkaian atau pengesahan. Pelayan HTTP ialah servis rangkaian yang berjalan lama dan boleh dicapai oleh banyak klien serentak, itulah sebabnya ia memerlukan TLS dan pengesahan. Gunakan stdio untuk alatan tempatan pengguna tunggal; gunakan HTTP (Streamable HTTP pada pelayan semasa) untuk sebarang perkara yang dikongsi atau kekal.

Bagaimanakah cara saya menjamin keselamatan pelayan MCP jauh?

Anggaplah ia memberikan akses alatan kepada fail, pangkalan data, atau shell anda, dan jangan sekali-kali mendedahkannya tanpa pengesahan. Cara terbaik adalah dengan mengekalkannya terikat pada localhost dan mencapainya melalui SSH tunnel atau VPN peribadi; jika ia mesti bersifat awam, letakkannya di belakang reverse proxy yang menguatkuasakan bearer token atau aliran MCP OAuth. Jana token dengan openssl rand -hex 32 dan jangan sekali-kali mengikat pelayan pada 0.0.0.0 tanpa salah satu daripada langkah ini di hadapannya.

Bagaimanakah cara saya menyahpepijat pelayan yang tidak mahu bermula?

Pertama, semak claude mcp list, ✗ Failed to connect dengan spawn ... ENOENT bermaksud arahan atau runtime tiada, jadi betulkan path atau pasangkannya. Jika ia bersambung kemudian terputus dengan ralat JSON parse, pelayan sedang merekod log ke stdout dan merosakkan aliran JSON-RPC; pindahkan semua log ke stderr. Untuk sebarang masalah lain, jalankan arahan yang tepat di bawah MCP Inspector, yang memacu pelayan secara terasing supaya anda boleh membezakan pepijat pelayan daripada pepijat konfigurasi klien.