SSD Nodes Learn
Mwongozo Matt ConnorNa Matt Connor · Imeboreshwa 2026-07-24

Kuweka MCP servers kwenye VPS yako

Jifunze jinsi ya kuendesha seva za Model Context Protocol kwa kutumia stdio na HTTP. Pata mwongozo wa systemd, TLS, na usalama wa remote transport.

Unachojenga

Mipangilio miwili ya MCP inayofanya kazi kwenye VPS moja. Kwanza ni seva ya stdio — zana ya filesystem au database ambayo Claude Code huianzisha kama child process na kuwasiliana nayo kupitia pipe. Kisha ni seva ya remote HTTP inayojiendesha kama network service ya muda mrefu nyuma ya systemd na nginx reverse proxy yenye TLS, inayoweza kufikiwa na client yoyote ya MCP unayoiunganisha. Ufungaji wa kila moja ni mdogo. Sehemu kubwa ya mwongozo huu inahusu mambo mawili muhimu: kuhakikisha JSON-RPC stream ni safi, na kutoweka endpoint ya zana isiyo na uthibitisho (unauthenticated) kwenye mtandao wa umma.

MCP ni nini hasa

Model Context Protocol ni njia ya kawaida kwa AI client — kama Claude Code, Claude Desktop, Gemini CLI kwenye VPS, au script yako — kuita tools za nje na kusoma rasilimali za nje. Model yenyewe haiteuzi chochote. Inauliza client, client hutumia JSON-RPC 2.0 kwenye server ya MCP, kisha server inatekeleza tool na kurudisha matokeo. Itifaki moja inamaanisha server unayoitengeneza mara moja itafanya kazi na kila client inayotumia MCP.

Kuna njia mbili za usafirishaji (transports), na mwongozo huu umegawanyika kulingana na hizo:

  • stdio. Client inazalisha server kama child process na kubadilishana ujumbe wa JSON-RPC yanayotenganishwa na mstari mpya (newline-delimited) kupitia standard input na standard output yake. Hakuna mtandao, hakuna port, hakuna uthibitisho (auth) — mipaka ya uaminifu ni process yenyewe. Karibu kila tool ya ndani hutumika kwa njia hii.
  • Streamable HTTP (na toleo lake la zamani, HTTP+SSE). Server ni huduma ya web inayojiendesha kwa muda mrefu. Client inaunganishwa kupitia HTTP na server inaweza kutuma majibu kama Server-Sent Events. Hii ndiyo njia ya kushiriki server moja na client nyingi, au kuendesha tool inayopaswa kubaki kwenye mashine kudumu.

Chagua stdio wakati tool inapotumika kwenye mashine moja na mtumiaji mmoja. Chagua HTTP wakati ni huduma ya pamoja.

Mahitaji ya awali na changamoto za kweli

Chukulia kuwa unatumia VPS mpya ya Ubuntu 24.04 KVM yenye root au sudo. Zaidi ya hayo:

  • Mazingira ya runtime ya server. Server nyingi za marejeleo hutumia Node au Python. Ubuntu 24.04 inakuja na Node 18, lakini baadhi ya paketi za MCP zinahitaji Node 20 au zaidi. Pakua toleo la LTS kutoka NodeSource au nvm badala ya kutegemea apt. Python 3.12 tayari ipo.
  • Domain na DNS A record, lakini kwa server ya HTTP ya mbali pekee — TLS inahitaji jina linaloelekeza kwenye VPS hii. Mfano wa stdio hauhitaji DNS kabisa.
  • 512 MB RAM inatosha. Server za MCP ni michakato midogo ya JSON-RPC; matumizi ya kumbukumbu yanategemea kifaa unachotumia (kama driver ya database au file cache), siyo itifaki yenyewe.
  • Maelezo (spec) ni mapya na yanabadilika. Toleo la 2025-03-26 limebadilisha HTTP+SSE kuwa Streamable HTTP na limeainisha SSE kuwa imepitwa na wakati. SSE bado inafanya kazi na server nyingi bado zinaitumia, hivyo usiamini kila itifaki ya usafirishaji (transport) bila kuikagua tena kwenye maelezo ya toleo la server husika.

Hatua ya 1: unganisha stdio server kwenye Claude Code

Anza na filesystem server — ni rasmi, inatunzwa mara kwa mara, na inahitaji Node pekee. Amri moja hapa chini inaweka server hiyo kwenye Claude Code na kuibakiza kwenye mradi wa sasa ili iwekwe kwenye faili inayoweza kuwekwa kwenye commit:

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

Kituo cha -- ni muhimu: kila kitu baada yake ni amri ambayo Claude Code itatekeleza, si flag ya Claude Code. Hii inaandika .mcp.json kwenye mzizi wa mradi:

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

