SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-28

วิธีติดตั้ง MCP Server บน VPS สำหรับ AI Coding Agents

เรียนรู้วิธีรัน Model Context Protocol บน VPS ทั้งรูปแบบ stdio และ HTTP รองรับ systemd, TLS และการทำ Auth เพื่อให้ AI Agents ใช้งานเครื่องมือได้อย่างปลอดภัยและมีประสิทธิภาพ

สิ่งที่คุณกำลังสร้าง

การตั้งค่า MCP สองรูปแบบบน VPS เดียวกัน รูปแบบแรกคือเซิร์ฟเวอร์แบบ stdio ซึ่งเป็นเครื่องมือสำหรับจัดการระบบไฟล์หรือฐานข้อมูล โดย Claude Code จะเรียกใช้งานในฐานะ child process และสื่อสารผ่าน pipe รูปแบบที่สองคือเซิร์ฟเวอร์แบบ remote HTTP ซึ่งทำงานเป็น network service ระยะยาวภายใต้ systemd และ nginx reverse proxy ที่มี TLS ทำให้ client ของ MCP ใดก็ตามที่คุณกำหนดค่าไว้สามารถเข้าถึงได้ การติดตั้งทั้งสองรูปแบบนั้นใช้ทรัพยากรน้อย คู่มือนี้จะเน้นไปที่สองประเด็นสำคัญที่มักก่อให้เกิดปัญหา คือการรักษาความสะอาดของ JSON-RPC stream และการไม่เปิดเผย endpoint ของเครื่องมือที่ไม่มีการตรวจสอบสิทธิ์ไว้บนอินเทอร์เน็ตสาธารณะโดยเด็ดขาด

MCP คืออะไรกันแน่

Model Context Protocol เป็นมาตรฐานที่ช่วยให้ AI client ไม่ว่าจะเป็น Claude Code, Claude Desktop, Gemini CLI บน VPS หรือสคริปต์ที่คุณเขียนขึ้นเอง สามารถเรียกใช้เครื่องมือภายนอกและอ่านทรัพยากรภายนอกได้ ตัวโมเดลเองไม่ได้เป็นผู้รันคำสั่งใดๆ แต่จะส่งคำขอไปยัง client จากนั้น client จะสื่อสารผ่าน JSON-RPC 2.0 ไปยัง MCP server เพื่อให้ server เป็นผู้รันเครื่องมือและส่งผลลัพธ์กลับมา ตัว client นี้เองคือสิ่งที่ผู้คนมักเรียกว่า agent harness ซึ่งหมายถึงลูปที่ครอบตัวโมเดลไว้ ทำหน้าที่จัดการรายการเครื่องมือ การตรวจสอบสิทธิ์ และสถานะของเซสชัน โดย MCP เป็นเพียงวิธีการที่คุณใช้ขยายขีดความสามารถด้านเครื่องมือของมันเท่านั้น ด้วยความเป็นมาตรฐานเดียว server ที่คุณเขียนขึ้นเพียงครั้งเดียวจึงสามารถทำงานร่วมกับทุก client ที่รองรับ MCP ได้ หากแนวคิดการแยกส่วนนี้ยังเป็นเรื่องใหม่สำหรับคุณ โดยเฉพาะในประเด็นที่ว่าโมเดลตัดสินใจเลือกใช้เครื่องมือได้อย่างไร เส้นทางการเรียนรู้พื้นฐานของ agent เป็นสิ่งที่คุ้มค่าที่จะใช้เวลาศึกษาประมาณหนึ่งชั่วโมง ก่อนที่คุณจะอนุญาตให้ server เหล่านี้เข้าถึงข้อมูลรับรองจริง

