Build MCP server แรกด้วย Python — 30 นาที จาก zero ถึง Claude เรียกใช้ได้
MCP = Model Context Protocol — open standard ของ Anthropic ที่ทำให้ Claude/Manus/GPT คุยกับ external tools ได้ ไม่รู้ว่า MCP คืออะไร? อ่าน why-mcp-changes-everything ก่อน
ที่จะสร้าง
MCP server ที่มี 3 tools:
count_words— นับคำในข้อความreverse_text— เรียงข้อความถอยหลังthai_greeting— ทักทายตามเวลา (เช้า/บ่าย/เย็น)
หลังเสร็จ Claude Desktop จะเรียก tools นี้ผ่านการคุยภาษาไทยได้
Prerequisites
- Python 3.10+ (
python3 --version) - pip + virtualenv
- Claude Desktop (claude.ai/download)
Step 1: Project setup (3 นาที)
mkdir my-mcp-server
cd my-mcp-server
python3 -m venv .venv
source .venv/bin/activate # Mac/Linux
# .venv\Scripts\activate # Windows
# ติดตั้ง dependencies
pip install mcp pydanticStep 2: เขียน server.py (10 นาที)
สร้างไฟล์ server.py:
"""
My first MCP server — 3 tools, Thai-friendly
"""
from datetime import datetime
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field, ConfigDict
mcp = FastMCP("my_first_mcp")
# ====================================
# Pydantic input models
# ====================================
class CountWordsInput(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
text: str = Field(
...,
description="ข้อความที่ต้องการนับคำ",
min_length=1,
max_length=10_000,
)
class ReverseTextInput(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
text: str = Field(..., min_length=1, max_length=1_000)
class ThaiGreetingInput(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
name: str = Field(..., min_length=1, max_length=100)
# ====================================
# Tools (with MCP annotations)
# ====================================
@mcp.tool(
name="count_words",
annotations={
"title": "นับจำนวนคำในข้อความ",
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": True,
"openWorldHint": False, # pure function, no external state
},
)
async def count_words(params: CountWordsInput) -> str:
"""นับจำนวนคำในข้อความ — แยกด้วย whitespace"""
n = len(params.text.split())
return f"📊 มี {n} คำในข้อความ"
@mcp.tool(
name="reverse_text",
annotations={
"title": "เรียงข้อความถอยหลัง",
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": True,
"openWorldHint": False,
},
)
async def reverse_text(params: ReverseTextInput) -> str:
"""เรียงข้อความถอยหลังจากท้ายไปต้น"""
reversed_text = params.text[::-1]
return f"🔄 {reversed_text}"
@mcp.tool(
name="thai_greeting",
annotations={
"title": "ทักทายภาษาไทยตามเวลา",
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": False, # ขึ้นกับเวลา ส่งซ้ำได้ผลต่าง
"openWorldHint": True, # ใช้ system clock = external state
},
)
async def thai_greeting(params: ThaiGreetingInput) -> str:
"""ทักทายภาษาไทยตามเวลาปัจจุบัน"""
hour = datetime.now().hour
if 5 <= hour < 12:
period = "เช้า"
emoji = "🌅"
elif 12 <= hour < 17:
period = "บ่าย"
emoji = "☀️"
elif 17 <= hour < 20:
period = "เย็น"
emoji = "🌇"
else:
period = "ค่ำ"
emoji = "🌙"
return f"{emoji} สวัสดีตอน{period}คุณ {params.name}"
# ====================================
# Run
# ====================================
if __name__ == "__main__":
mcp.run()Step 3: ทดสอบ standalone (1 นาที)
python server.pyถ้าไม่มี error แปลว่า OK — กด Ctrl+C ออก. Claude Desktop จะ run ให้อัตโนมัติ.
Step 4: เชื่อมเข้า Claude Desktop (5 นาที)
เปิดไฟล์ config:
| OS | path |
|---|---|
| Mac | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
เพิ่ม:
{
"mcpServers": {
"my_first_mcp": {
"command": "/full/path/to/.venv/bin/python",
"args": ["/full/path/to/server.py"]
}
}
}⚠️ ใช้ absolute path เท่านั้น — ~ หรือ relative ไม่ทำงาน
Restart Claude Desktop (Cmd+Q บน Mac, ไม่ใช่แค่ปิด window)
Step 5: ทดสอบกับ Claude (1 นาที)
ใน Claude Desktop พิมพ์:
คุณ: นับคำในประโยค 'สวัสดี ยินดีต้อนรับ NirvaDeploy'
>
Claude: (เรียก count_words) 📊 มี 3 คำในข้อความ
คุณ: เรียง 'NirvaDeploy' ถอยหลัง
>
Claude: (เรียก reverse_text) 🔄 yolpeDavriN
คุณ: ทักทายผม ชื่อ Tom
>
Claude: (เรียก thai_greeting) 🌇 สวัสดีตอนเย็นคุณ Tom
🎉 เสร็จ!
ทำต่อ — Deploy MCP server บน NirvaDeploy
MCP server ที่ run บน local ใช้ได้แค่กับ Claude Desktop ของคุณ. ถ้าต้องการให้ใช้ผ่าน network (Cursor, web client, ทีม) — deploy ขึ้นคลาวด์ผ่าน NirvaDeploy:
# server.py — เปลี่ยน transport จาก stdio เป็น SSE
if __name__ == "__main__":
mcp.run(transport="sse", port=int(os.environ.get("PORT", 8000)))จากนั้นใช้ Claude สั่ง:
คุณ: Deploy github.com/myorg/my-mcp-server บน NirvaDeploy
>
Claude: (เรียก NirvaDeploy's create_project_from_repo) ✅ สร้างโปรเจกต์ + service เรียบร้อย — service URL: my-mcp-svc.nirvadeploy.comClaude clients ที่ support remote MCP จะเรียกใช้ผ่าน URL นั้นได้
ทำอะไรได้อีก?
Tool ที่เรียก external API
import httpx
class WeatherInput(BaseModel):
model_config = ConfigDict(extra="forbid")
city: str = Field(..., description="ชื่อเมือง")
@mcp.tool(
name="weather",
annotations={
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": False,
"openWorldHint": True,
},
)
async def weather(params: WeatherInput) -> str:
async with httpx.AsyncClient() as client:
r = await client.get(f"https://wttr.in/{params.city}?format=%C+%t")
return f"☁️ {params.city}: {r.text.strip()}"Tool ที่ทำ destructive action (ต้อง confirm)
class DeleteInput(BaseModel):
model_config = ConfigDict(extra="forbid")
id: str = Field(..., min_length=1)
confirm: bool = Field(False, description="ต้องตั้ง True เพื่อยืนยัน")
@mcp.tool(
name="delete_item",
annotations={
"readOnlyHint": False,
"destructiveHint": True, # ★ บอก Claude ขอ confirm
"idempotentHint": False,
"openWorldHint": True,
},
)
async def delete_item(params: DeleteInput) -> str:
if not params.confirm:
return "⚠️ ต้องส่ง confirm=True เพื่อยืนยันการลบ"
# ... actually delete ...
return f"🗑 ลบ {params.id} แล้ว"destructiveHint: True → Claude จะถาม user confirm ก่อนเรียก (สำคัญสำหรับ safety)
ลึกขึ้น — ดู NirvaDeploy MCP server
Source ของ NirvaDeploy เป็น production MCP ตัวอย่าง — มี 16 tools, Pydantic input ทุก tool, Thai-first error handling.
อ่าน docs/tools-reference.md ดู spec ของ tool ทุกตัว — ใช้เป็น reference
คำถามที่พบบ่อย
Q: Tools เห็นใน Claude แต่ error เมื่อเรียก? A: 90% — Python path ผิดใน config (ต้องใช้ .venv/bin/python ไม่ใช่ system python). เช็ค log ที่ ~/Library/Logs/Claude/mcp*.log
Q: ใส่ env vars ยังไง? A: ใน config:
"env": { "MY_API_KEY": "xxx", "DEBUG": "true" }Q: Tool ของผมเรียกซ้ำได้ผลต่าง (เช่น weather) — annotation ตั้งยังไง? A: idempotentHint: False + openWorldHint: True
Q: ใช้ TypeScript แทน Python ได้ไหม? A: ได้ — Anthropic มี TypeScript SDK. API คล้ายกัน
ลองเลย
- Clone tutorial repo:
git clone https://github.com/nirvadeploy/tutorial-mcp-tools - ดู NirvaDeploy production MCP: github.com/Nirvacore/nirvadeploy
- Deploy MCP บน NirvaDeploy ฟรี: nirvadeploy.com
- Subscribe Line OA
@nirvadeployรับ tips MCP + AI dev
คำถาม? Line OA หรือ open GitHub issue
ชอบบทความนี้?
ลอง NirvaDeploy ฟรี
Free tier ตลอดชีพ · ไม่ต้องใส่บัตร · Sign in ด้วย Line/Google
เริ่มเลย →