Bado hakuna kinachojiendesha. Unapoanza Claude Code kwenye directory hii wakati ujao, agent itasoma .mcp.json, itaanzisha npx -y @modelcontextprotocol/server-filesystem ... kama child process, na itafanya MCP handshake kupitia stdin/stdout ya process hiyo. Thitibisha kuwa imekubali:

claude mcp list

Server inayofanya kazi vizuri huchapisha amri yake na alama ya tiki ya kijani — filesystem: npx -y @modelcontextprotocol/server-filesystem ... - ✓ Connected. Ndani ya session, slash command ya /mcp inaorodhesha zana ambazo server inazitoa (read_file, write_file, list_directory), na agent sasa inaweza kuzitumia kwenye njia ulizoruhusu. Zana ya database ina muundo sawa — badilisha package na utoe connection string kama argument yake ya mwisho — lakini kagua repository ya server hiyo kwa jina la sasa la package, kwa sababu Postgres server ya marejeleo imebadilishwa mara nyingi.

Huu ndio lengo kuu la kuendesha agent kwenye mashine: Claude Code session inaishi kwenye VPS ndani ya tmux, na stdio servers zake zinafanya kazi kando yake kwa ufikiaji wa moja kwa moja wa faili za mradi na huduma za ndani, bila kuhitaji safari ya mtandao.

Hatua ya 2: jenga seva ya HTTP ya mbali

Seva ya stdio hufungwa pamoja na mzazi wake. Unapotaka zana inayobaki ikiwa hai kwa kila mteja — zana ya pamoja ya uendeshaji, lango la database, au kitu ambacho laptop yako na CI yako vyote vinaita — unahitaji usafirishaji wa HTTP na huduma halisi. Hapa kuna seva ndogo ya Python inayotumia SDK rasmi, ikitoa zana moja:

# /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")

Zingatia host="127.0.0.1". Seva inafungwa kwenye localhost pekee — hakuna kitu nje ya mfumo kinachoweza kuifikia moja kwa moja, jambo ambalo ni muhimu kabla ya kuwepo kwa uthibitishaji (auth). Iweke kwenye virtualenv yake mwenyewe ili systemd iwe na njia ya interpreta thabiti:

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]"

Hatua ya 3: iwe hai kwa kutumia systemd

Zana inayozimika wakati agent inapoijaribu ni mbaya zaidi kuliko kutokuwa na zana kabisa. Andika /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

Njia kamili (absolute path) ya Python ya venv katika ExecStart si hiari — ielekeze kwenye /usr/bin/python3 na mchakato utaanza na ModuleNotFoundError: No module named 'mcp', kwa sababu interpreter wa mfumo hautaona pip install yako. Washa na kagua:

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 inapaswa kusoma active (running). curl itarudi HTTP/1.1 400 Bad Request ikiwa na kosa la JSON-RPC kwenye mwili wa ujumbe — ombi halikuwa na session na halikuwa na payload ya JSON halali — na hilo ndilo unalotaka: inathibitisha kuwa port inajibu na inatumia itifaki hiyo. Connection refused au jibu tupu inamaanisha mchakato haujafungwa (bound) mahali unapofikiri; soma journalctl -u mcp-ops -n 50.

Hatua ya 4: weka TLS na reverse proxy mbele

Server inasikiliza kwenye localhost. Ili kuifikia kutoka mahali popote, malizia TLS kwenye nginx na uweke proxy ndani. Sakinisha nginx, pata cheti kwa kutumia Certbot na Let's Encrypt kwenye nginx, kisha andika location block. Sehemu muhimu ni kuzima buffering, kwa sababu tabia ya nginx ni kushikilia jibu mpaka liwe kamili, jambo ambalo linazuia mtiririko wa SSE milele:

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;
    }
}

Pakia upya (reload) kwa kutumia sudo nginx -t && sudo systemctl reload nginx. Kama tayari unatumia mkusanyiko wa containers, kazi hiyo hiyo inafanywa na Traefik reverse proxy yenye TLS ya kiotomatiki — hutatua cheti na kuelekeza kwa hostname, na unapaswa kuongeza tu labels kwenye container ya MCP. Vyovyote vile, reverse proxy sasa ndiyo kitu pekee kwenye port ya umma, na inaelekeza kwenye huduma ambayo bado hujailinda. Rekebisha hilo kabla ya kusajili URL mahali popote.

Hatua ya 5: kanuni ya usalama inayotawala mada hii

Usifichue kamwe MCP endpoint isiyo na uthibitisho. MCP server si API ya kusoma tu. Inatoa ufikiaji wa zana — kwenye faili zako, database yako, na wakati mwingine shell. /mcp iliyo wazi kwenye mtandao wa umma ni kama mgeni mwenye uwezo sawa na AI agent yako: wanaorodhesha zana zako, kisha wanazitumia. Ichupe kama socket ya admin isiyo na uthibitisho, kwa sababu ndivyo ilivyo.