โปรโตคอลนี้มีรูปแบบการรับส่งข้อมูล (transport) สองแบบ ซึ่งเนื้อหาที่เหลือในคู่มือนี้จะแบ่งตามรูปแบบดังกล่าว:

  • stdio: client จะเรียกใช้ server ในฐานะ child process และแลกเปลี่ยนข้อความ JSON-RPC ที่คั่นด้วยบรรทัดใหม่ผ่าน standard input และ standard output โดยไม่มีการใช้เครือข่าย ไม่มีพอร์ต และไม่มีการตรวจสอบสิทธิ์ ขอบเขตความปลอดภัยจะอยู่ที่ตัว process เอง เครื่องมือที่ใช้งานภายในเครื่องส่วนใหญ่ใช้วิธีนี้
  • Streamable HTTP (และรูปแบบเดิมอย่าง HTTP+SSE): server จะทำงานเป็นเว็บเซอร์วิสที่รันอยู่ตลอดเวลา client จะเชื่อมต่อผ่าน HTTP และ server สามารถส่งการตอบกลับแบบสตรีมผ่าน Server-Sent Events ได้ วิธีนี้เหมาะสำหรับการแชร์ server หนึ่งตัวให้กับหลาย client หรือการรันเครื่องมือที่จำเป็นต้องทำงานอยู่บนเซิร์ฟเวอร์ตลอดเวลา

ให้เลือกใช้ stdio เมื่อเครื่องมือดังกล่าวใช้งานบนเครื่องเดียวและโดยผู้ใช้คนเดียว และเลือกใช้ HTTP เมื่อเป็นบริการที่ต้องใช้งานร่วมกันหลายคน

ข้อกำหนดเบื้องต้นและข้อควรระวังที่ต้องทราบ

สมมติว่าคุณใช้ Ubuntu 24.04 KVM VPS ใหม่ที่เข้าถึงด้วยสิทธิ์ root หรือ sudo นอกจากนี้ยังมีประเด็นที่ควรทราบดังนี้:

  • Runtime สำหรับรันเซิร์ฟเวอร์: เซิร์ฟเวอร์อ้างอิงส่วนใหญ่เขียนด้วย Node หรือ Python โดย Ubuntu 24.04 มาพร้อมกับ Node 18 แต่แพ็กเกจ MCP ปัจจุบันหลายรายการต้องการ Node 20 หรือใหม่กว่า ดังนั้นควรติดตั้งเวอร์ชัน LTS ปัจจุบันจาก NodeSource หรือ nvm แทนการเชื่อถือ apt ส่วน Python 3.12 นั้นมีติดตั้งมาให้พร้อมใช้งานอยู่แล้ว
  • โดเมนและ DNS A record: จำเป็นเฉพาะสำหรับเซิร์ฟเวอร์ HTTP ระยะไกลเท่านั้น เนื่องจาก TLS ต้องการชื่อที่ชี้มายัง VPS นี้ ส่วนตัวอย่าง stdio ไม่จำเป็นต้องใช้ DNS เลย
  • RAM ขนาด 512 MB ก็เพียงพอ: เซิร์ฟเวอร์ MCP เป็นกระบวนการ JSON-RPC ขนาดเล็ก การใช้หน่วยความจำจะขึ้นอยู่กับสิ่งที่เครื่องมือของคุณเรียกใช้งาน (เช่น ไดรเวอร์ฐานข้อมูล หรือแคชไฟล์) ไม่ใช่ตัวโปรโตคอลเอง
  • ข้อกำหนด (spec) ยังอยู่ในช่วงเริ่มต้นและมีการเปลี่ยนแปลง: การแก้ไขเมื่อวันที่ 2025-03-26 ได้เปลี่ยนจากการใช้ HTTP+SSE มาเป็น Streamable HTTP และระบุว่า SSE เป็นสิ่งที่เลิกใช้แล้ว (deprecated) อย่างไรก็ตาม SSE ยังคงทำงานได้และเซิร์ฟเวอร์จำนวนมากยังคงรองรับอยู่ ดังนั้นให้ถือว่าการกำหนด transport เป็นสิ่งที่ควรตรวจสอบซ้ำกับบันทึกประจำรุ่น (release notes) ของเซิร์ฟเวอร์ แทนที่จะยึดถือเป็นกฎตายตัว

ขั้นตอนที่ 1: เชื่อมต่อ stdio server เข้ากับ Claude Code

เริ่มต้นด้วย filesystem server ซึ่งเป็นเครื่องมืออย่างเป็นทางการ มีการดูแลรักษาอย่างต่อเนื่อง และต้องการเพียง Node เท่านั้น คำสั่งเดียวด้านล่างนี้จะลงทะเบียนเครื่องมือดังกล่าวเข้ากับ Claude Code และกำหนดขอบเขตไว้ที่โปรเจกต์ปัจจุบัน เพื่อให้ไฟล์ที่ได้สามารถ commit ลงระบบควบคุมเวอร์ชันได้:

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

