Tự viết MCP server bằng Python trong 30 dòng
Tóm tắt
Với SDK Python, MCP server chỉ cần type hint, docstring và mcp.run(). Lỗi phổ biến nhất là in ra stdout làm hỏng kênh stdio; hãy test bằng MCP Inspector trước, dùng đường dẫn tuyệt đối và bắt đầu với tool chỉ đọc.
Bạn có một API nội bộ (tra đơn hàng, xem tồn kho, đọc báo cáo) và muốn Claude gọi được nó trong Claude Desktop hay Claude Code. Cách chuẩn hiện nay là bọc API đó thành một MCP server. Với SDK Python chính thức, một server tối giản chỉ khoảng 30 dòng. Bài này đi từ con số 0 tới lúc Claude gọi được tool của bạn, kèm những lỗi người mới hay gặp nhất.
Chuẩn bị
- Python 3.10 trở lên và
uv(trình quản lý gói/dự án). - Gói
mcp[cli]từ SDK chính thức.
uv init order-server
cd order-server
uv venv && source .venv/bin/activate
uv add "mcp[cli]"
Viết server
SDK tự sinh định nghĩa tool từ type hint và docstring, nên hãy viết chúng cẩn thận: đó chính là "tài liệu" mà model đọc để quyết định gọi tool nào, với tham số gì.
import logging
from mcp.server import MCPServer
logging.basicConfig(level=logging.INFO) # ghi ra stderr
log = logging.getLogger("orders")
mcp = MCPServer("orders")
ORDERS = {"A100": "Đang giao", "A101": "Đã huỷ"}
@mcp.tool()
async def get_order_status(order_id: str) -> str:
"""Tra trạng thái đơn hàng theo mã.
Args:
order_id: Mã đơn, ví dụ A100
"""
log.info("lookup %s", order_id)
return ORDERS.get(order_id.upper(), "Không tìm thấy đơn hàng.")
if __name__ == "__main__":
mcp.run(transport="stdio")
Ở các bản SDK và bài hướng dẫn cũ, lớp này có tên FastMCP; tài liệu quickstart hiện tại dùng MCPServer. Ngoài @mcp.tool(), server còn có thể cung cấp resources (dữ liệu dạng tệp để đọc) và prompts (mẫu prompt dựng sẵn).
Lỗi số 1: in ra stdout
Với transport stdio, stdout là kênh truyền thông điệp JSON-RPC. Chỉ một lệnh print() cũng đủ làm hỏng giao tiếp và server "chết" không rõ lý do. Tài liệu nhấn mạnh: không bao giờ ghi gì ra stdout; dùng module logging (mặc định ghi ra stderr).
Kiểm thử bằng MCP Inspector
Trước khi nối vào Claude, thử bằng Inspector (cần Node 22.19+):
# Giao diện web
npx @modelcontextprotocol/inspector uv run server.py
# CLI, hợp cho CI
npx @modelcontextprotocol/inspector --cli uv run server.py --method tools/list
Inspector cho xem danh sách tool, gọi thử với tham số tuỳ ý và đọc toàn bộ thông điệp JSON-RPC. Nếu muốn chắc server chạy được với cả client cũ và client theo spec mới, chạy CLI với --protocol-era legacy và --protocol-era modern.
Kết nối với Claude
Claude Desktop: sửa ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) rồi khởi động lại app:
{
"mcpServers": {
"orders": {
"command": "uv",
"args": ["--directory", "/ĐƯỜNG/DẪN/TUYỆT/ĐỐI/order-server", "run", "server.py"]
}
}
}
Claude Code: claude mcp add orders -- uv --directory /ĐƯỜNG/DẪN/order-server run server.py, rồi kiểm tra bằng /mcp.
Khi không chạy
| Triệu chứng | Hướng xử lý |
|---|---|
| Server không xuất hiện | Dùng đường dẫn tuyệt đối; có thể phải ghi đường dẫn đầy đủ tới uv (which uv) |
| Kết nối rồi đứt ngay | Tìm print() hay thư viện ghi ra stdout |
| Muốn xem log | Claude Desktop trên macOS: tail -f ~/Library/Logs/Claude/mcp*.log |
| Claude không gọi tool | Viết lại docstring rõ hơn: tool làm gì, khi nào dùng, ví dụ tham số |
Rủi ro và lưu ý
- Tool là cửa vào hệ thống: tool ghi/xoá dữ liệu cần kiểm tra quyền và xác nhận; bắt đầu bằng tool chỉ đọc.
- Kết quả tool là dữ liệu không tin cậy: nội dung trả về có thể chứa prompt injection nếu lấy từ nguồn bên ngoài.
- Bí mật: truyền API key qua biến môi trường (
envtrong config), không hard-code trong code. - Trả về gọn: kết quả dài tốn context; lọc và định dạng trước khi trả.
Áp dụng thế nào?
- Dev: Bọc một API nội bộ chỉ đọc thành 2–3 tool, test bằng Inspector, rồi dùng trong Claude Code hằng ngày.
- Tech Lead: Đưa lệnh Inspector CLI
tools/listvào CI để phát hiện server hỏng trước khi phát hành. - PM: Liệt kê 5 câu hỏi nội bộ được hỏi nhiều nhất; mỗi câu là ứng viên cho một tool MCP.
Nguồn & tham khảo
- Build an MCP server — Model Context Protocol
- MCP Python SDK — Model Context Protocol (GitHub)
- MCP Inspector — Model Context Protocol