Njia tatu za ulinzi, kwa kufuata mpangilio wa upendeleo:

  1. Usichapishwe. Weka server kwenye 127.0.0.1 na uifikie kutoka kwenye laptop yako kwa kutumia SSH tunnel: ssh -L 8000:127.0.0.1:8000 matt@vps, kisha elekeza client kwenye http://127.0.0.1:8000/mcp. Hakuna kitu kinachofichuliwa.
  2. Iweke kwenye mtandao wa ndani. Unganisha anwani ya tunnel ya self-hosted WireGuard VPN na uruhusu tu VPN peers kuifikia. Mtandao wa umma utaona port iliyofungwa.
  3. Ikiwa lazima iwe ya umma, hitaji token. Jibu sahihi ni mtiririko wa MCP OAuth ambao HTTP transport inaunga mkono kiasili. Kiwango cha chini cha kiufundi ni bearer token inayoshirikishwa na kuangaliwa kwenye proxy — ni rahisi, na inazuia mashambulio ya bahatumbuia kabisa:
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...
}

Tengeneza token kwa kutumia openssl rand -hex 32, na usifunge server yenyewe kwenye 0.0.0.0 bila moja ya njia hizi mbele yake. Kisha client hutuma token kama header. Katika Claude Code:

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

Weka MCP_TOKEN kwenye shell yako ili siri isihifadhiwe kama plaintext kwenye .mcp.json — Claude Code hufafanua ${MCP_TOKEN} kutoka kwenye mazingira (environment) wakati wa kusoma.

Hatua ya 6: debug kwa kutumia MCP Inspector

Server ikifanya kazi isivyo kawaida, usikisie ndani ya agent — itumie moja kwa moja kupitia Inspector, ambayo ni client rasmi ya majaribio inayotumia web. Kwa stdio server, tumia amri ile ile inayotumiwa na agent:

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

Inafungua UI kwenye http://localhost:6274 (matoleo mapya hutoa URL yenye MCP_PROXY_AUTH_TOKEN query string — tumia link hiyo hiyo vinginevyo UI itakataa) na proxy kwenye 6277. Bonyeza Connect, kisha List Tools, kisha Call Tool ukitumia argument halisi. Ikiwa inafanya kazi kwenye Inspector lakini inafeli kwenye agent, hitilafu ipo kwenye client config yako, siyo kwenye server. Kwa remote HTTP server, chagua transport ya Streamable HTTP, weka https://mcp.example.com/mcp, ongeza header ya Authorization, kisha unganisha — hii ndiyo njia ya haraka zaidi kuthibitisha kuwa auth na proxy ni sahihi kabla ya kutumia agent yoyote.

Kuweka seva zikiwa na toleo la hivi karibuni

MCP inabadilika kwa kasi, hivyo weka ratiba ya kuweka mpatch. Seva za Node zinazozinduliwa kwa npx -y hupata toleo la hivi karibuni kila zinapozinduliwa, jambo ambalo ni rahisi lakini haliwezi kurudiwa; weka toleo kamili ulilojaribu — lisome kutoka npm view @modelcontextprotocol/server-filesystem version na uongeze kwenye jina la kifurushi katika .mcp.json (@modelcontextprotocol/server-filesystem@<version>) — mara tu seva itakapokuwa muhimu, ongeza toleo kwa makusudi. Seva za Python chini ya systemd zinahuishwa kwa sudo -H -u mcp /opt/mcp-ops/.venv/bin/pip install -U "mcp[cli]" ikifuatiwa na sudo systemctl restart mcp-ops. Angalia marekebisho ya spec inayolengwa na SDK yako unapofanya upgrade — mabadiliko kutoka SSE kwenda Streamable-HTTP yanaweza kubadilisha usafirishaji (transport) ambao wateja wako wanapaswa kuomba.

Njia za kushindwa, pamoja na maandishi utakayoyaona

Agent inaonyesha kuwa server imefeli. claude mcp list huchapisha ✗ Failed to connect, na TUI inatoa taarifa ya MCP server 'filesystem' failed to start. Run claude --debug na kwa kawaida utaona Error: spawn npx ENOENT — amri haipo kwenye PATH ya agent. Runtime haipo au haipo mahali agent inapotafuta: Node haijawekwa, npx haipo, au virtualenv ya Python imetajwa kwa jina la kawaida. Rekebisha amri iwe njia kamili (absolute path) au weka runtime, kisha unganisha tena.