ตัวคั่น -- มีความสำคัญ: ทุกอย่างที่อยู่หลังจากนี้คือคำสั่งที่ Claude Code จะเรียกใช้งาน ไม่ใช่ flag สำหรับ Claude Code คำสั่งนี้จะเขียนไฟล์ .mcp.json ไว้ที่ root ของโปรเจกต์:

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

ในขณะนี้ยังไม่มีกระบวนการใดทำงานอยู่ เมื่อคุณเริ่มใช้งาน Claude Code ในไดเรกทอรีนี้ในครั้งถัดไป ตัว agent จะอ่านไฟล์ .mcp.json, เรียกใช้ npx -y @modelcontextprotocol/server-filesystem ... เป็น child process และดำเนินการ MCP handshake ผ่าน stdin/stdout ของกระบวนการนั้น ตรวจสอบความเรียบร้อยด้วยคำสั่ง:

claude mcp list

หาก server ทำงานปกติจะแสดงคำสั่งและเครื่องหมายถูกสีเขียว filesystem: npx -y @modelcontextprotocol/server-filesystem ... - ✓ Connected ภายใน session คำสั่ง slash /mcp จะแสดงรายการเครื่องมือที่ server เปิดให้ใช้งาน (read_file, write_file, list_directory) และตอนนี้ตัว agent สามารถเรียกใช้เครื่องมือเหล่านั้นบน path ที่คุณอนุญาตได้แล้ว สำหรับเครื่องมือฐานข้อมูลจะมีลักษณะการทำงานแบบเดียวกัน ให้เปลี่ยน package และส่ง connection string เป็นอาร์กิวเมนต์สุดท้าย แต่ควรตรวจสอบชื่อ package ปัจจุบันจาก repository ของ server นั้นๆ เนื่องจาก reference Postgres server มีการเปลี่ยนผู้ดูแลมาแล้วหลายครั้ง

นี่คือหัวใจสำคัญของการรัน agent บนเครื่อง: Claude Code session ทำงานอยู่บน VPS ภายใน tmux และ stdio server ต่างๆ ก็ทำงานอยู่ข้างกันโดยสามารถเข้าถึงไฟล์โปรเจกต์และบริการภายในเครื่องได้โดยตรงโดยไม่ต้องผ่าน network round-trip เมื่อ agent มีสิทธิ์ write_file และ read_file แล้ว ควรจับคู่ความสามารถนี้เข้ากับ ทักษะที่ผลักดันให้ agent เลือกการเปลี่ยนแปลงที่เล็กที่สุดที่ใช้งานได้ เพราะเครื่องมือจัดการไฟล์ทำให้การเขียนโค้ดใหม่ทั้งหมดมีต้นทุนต่ำพอๆ กับการแก้ไขเพียงสองบรรทัด การเชื่อมต่อนี้ยังขยายไปไกลกว่าไฟล์ในเครื่อง: หากคุณรัน search engine บน VPS อยู่แล้ว คุณสามารถ ส่ง instance ของ SearXNG ของคุณให้ agent เป็นเครื่องมือค้นหา ซึ่งจะช่วยให้การสืบค้นยังคงอยู่ภายในเครื่องของคุณ แต่ดึงข้อความจากหน้าเว็บที่ไม่น่าเชื่อถือเข้ามาใน context เพื่อให้ agent ดำเนินการต่อไปได้

ขั้นตอนที่ 2: การสร้าง HTTP server ระยะไกล

Stdio server จะหยุดทำงานเมื่อ process แม่สิ้นสุดลง และจะถูกเรียกใช้งานหนึ่งครั้งต่อหนึ่ง client ดังนั้นหากคุณรัน Claude Code สอง session บนเครื่องเดียวกันที่ส่งงานให้กัน แต่ละ session จะได้รับสำเนาของเครื่องมือส่วนตัวแยกกัน หากคุณต้องการเครื่องมือที่ทำงานตลอดเวลาสำหรับทุก client เช่น เครื่องมือจัดการระบบ (ops tool) ส่วนกลาง, database gateway หรือสิ่งที่ทั้งแล็ปท็อปของคุณและ CI เรียกใช้งาน คุณจำเป็นต้องใช้ HTTP transport และบริการจริง นี่คือตัวอย่าง Python server ขนาดเล็กที่ใช้ SDK อย่างเป็นทางการ โดยเปิดใช้งานเครื่องมือหนึ่งรายการ:

