VPS पर MCP servers कैसे चलाएं
अपने VPS पर MCP servers setup करें। इसमें stdio, remote HTTP, systemd और TLS configuration के साथ authentication की पूरी जानकारी दी गई है।
आप क्या बना रहे हैं
एक ही VPS पर दो working MCP setups. पहला एक stdio server है — एक filesystem या database tool जिसे Claude Code एक child process के रूप में launch करता है और pipe के माध्यम से बात करता है। दूसरा एक remote HTTP server है जो systemd और TLS के साथ nginx reverse proxy के पीछे एक long-lived network service के रूप में चलता है, जिसे किसी भी MCP client द्वारा एक्सेस किया जा सकता है। दोनों का installation छोटा है। इस guide का अधिकांश हिस्सा उन दो मुख्य चुनौतियों पर केंद्रित है: JSON-RPC stream को clean रखना, और किसी भी unauthenticated tool endpoint को public internet पर न डालना।
MCP वास्तव में क्या है
Model Context Protocol एक मानक तरीका है जिससे एक AI client — जैसे Claude Code, Claude Desktop, Gemini CLI on a VPS, या आपका अपना script — बाहरी tools को call कर सकता है और external resources को पढ़ सकता है। Model स्वयं कुछ भी run नहीं करता है। यह client से अनुरोध करता है, client MCP server को JSON-RPC 2.0 भेजता है, और server tool को run करके परिणाम वापस देता है। एक ही protocol होने के कारण, आपके द्वारा लिखा गया server उन सभी clients के साथ काम करेगा जो MCP का उपयोग करते हैं।
इसमें दो transports हैं, और इस guide का अगला हिस्सा इन्हीं पर आधारित है:
- stdio. Client server को एक child process के रूप में चलाता है। यह standard input और standard output के माध्यम से newline-delimited JSON-RPC messages का आदान-प्रदान करता है। इसमें कोई network, port, या auth की आवश्यकता नहीं होती — trust boundary स्वयं process है। लगभग सभी local tools इसी तरह काम करते हैं।
- Streamable HTTP (और इसका पुराना version, HTTP+SSE)। Server एक long-running web service होता है। Client HTTP के माध्यम से connect करता है और server responses को Server-Sent Events के रूप में stream कर सकता है। इस तरीके से आप एक server को कई clients के साथ share कर सकते हैं, या ऐसे tool को चला सकते हैं जिसे machine पर स्थायी रूप से रहना चाहिए।
जब tool किसी एक machine और एक user के लिए हो, तब stdio चुनें। जब यह एक shared service हो, तब HTTP चुनें।
Prerequisites और महत्वपूर्ण बातें
मान लीजिए कि आपके पास root या sudo एक्सेस वाला एक नया Ubuntu 24.04 KVM VPS है। इसके अलावा:
- सर्वर के लिए आवश्यक runtime. अधिकांश reference servers Node या Python पर आधारित होते हैं। Ubuntu 24.04 में Node 18 मिलता है, जबकि कई वर्तमान MCP packages को Node 20 या उससे नया version चाहिए। इसलिए
aptपर भरोसा करने के बजाय NodeSource या nvm से current LTS install करें। Python 3.12 पहले से मौजूद है। - एक domain और DNS A record. यह केवल remote HTTP server के लिए आवश्यक है — TLS के लिए एक ऐसे name की आवश्यकता होती है जो इस VPS पर resolve हो सके। stdio example के लिए किसी DNS की आवश्यकता नहीं है।
- 512 MB RAM पर्याप्त है. MCP servers हल्के JSON-RPC processes होते हैं; memory का उपयोग आपके tool (जैसे database driver, file cache) पर निर्भर करता है, protocol पर नहीं।
- Spec नया है और बदल रहा है. 2025-03-26 के revision में HTTP+SSE की जगह Streamable HTTP को लाया गया है और SSE को deprecated घोषित किया गया है। SSE अभी भी काम करता है और कई servers अभी भी इसका उपयोग करते हैं, इसलिए किसी भी transport method को अंतिम सत्य मानने के बजाय server के release notes से दोबारा जांच लें।
Step 1: Claude Code में stdio server को जोड़ें
filesystem server से शुरुआत करें — यह official है, इसे actively maintain किया जाता है, और इसे Node के अलावा किसी और चीज़ की आवश्यकता नहीं है। नीचे दिया गया command इसे Claude Code के साथ register कर देगा और इसे current project तक सीमित कर देगा ताकि यह एक committable file में सेव हो सके:
cd /home/matt/projects/api
claude mcp add --scope project --transport stdio filesystem \
-- npx -y @modelcontextprotocol/server-filesystem /home/matt/projects/api-- separator महत्वपूर्ण है: इसके बाद की सभी चीजें वह command हैं जिसे Claude Code चलाएगा, यह Claude Code के लिए कोई flag नहीं है। यह project root में .mcp.json लिखता है:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/home/matt/projects/api"
]
}
}
}अभी कुछ भी run नहीं हो रहा है। जब आप इस directory में अगली बार Claude Code शुरू करेंगे, तो agent .mcp.json को पढ़ेगा, npx -y @modelcontextprotocol/server-filesystem ... को एक child process के रूप में चलाएगा, और उस process के stdin/stdout पर MCP handshake करेगा। पुष्टि करें कि यह सफल रहा:
claude mcp listएक सही server अपना command और एक हरा tick प्रिंट करता है — filesystem: npx -y @modelcontextprotocol/server-filesystem ... - ✓ Connected। Session के अंदर, /mcp slash command उन tools की सूची देता है जिन्हें server expose करता है (read_file, write_file, list_directory), और agent अब आपके द्वारा allow किए गए paths पर उन्हें call कर सकता है। Database tool भी इसी तरह काम करता है — package को बदलें और अंतिम argument के रूप में connection string पास करें — लेकिन current package name के लिए server के अपने repository को ज़रूर चेक करें, क्योंकि reference Postgres server को कई बार बदला जा चुका है।
Agent को machine पर चलाने का मुख्य उद्देश्य यही है: Claude Code session tmux के अंदर VPS पर चलता है, और इसके stdio servers project files और local services तक direct access के साथ इसके ठीक बगल में चलते हैं, बिना किसी network round-trip के।
Step 2: एक remote HTTP server बनाएँ
stdio server अपने parent process के साथ बंद हो जाता है। जब आपको एक ऐसे tool की आवश्यकता हो जो हर client के लिए चलता रहे — जैसे कि एक shared ops tool, database gateway, या कोई ऐसा tool जिसे आपका laptop और CI दोनों call करें — तो आपको HTTP transport और एक real service की आवश्यकता होगी। यहाँ official SDK का उपयोग करने वाला एक minimal Python server दिया गया है, जो एक tool expose करता है:
# /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")host="127.0.0.1" पर ध्यान दें। Server केवल localhost पर bind होता है — बाहर से कोई भी इसे directly access नहीं कर सकता, जो authentication से पहले आपके लिए आवश्यक है। इसे अपने स्वयं के virtualenv में install करें ताकि systemd के पास एक stable interpreter path रहे:
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]"Step 3: systemd के साथ इसे चालू रखें
यदि agent को आवश्यक tool न मिले, तो वह काम नहीं कर पाएगा। /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.targetExecStart में venv Python का absolute path देना अनिवार्य है — इसे /usr/bin/python3 पर point करें। प्रक्रिया ModuleNotFoundError: No module named 'mcp' के साथ शुरू होगी, क्योंकि system interpreter आपके pip install को नहीं पहचानता है। इसे enable करें और check करें:
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/mcpactive (running) को status पढ़ना चाहिए। curl को HTTP/1.1 400 Bad Request के साथ JSON-RPC error मिलता है — request में कोई session या valid JSON payload नहीं था — और आपको यही चाहिए: यह साबित करता है कि port जवाब दे रहा है और protocol समझता है। Connection refused या empty reply का मतलब है कि process वहां bound नहीं है जहाँ आप सोच रहे हैं; journalctl -u mcp-ops -n 50 पढ़ें।
Step 4: TLS और reverse proxy सेटअप करें
Server localhost पर listen करता है। इसे कहीं से भी एक्सेस करने के लिए, आपको nginx पर TLS terminate करना होगा और फिर inward proxy करना होगा। nginx इंस्टॉल करें, Certbot and Let's Encrypt on nginx का उपयोग करके certificate प्राप्त करें, और फिर location block लिखें। सबसे महत्वपूर्ण काम buffering को disable करना है। nginx का default behaviour response को पूरा होने तक रोक कर रखता है, जिससे SSE stream हमेशा के लिए रुक जाती है:
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;
}
}sudo nginx -t && sudo systemctl reload nginx के साथ reload करें। यदि आप पहले से ही containers का fleet चला रहे हैं, तो Traefik reverse proxy with automatic TLS यह काम आपके लिए कर सकता है — यह certificate जारी करता है और hostname के आधार पर route करता है। आपको बस MCP container में labels जोड़ने होंगे। दोनों ही मामलों में, अब reverse proxy ही एकमात्र चीज़ है जो public port पर है, और यह एक ऐसी service की ओर इशारा कर रहा है जिसे आपने अभी तक secure नहीं किया है। URL को कहीं भी register करने से पहले इसे ठीक कर लें।
Step 5: इस विषय का सबसे महत्वपूर्ण सुरक्षा नियम
कभी भी unauthenticated MCP endpoint को सार्वजनिक न करें। MCP server केवल read-only API नहीं है। यह आपके files, database, और कभी-कभी shell तक tool access प्रदान करता है। Public internet पर खुला /mcp उतना ही खतरनाक है जितना आपका AI agent: वे आपके tools की सूची बना सकते हैं और फिर उन्हें call कर सकते हैं। इसे एक unauthenticated admin socket की तरह ही समझें, क्योंकि वास्तव में यह वही है।
तीन सुरक्षा उपाय, प्राथमिकता के क्रम में:
- इसे publish न करें। Server को
127.0.0.1पर रखें और अपने laptop से SSH tunnel के माध्यम से इसे एक्सेस करें:ssh -L 8000:127.0.0.1:8000 matt@vps, फिर client कोhttp://127.0.0.1:8000/mcpपर point करें। इससे कुछ भी public नहीं होता। - इसे private network पर रखें। self-hosted WireGuard VPN के tunnel address को bind करें और केवल VPN peers को ही access दें। Public internet को केवल एक closed port दिखाई देगा।
- यदि इसे public रखना ही है, तो token अनिवार्य करें। सबसे सही तरीका MCP OAuth flow है जिसे HTTP transport natively support करता है। एक व्यावहारिक न्यूनतम उपाय proxy पर check किया जाने वाला shared bearer token है — यह सस्ता है और drive-by attacks को पूरी तरह रोकता है:
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...
}openssl rand -hex 32 का उपयोग करके token generate करें, और server को बिना किसी authentication के 0.0.0.0 पर कभी भी bind न करें। Client फिर token को header के रूप में भेजता है। Claude Code में:
claude mcp add --scope project --transport http ops-tools https://mcp.example.com/mcp \
--header 'Authorization: Bearer ${MCP_TOKEN}'अपने shell में MCP_TOKEN सेट करें ताकि secret कभी भी .mcp.json में plaintext के रूप में न जाए — Claude Code read time पर environment से ${MCP_TOKEN} को expand करता है।
Step 6: MCP Inspector के साथ debug करें
जब server सही से काम न करे, तो agent के अंदर से अंदाज़ा न लगाएँ — सीधे Inspector का उपयोग करें, जो कि official web-based test client है। stdio server के लिए, वही command दें जो agent चलाता है:
npx @modelcontextprotocol/inspector \
npx -y @modelcontextprotocol/server-filesystem /tmpयह http://localhost:6274 पर एक UI शुरू करता है (recent versions में MCP_PROXY_AUTH_TOKEN query string के साथ एक URL मिलता है — उसी exact link का उपयोग करें वरना UI reject कर देगा) और 6277 पर एक proxy शुरू करता है। Connect पर क्लिक करें, फिर List Tools चुनें, और फिर real arguments के साथ Call Tool चुनें। यदि Inspector में यह काम करता है लेकिन agent में fail हो जाता है, तो bug आपके client config में है, server में नहीं। Remote HTTP server के लिए, Streamable HTTP transport चुनें, https://mcp.example.com/mcp दर्ज करें, Authorization header जोड़ें, और connect करें — agent के शामिल होने से पहले auth और proxy को verify करने का यह सबसे तेज़ तरीका है।
Servers को अपडेट रखना
MCP तेज़ी से बदलता है, इसलिए एक schedule पर patch करें। npx -y के साथ launch किए गए Node servers हर spawn पर latest version fetch करते हैं। यह सुविधाजनक है लेकिन non-reproducible है; जिस version का आपने test किया है, उसे pin करें — इसे npm view @modelcontextprotocol/server-filesystem version से पढ़ें और .mcp.json (@modelcontextprotocol/server-filesystem@<version>) में package name के साथ जोड़ें — जब server महत्वपूर्ण हो जाए, तो इसे जानबूझकर bump करें। systemd के अंतर्गत Python servers sudo -H -u mcp /opt/mcp-ops/.venv/bin/pip install -U "mcp[cli]" और उसके बाद sudo systemctl restart mcp-ops के साथ update होते हैं। upgrade करते समय अपने SDK के spec revision पर ध्यान दें — SSE-to-Streamable-HTTP boundary के पार जाने से वह transport बदल सकता है जिसे आपके clients को request करना होगा।
Failure modes, with the strings you will see
The agent shows the server failed. claude mcp list prints ✗ Failed to connect, aur TUI MCP server 'filesystem' failed to start report karta hai. claude --debug run karein, aapko aam taur par Error: spawn npx ENOENT dikhega — command agent ke PATH mein nahi hai. Runtime missing hai ya galat location par hai: Node installed nahi hai, npx absent hai, ya bare name se Python virtualenv ko reference kiya gaya hai. Command ko absolute path par fix karein ya runtime install karein, phir reconnect karein.
A stdio server connects, then instantly drops. Client ek JSON parse error log karta hai — jaise ki Unexpected token 'S', "Server sta"... is not valid JSON ya Failed to parse message. Iska karan hamesha ek hi hota hai: server ne stdout par ek log line likhi hai. stdio par, stdout hi JSON-RPC channel hota hai, isliye koi bhi extra text stream ko corrupt kar deta hai aur handshake fail ho jata hai. Node mein, console.log stdout par jata hai — console.error ka upyog karein. Python mein, bare print() stdout par jata hai — logs ko sys.stderr ke liye configured logging ke saath likhein, ya file=sys.stderr pass karein. Niyam nishchit hai: stdio par, stdout par sirf JSON-RPC hona chahiye, aur human-readable text stderr par hona chahiye.
A remote server times out or closes mid-handshake. Client MCP error -32000: Connection closed ke saath fail hota hai, ya Inspector Connect par hang ho jata hai aur tools list nahi karta. nginx ke peeche, yeh buffering ki wajah se hota hai: proxy SSE stream ko flush karne ke bajaye hold kar leta hai, isliye client us response ka intezar karta hai jo kabhi nahi aata. location mein proxy_buffering off; (aur Step 4 mein diya gaya baaki block) add karein. Public URL par curl -N ke saath confirm karein — aapko event data incrementally milna chahiye, ek saath end mein nahi.
Auth is rejected. Client Error POSTing to endpoint (HTTP 401) ya seedhe 401 Unauthorized report karta hai. Ya toh header missing hai, token galat hai, ya client dwara config read karte samay shell variable empty tha — yeh ek aam samasya hai, kyunki agar variable unset hai toh ${MCP_TOKEN} khali ho jata hai aur nginx ko bina value ke Bearer milta hai. Variable ko echo karein, header ko phir se add karein, aur verify karein ki bytes nginx if mein maujood token se match karte hain.
The service will not start under systemd. journalctl -u mcp-ops ModuleNotFoundError: No module named 'mcp' dikhata hai — ExecStart venv interpreter ke bajaye system Python ko point kar raha hai. Ya Address already in use — koi dusra process 8000 port ka upyog kar raha hai; ise sudo ss -ltnp | grep 8000 se dhundhein.
FAQ
MCP server क्या है?
यह एक program है जो Model Context Protocol का उपयोग करके AI client को tools और resources प्रदान करता है। यह JSON-RPC 2.0 का उपयोग करता है। AI model स्वयं tool को run नहीं करता है — वह अपने client से अनुरोध करता है, client MCP server को call करता है, और server उसे execute करके result वापस करता है। चूंकि यह protocol standard है, इसलिए एक server किसी भी compliant client के साथ काम कर सकता है, जैसे कि Claude Code, Claude Desktop, या Gemini CLI।
stdio और HTTP transport में क्या अंतर है?
stdio server को client द्वारा एक child process के रूप में launch किया जाता है। यह stdin/stdout के माध्यम से communicate करता है, इसलिए यह एक machine पर एक ही client के साथ चलता है। इसके लिए किसी network या authentication की आवश्यकता नहीं होती है। HTTP server एक long-running network service है जिसे एक साथ कई clients एक्सेस कर सकते हैं, इसलिए इसे TLS और authentication की आवश्यकता होती है। Local, single-user tools के लिए stdio का उपयोग करें; shared या persistent कार्यों के लिए HTTP (वर्तमान servers पर Streamable HTTP) का उपयोग करें।
मैं remote MCP server को कैसे secure करूँ?
मान लें कि यह आपके files, database, या shell तक tool access प्रदान करता है, इसलिए इसे कभी भी बिना authentication के expose न करें। सबसे अच्छा तरीका है कि इसे localhost तक ही सीमित रखें और SSH tunnel या private VPN के माध्यम से एक्सेस करें; यदि इसे public होना ही है, तो इसे reverse proxy के पीछे रखें जो bearer token या MCP OAuth flow को लागू करता हो। Token को openssl rand -hex 32 के साथ generate करें और बिना इनमें से किसी एक के server को 0.0.0.0 पर bind न करें।
यदि server start नहीं हो रहा है, तो मैं debug कैसे करूँ?
सबसे पहले claude mcp list — ✗ Failed to connect को spawn ... ENOENT के साथ check करें। इसका अर्थ है कि command या runtime मौजूद नहीं है, इसलिए path ठीक करें या उसे install करें। यदि connection बनता है और फिर JSON parse error के साथ disconnect हो जाता है, तो इसका मतलब है कि server stdout पर log कर रहा है जिससे JSON-RPC stream corrupt हो रही है; सभी logging को stderr पर move करें। अन्य किसी भी समस्या के लिए, exact command को MCP Inspector के अंतर्गत चलाएं। यह server को isolation में चलाता है ताकि आप server bug और client-config bug के बीच अंतर कर सकें।