Stdio server inaunganisha, kisha inakatika papo hapo. Client inarekodi kosa la JSON parse — kama vile Unexpected token 'S', "Server sta"... is not valid JSON au Failed to parse message. Sababu ni ileile: server imeandika mstari wa log kwenye stdout. Kwenye stdio, stdout ndio njia ya JSON-RPC, hivyo maandishi yoyote yasiyohusika yanaharibu mtiririko na handshake inafeli. Kwenye Node, console.log huenda kwenye stdout — tumia console.error. Kwenye Python, print() ya kawaida huenda kwenye stdout — andika logs kwa kutumia logging iliyosanidiwa kwenda sys.stderr, au tumia file=sys.stderr. Kanuni ni thabiti: kwenye stdio, JSON-RPC pekee kwenye stdout, kila kitu cha binadamu kwenye stderr.

Remote server inakatika muda wa handshake (timeout). Client inafeli na MCP error -32000: Connection closed, au Inspector inakwama kwenye Connect na haionyeshi tools. Nyuma ya nginx hii ni buffering: proxy inashikilia SSE stream badala ya kuisukuma, hivyo client inasubiri jibu ambalo haliji. Ongeza proxy_buffering off; (na sehemu nyingine ya block katika Hatua ya 4) kwenye location. Hakikisha kwa kutumia curl -N dhidi ya URL ya umma — unapaswa kuona data ya event ikija kidogo kidogo, siyo yote kwa pamoja mwishoni.

Auth imekataliwa. Client inatoa taarifa ya Error POSTing to endpoint (HTTP 401) au 401 Unauthorized. Ama header haipo, token ni mbaya, au variable ya shell ilikuwa tupu wakati client ilisoma config — mtego wa kawaida, kwa sababu ${MCP_TOKEN} hupanuka kuwa kitu kisichopo ikiwa variable haijasetuliwa na nginx inaona Bearer bila thamani. Echo variable hiyo, ongeza tena header, na uhakikishe byte sahihi zinafanana na token kwenye nginx if.

Service haianzi chini ya systemd. journalctl -u mcp-ops inaonyesha ModuleNotFoundError: No module named 'mcp'ExecStart inaashiria Python ya mfumo badala ya interpreter ya venv. Au Address already in use — mchakato mwingine unashikilia port 8000; utafute kwa kutumia sudo ss -ltnp | grep 8000.

FAQ

MCP server ni nini hasa?

Ni programu inayotoa zana na rasilimali kwa AI client kupitia Model Context Protocol, ikitumia JSON-RPC 2.0. AI model haiteuzi zana yenyewe — huuliza client yake, client huita MCP server, na server hutekeleza na kurudisha matokeo. Kwa sababu itifaki hii ni ya kawaida, server moja inaweza kufanya kazi na client yoyote inayozingatia viwango, iwe ni Claude Code, Claude Desktop, au Gemini CLI.

Tofauti kati ya stdio na HTTP transport ni ipi?

Stdio server huanzishwa na client kama child process na huwasiliana kupitia stdin/stdout, hivyo huishi na kufa pamoja na client mmoja kwenye mashine moja bila kuhitaji mtandao au uthibitisho (auth). HTTP server ni huduma ya mtandao inayojiendesha kwa muda mrefu ambayo client nyingi zinaweza kuifikia kwa wakati mmoja, ndiyo maana inahitaji TLS na uthibitisho. Tumia stdio kwa zana za ndani za mtumiaji mmoja; tumia HTTP (Streamable HTTP kwenye server za sasa) kwa kitu chochote kinachoshirikishwa au kinachodumu.

Ninawezaje kuimarisha usalama wa MCP server ya mbali?

Chukulia kuwa server inatoa ufikiaji wa zana kwenye faili, database, au shell yako, hivyo usiiweke wazi bila uthibitisho. Njia bora ni kuibandika kwenye localhost na kuifikia kupitia SSH tunnel au VPN ya ndani; ikiwa lazima iwe ya hadhara, iweke nyuma ya reverse proxy inayotumia bearer token au mtiririko wa MCP OAuth. Tengeneza token kwa kutumia openssl rand -hex 32 na usibandike server kwenye 0.0.0.0 bila moja ya hizi mbele yake.

Ninawezaje kuingilia (debug) server isiyoanzika?

Kwanza kagua claude mcp list✗ Failed to connect pamoja na spawn ... ENOENT inamaanisha kuwa amri au runtime haipo, hivyo rekebisha njia (path) au iweke. Ikiwa inaunganisha kisha inakatika kwa kosa la JSON parse, server inatoa log kwenye stdout na kuharibu mtiririko wa JSON-RPC; hamisha log zote kwenda stderr. Kwa jambo lingine lolote, run amri halisi chini ya MCP Inspector, ambayo huendesha server peke yake ili uweze kutofautisha hitilafu ya server na hitilafu ya usanidi wa client.