# /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" เซิร์ฟเวอร์นี้จะผูกกับ localhost เท่านั้น ไม่มีสิ่งใดจากภายนอกเครื่องที่สามารถเข้าถึงได้โดยตรง ซึ่งเป็นสิ่งที่คุณต้องการก่อนที่จะมีการตั้งค่าการยืนยันตัวตน ให้ติดตั้งเซิร์ฟเวอร์นี้ใน virtualenv ของตนเองเพื่อให้ systemd มีเส้นทางของ interpreter ที่เสถียร:

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

ขั้นตอนที่ 3: รักษาการทำงานด้วย systemd

เครื่องมือที่หยุดทำงานในขณะที่เอเจนต์พยายามเรียกใช้งานนั้นแย่ยิ่งกว่าการไม่มีเครื่องมือเสียอีก สิ่งนี้มีความสำคัญอย่างยิ่งเมื่อตัวไคลเอนต์เองเป็นกระบวนการที่ทำงานต่อเนื่องยาวนาน: เอเจนต์ที่ทำงานตลอดเวลาซึ่งรักษาหน่วยความจำและตารางเวลาไว้แม้ผ่านการรีบูต จะเรียกใช้เครื่องมือเหล่านี้ตามกำหนดการโดยไม่มีผู้ดูแล ดังนั้นเซิร์ฟเวอร์จึงต้องกลับมาทำงานได้ด้วยตัวเอง ให้เขียนไฟล์ /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

การระบุพาธแบบสัมบูรณ์ (absolute path) ไปยัง Python ใน venv ที่ ExecStart เป็นสิ่งที่จำเป็น ต้องชี้ไปยัง /usr/bin/python3 เพื่อให้กระบวนการเริ่มต้นด้วย ModuleNotFoundError: No module named 'mcp' เนื่องจากตัวแปลภาษาของระบบไม่เคยเห็น pip install ของคุณ ให้เปิดใช้งานและตรวจสอบ:

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 ควรแสดงผลเป็น active (running) ส่วน curl จะตอบกลับมาเป็น HTTP/1.1 400 Bad Request พร้อมข้อความแสดงข้อผิดพลาด JSON-RPC ในเนื้อหา เนื่องจากคำขอไม่มีเซสชันและไม่มี payload JSON ที่ถูกต้อง ซึ่งนั่นคือสิ่งที่คุณต้องการอย่างแท้จริง: มันพิสูจน์ว่าพอร์ตมีการตอบสนองและสื่อสารด้วยโปรโตคอลที่ถูกต้อง หากได้รับ Connection refused หรือการตอบกลับที่ว่างเปล่า แสดงว่ากระบวนการไม่ได้ผูกอยู่กับพอร์ตที่คุณคิดไว้ ให้ตรวจสอบ journalctl -u mcp-ops -n 50

ขั้นตอนที่ 4: ติดตั้ง TLS และ reverse proxy ไว้ด้านหน้า

เซิร์ฟเวอร์จะฟังคำขอที่ localhost หากต้องการเข้าถึงจากภายนอก ให้ทำ TLS termination ที่ nginx แล้วทำ proxy ส่งต่อเข้ามาภายใน ให้ติดตั้ง nginx และขอใบรับรองด้วย Certbot และ Let's Encrypt บน nginx จากนั้นเขียน location block ส่วนที่สำคัญที่สุดคือการปิดใช้งาน buffering เนื่องจากพฤติกรรมปกติของ nginx คือการพักข้อมูลตอบกลับไว้จนกว่าจะเสร็จสิ้น ซึ่งจะทำให้ 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 หากคุณรันกลุ่มคอนเทนเนอร์อยู่แล้ว งานเดียวกันนี้สามารถทำได้โดย Traefik reverse proxy พร้อม TLS อัตโนมัติ ซึ่งจะออกใบรับรองและกำหนดเส้นทางตามชื่อโฮสต์ คุณเพียงแค่เพิ่ม label ให้กับคอนเทนเนอร์ MCP ไม่ว่าจะใช้วิธีใด reverse proxy จะกลายเป็นสิ่งเดียวที่อยู่บนพอร์ตสาธารณะ และมันจะชี้ไปยังบริการที่คุณยังไม่ได้รักษาความปลอดภัย โปรดแก้ไขส่วนนี้ก่อนที่จะนำ URL ไปลงทะเบียนที่ใดก็ตาม

ขั้นตอนที่ 5: กฎความปลอดภัยที่สำคัญที่สุดในหัวข้อนี้

ห้ามเปิดเผย MCP endpoint ที่ไม่มีการตรวจสอบสิทธิ์โดยเด็ดขาด MCP server ไม่ใช่ API แบบอ่านได้อย่างเดียว แต่มันให้สิทธิ์ในการเข้าถึงเครื่องมือ ไฟล์ ฐานข้อมูล และบางครั้งรวมถึง shell ของคุณ การเปิด /mcp ไว้บนอินเทอร์เน็ตสาธารณะเปรียบเสมือนการเปิดโอกาสให้คนแปลกหน้ามีสิทธิ์เข้าถึงเท่ากับ AI agent ของคุณ พวกเขาสามารถดูรายการเครื่องมือและเรียกใช้งานได้ทันที ให้ปฏิบัติต่อมันเหมือนกับ admin socket ที่ไม่มีการตรวจสอบสิทธิ์ เพราะนั่นคือสิ่งที่มันเป็น ความเสียหายจากการถูกขโมย token ขึ้นอยู่กับเซิร์ฟเวอร์ที่อยู่เบื้องหลังด้วย เช่น MCP server แบบอ่านได้อย่างเดียวที่มาพร้อมกับ openGym workout tracker จะส่งคืนได้เพียงข้อมูลการฝึกซ้อมเท่านั้น ในขณะที่เครื่องมือจัดการไฟล์หรือ shell จะทำให้ผู้บุกรุกเข้าควบคุมเครื่องได้ทั้งเครื่อง

มีแนวทางการป้องกัน 3 วิธี เรียงตามลำดับความสำคัญ:

  1. อย่าเผยแพร่สู่สาธารณะ ให้รันเซิร์ฟเวอร์บน 127.0.0.1 และเข้าถึงจากแล็ปท็อปของคุณผ่าน SSH tunnel ด้วยคำสั่ง ssh -L 8000:127.0.0.1:8000 matt@vps จากนั้นชี้ client ไปที่ http://127.0.0.1:8000/mcp วิธีนี้จะไม่มีการเปิดเผยข้อมูลใดๆ ออกไป
  2. วางไว้บนเครือข่ายส่วนตัว ผูก tunnel address ไว้กับ WireGuard VPN ที่โฮสต์เอง และอนุญาตให้เฉพาะผู้ที่อยู่ใน VPN เท่านั้นที่เข้าถึงได้ อินเทอร์เน็ตสาธารณะจะมองเห็นเพียงพอร์ตที่ปิดอยู่
  3. หากจำเป็นต้องเปิดเผยต่อสาธารณะ ต้องมีการใช้ token วิธีที่ถูกต้องคือการใช้ MCP OAuth flow ซึ่ง HTTP transport รองรับโดยตรง วิธีที่ง่ายและได้ผลคือการใช้ shared bearer token ที่ตรวจสอบผ่าน proxy ซึ่งทำได้ง่ายและป้องกันการโจมตีแบบสุ่มได้ทันที:
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...
}

สร้าง token ด้วย openssl rand -hex 32 และห้ามผูกเซิร์ฟเวอร์เข้ากับ 0.0.0.0 โดยไม่มีมาตรการเหล่านี้ป้องกันไว้เด็ดขาด จากนั้น client จะส่ง token ในรูปแบบ header สำหรับ Claude Code ให้ทำดังนี้:

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

ตั้งค่า MCP_TOKEN ใน shell ของคุณ เพื่อไม่ให้ secret ถูกบันทึกใน .mcp.json แบบ plaintext โดย Claude Code จะอ่านค่า ${MCP_TOKEN} จาก environment ในขณะที่ทำงาน

มาตรการป้องกันข้างต้นทั้งหมดเป็นการปกป้องที่ตัว endpoint ไม่ใช่ที่ตัว agent ซึ่งถือเป็นอีกส่วนหนึ่งของปัญหา หาก client ของคุณคือ DeepSeek Harness ให้ใช้ ปลั๊กอินที่จำกัดสิทธิ์การเรียกใช้เครื่องมือของ agent และสแกนผลลัพธ์จากเครื่องมือเพื่อตรวจหาคำสั่งที่ถูกแทรกเข้ามา เพื่อป้องกันในส่วนนี้

ขั้นตอนที่ 6: การดีบั๊กด้วย MCP Inspector

เมื่อเซิร์ฟเวอร์ทำงานผิดปกติ อย่าเดาสาเหตุจากภายในตัว agent ให้ทดสอบโดยตรงด้วย Inspector ซึ่งเป็นไคลเอนต์ทดสอบบนเว็บอย่างเป็นทางการ สำหรับ stdio server ให้ใช้คำสั่งเดียวกับที่ agent เรียกใช้งาน:

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

ระบบจะเริ่ม UI บน http://localhost:6274 (เวอร์ชันล่าสุดจะแสดง URL พร้อม query string MCP_PROXY_AUTH_TOKEN ให้ใช้ลิงก์นั้นโดยตรง มิฉะนั้น UI จะปฏิเสธการเชื่อมต่อ) และเริ่ม proxy บนพอร์ต 6277 ให้คลิก Connect จากนั้นเลือก List Tools และทดสอบด้วย Call Tool โดยใช้ argument จริง หากการทำงานใน Inspector สำเร็จแต่ล้มเหลวใน agent แสดงว่าข้อผิดพลาดอยู่ที่การตั้งค่าไคลเอนต์ของคุณ ไม่ใช่ที่ตัวเซิร์ฟเวอร์ สำหรับ remote HTTP server ให้เลือก transport แบบ Streamable HTTP ระบุ https://mcp.example.com/mcp เพิ่ม header Authorization แล้วทำการเชื่อมต่อ นี่เป็นวิธีที่รวดเร็วที่สุดในการตรวจสอบว่าการยืนยันตัวตน (auth) และ proxy ทำงานถูกต้องก่อนที่จะเริ่มใช้งานผ่าน agent

การอัปเดตเซิร์ฟเวอร์ให้เป็นปัจจุบัน

MCP มีการพัฒนาอย่างรวดเร็ว ดังนั้นควรวางแผนการแพตช์ตามกำหนดเวลา เซิร์ฟเวอร์ Node ที่เปิดใช้งานด้วย npx -y จะดึงเวอร์ชันล่าสุดมาใช้ทุกครั้งที่เริ่มทำงาน ซึ่งสะดวกแต่ไม่สามารถทำซ้ำผลลัพธ์ได้เหมือนเดิม คุณควรระบุเวอร์ชันที่ผ่านการทดสอบแล้วให้ชัดเจน โดยอ่านค่าจาก npm view @modelcontextprotocol/server-filesystem version แล้วนำไปต่อท้ายชื่อแพ็กเกจใน .mcp.json (@modelcontextprotocol/server-filesystem@<version>) เมื่อเซิร์ฟเวอร์เริ่มมีความสำคัญ และค่อยปรับเวอร์ชันอย่างตั้งใจ สำหรับเซิร์ฟเวอร์ Python ที่รันภายใต้ systemd ให้ทำการอัปเดตด้วย sudo -H -u mcp /opt/mcp-ops/.venv/bin/pip install -U "mcp[cli]" ตามด้วย sudo systemctl restart mcp-ops เมื่อคุณทำการอัปเกรด ให้ตรวจสอบการแก้ไขข้อมูลจำเพาะ (spec revision) ที่ SDK ของคุณรองรับ เนื่องจากการข้ามผ่านขอบเขตจาก SSE ไปยัง Streamable-HTTP อาจทำให้รูปแบบการรับส่งข้อมูล (transport) ที่ไคลเอนต์ต้องร้องขอเปลี่ยนแปลงไป

รูปแบบความล้มเหลวและข้อความที่คุณจะพบ

เอเจนต์แสดงสถานะว่าเซิร์ฟเวอร์ล้มเหลว claude mcp list จะแสดงผล ✗ Failed to connect และ TUI จะรายงาน MCP server 'filesystem' failed to start ให้รันคำสั่ง claude --debug ซึ่งโดยปกติคุณจะเห็น Error: spawn npx ENOENT นั่นหมายความว่าคำสั่งดังกล่าวไม่อยู่ใน PATH ของเอเจนต์ หรือ runtime หายไปหรือไม่ตรงกับตำแหน่งที่เอเจนต์ตรวจสอบ เช่น ไม่ได้ติดตั้ง Node, ไม่มี npx หรือมีการอ้างอิง Python ใน virtualenv ด้วยชื่อเปล่าๆ ให้แก้ไขคำสั่งเป็น absolute path หรือติดตั้ง runtime ให้เรียบร้อย แล้วจึงเชื่อมต่อใหม่

เซิร์ฟเวอร์แบบ stdio เชื่อมต่อแล้วตัดการเชื่อมต่อทันที ฝั่งไคลเอนต์จะบันทึก log ว่าเกิด JSON parse error เช่น Unexpected token 'S', "Server sta"... is not valid JSON หรือ Failed to parse message สาเหตุเกิดจากเซิร์ฟเวอร์เขียนข้อความ log ลงใน stdout เสมอ ในการสื่อสารผ่าน stdio นั้น stdout คือช่องทางของ JSON-RPC ดังนั้นข้อความใดๆ ที่หลุดเข้าไปจะทำให้ stream เสียหายและ handshake ล้มเหลว ใน Node คำสั่ง console.log จะส่งผลไปยัง stdout ให้ใช้ console.error แทน ใน Python คำสั่ง print() แบบปกติจะส่งผลไปยัง stdout ให้เขียน log ด้วย logging ที่กำหนดค่าเป็น sys.stderr หรือส่งผ่าน file=sys.stderr กฎนี้เป็นข้อบังคับเด็ดขาด: บน stdio ต้องมีเฉพาะ JSON-RPC บน stdout เท่านั้น ส่วนข้อความสำหรับมนุษย์ต้องส่งไปที่ stderr ทั้งหมด

เซิร์ฟเวอร์ระยะไกลหมดเวลาหรือปิดการเชื่อมต่อระหว่าง handshake ไคลเอนต์จะล้มเหลวด้วยข้อความ MCP error -32000: Connection closed หรือ Inspector ค้างอยู่ที่สถานะ Connect และไม่แสดงรายการเครื่องมือ สาเหตุที่อยู่เบื้องหลัง nginx คือการทำ buffering: พร็อกซีจะกักเก็บ SSE stream ไว้แทนที่จะส่งออกทันที ทำให้ไคลเอนต์รอการตอบกลับที่ไม่มาถึง ให้เพิ่ม proxy_buffering off; (และส่วนที่เหลือของบล็อกในขั้นตอนที่ 4) ลงใน location ตรวจสอบด้วยคำสั่ง curl -N กับ URL สาธารณะ คุณควรเห็นข้อมูล event ทยอยเข้ามาทีละส่วน ไม่ใช่มาพร้อมกันทั้งหมดในตอนท้าย

การยืนยันตัวตนถูกปฏิเสธ ไคลเอนต์รายงาน Error POSTing to endpoint (HTTP 401) หรือแจ้งตรงๆ ว่า 401 Unauthorized อาจเป็นเพราะ header หายไป, token ไม่ถูกต้อง หรือตัวแปร shell ว่างเปล่าในขณะที่ไคลเอนต์อ่านค่า config ซึ่งเป็นปัญหาที่พบบ่อย เนื่องจาก ${MCP_TOKEN} จะขยายผลเป็นค่าว่างหากตัวแปรไม่ได้ถูกตั้งค่าไว้ และ nginx จะเห็นเป็น Bearer โดยไม่มีค่า ให้ตรวจสอบตัวแปรด้วย echo, เพิ่ม header อีกครั้ง และตรวจสอบว่าไบต์ของ token ตรงกับที่ระบุใน if ของ nginx อย่างถูกต้อง

บริการไม่สามารถเริ่มทำงานภายใต้ systemd journalctl -u mcp-ops แสดงผล ModuleNotFoundError: No module named 'mcp' โดยที่ ExecStart ชี้ไปยัง system Python แทนที่จะเป็น venv interpreter หรืออาจเกิดจาก Address already in use ซึ่งมีกระบวนการอื่นใช้งานพอร์ต 8000 อยู่ ให้ค้นหาด้วยคำสั่ง sudo ss -ltnp | grep 8000

FAQ

MCP server คืออะไรกันแน่?

มันคือโปรแกรมที่เปิดเผยเครื่องมือและทรัพยากรให้กับ AI client ผ่านทาง Model Context Protocol โดยใช้ JSON-RPC 2.0 ตัวโมเดล AI ไม่ได้รันเครื่องมือด้วยตัวเอง แต่จะร้องขอไปยัง client จากนั้น client จะเรียกใช้งาน MCP server และ server จะเป็นผู้ดำเนินการพร้อมส่งผลลัพธ์กลับมา เนื่องจากโปรโตคอลนี้เป็นมาตรฐาน server หนึ่งตัวจึงสามารถทำงานร่วมกับ client ใดก็ได้ที่รองรับ ไม่ว่าจะเป็น Claude Code, Claude Desktop หรือ Gemini CLI

stdio และ HTTP transport แตกต่างกันอย่างไร?

stdio server จะถูกเปิดโดย client ในฐานะ child process และสื่อสารผ่าน stdin/stdout จึงมีอายุการใช้งานผูกติดกับ client หนึ่งตัวบนเครื่องเดียวโดยไม่ต้องใช้เครือข่ายหรือการยืนยันตัวตน ส่วน HTTP server เป็นบริการเครือข่ายที่ทำงานต่อเนื่องและรองรับหลาย client พร้อมกัน จึงจำเป็นต้องใช้ TLS และการยืนยันตัวตน ให้ใช้ stdio สำหรับเครื่องมือที่ใช้งานเฉพาะที่และผู้ใช้คนเดียว และใช้ HTTP (หรือ Streamable HTTP ใน server ปัจจุบัน) สำหรับบริการที่ต้องใช้งานร่วมกันหรือต้องการความต่อเนื่อง

ฉันจะรักษาความปลอดภัยให้กับ remote MCP server ได้อย่างไร?

ให้สันนิษฐานว่ามันมีสิทธิ์เข้าถึงไฟล์ ฐานข้อมูล หรือ shell ของคุณ ดังนั้นห้ามเปิดใช้งานโดยไม่มีการยืนยันตัวตนเด็ดขาด วิธีที่ดีที่สุดคือผูกไว้กับ localhost และเข้าถึงผ่าน SSH tunnel หรือ private VPN เท่านั้น หากจำเป็นต้องเปิดเป็นสาธารณะ ให้วางไว้หลัง reverse proxy ที่บังคับใช้ bearer token หรือ MCP OAuth flow ให้สร้าง token ด้วย openssl rand -hex 32 และห้ามผูก server เข้ากับ 0.0.0.0 โดยไม่มีมาตรการเหล่านี้ป้องกันอยู่ด้านหน้า

ฉันจะแก้ไขปัญหา server ที่ไม่ยอมเริ่มทำงานได้อย่างไร?

ขั้นแรกให้ตรวจสอบ claude mcp list และ ✗ Failed to connect หากพบ spawn ... ENOENT แสดงว่าคำสั่งหรือ runtime นั้นหายไป ให้แก้ไข path หรือติดตั้งให้เรียบร้อย หาก server เชื่อมต่อได้แต่หลุดพร้อมข้อความ JSON parse error แสดงว่า server กำลังเขียน log ลง stdout ซึ่งทำให้ stream ของ JSON-RPC เสียหาย ให้ย้ายการเขียน log ทั้งหมดไปที่ stderr แทน สำหรับปัญหาอื่น ให้รันคำสั่งนั้นโดยตรงผ่าน MCP Inspector ซึ่งจะช่วยรัน server ในสภาพแวดล้อมแยกต่างหาก เพื่อให้คุณแยกแยะได้ว่าปัญหาเกิดจากบั๊กของ server หรือการตั้งค่าของ client