Đến lúc viết code. Bài này bạn dựng một MCP server có một tool get_weather (giả lập), chạy qua stdio. Ta dùng SDK chính thức (ví dụ Python), nhưng ý tưởng áp dụng cho mọi ngôn ngữ.
Bước 1 — Cài đặt và khởi tạo
pip install "mcp[cli]"
Tạo file weather_server.py:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather") # tên server
FastMCP lo phần handshake, tools/list, JSON-RPC — bạn chỉ tập trung vào logic.
Bước 2 — Định nghĩa một tool
@mcp.tool()
def get_weather(city: str) -> str:
"""Lấy thời tiết hiện tại của một thành phố.
Args:
city: Tên thành phố, ví dụ 'Ha Noi'.
"""
fake = {"Ha Noi": "32°C, nắng", "Da Nang": "29°C, mưa rào"}
return fake.get(city, "Không có dữ liệu cho thành phố này.")
Ba yếu tố quan trọng để model chọn đúng tool:
- Tên tool rõ nghĩa (
get_weather, không phảigw). - Docstring/description mô tả tool làm gì — model đọc cái này để quyết định.
- Type hint tham số (
city: str) → SDK tự sinh JSON Schema cho input, giúp model điền đúng.
Bước 3 — Chạy server
if __name__ == "__main__":
mcp.run(transport="stdio")
Bước 4 — Đăng ký với host
Thêm vào config host:
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/tuyet/doi/weather_server.py"]
}
}
}
Khởi động lại host. Agent giờ thấy tool get_weather.
Bước 5 — Kiểm thử độc lập bằng Inspector
Đừng test qua agent ngay — quá nhiều biến. Dùng MCP Inspector:
npx @modelcontextprotocol/inspector python weather_server.py
Inspector mở UI cho bạn: xem tools/list, tự tay gọi get_weather với city="Ha Noi", kiểm tra kết quả JSON. Đây là cách gỡ lỗi nhanh nhất.
Khung tư duy: viết tool cho model đọc, không phải cho người
Mô tả tool là giao diện giữa model và code của bạn. Hãy viết như đang hướng dẫn một đồng nghiệp mới:
- Tool này làm gì?
- Khi nào nên dùng nó?
- Mỗi tham số nghĩa là gì, định dạng ra sao?
- Trả về cái gì?
Checklist một tool tốt
- [ ] Tên động từ + danh từ rõ ràng (
create_ticket,list_orders). - [ ] Description nói rõ mục đích và khi nào dùng.
- [ ] Mỗi tham số có kiểu và mô tả.
- [ ] Tool làm một việc (single responsibility).
- [ ] Trả về text/JSON gọn, có cấu trúc để model diễn giải.
- [ ] Xử lý lỗi trả message rõ ràng thay vì để văng exception.
Ví dụ xử lý lỗi đúng cách
@mcp.tool()
def get_weather(city: str) -> str:
if not city.strip():
return "Lỗi: 'city' rỗng. Hãy cung cấp tên thành phố."
...
Trả message lỗi dạng text giúp model tự sửa (hỏi lại người dùng), thay vì làm sập cả lời gọi.
Từ tool giả lập tới tool thật
get_weather trên là giả lập để bạn thấy cơ chế. Trong thực tế, thân hàm sẽ gọi API/database thật. Ba điều cần thêm khi chuyển sang tool thật:
- Biến môi trường cho cấu hình bí mật: khóa API, chuỗi kết nối lấy từ
os.environ, không hardcode. Host truyền vào quaenvtrong config. - Validate và chuẩn hóa input trước khi gọi hệ thống ngoài — model có thể điền tham số sai định dạng.
- Giới hạn và định dạng output: nếu API trả JSON dài, hãy trích những trường model cần rồi trả lại gọn gàng, thay vì đổ nguyên payload.
@mcp.tool()
def get_weather(city: str) -> str:
"""Thời tiết hiện tại của một thành phố (nguồn: OpenWeather)."""
key = os.environ["OWM_KEY"]
r = requests.get(API, params={"q": city, "appid": key}, timeout=5)
if r.status_code == 404:
return f"Không tìm thấy thành phố '{city}'."
d = r.json()
return f"{d['main']['temp']}°C, {d['weather'][0]['description']}"
Lưu ý timeout=5: một tool không được để agent treo vô hạn vì API chậm. Đây là thói quen bạn nên có ngay từ tool đầu tiên.
Sai lầm thường gặp
- Docstring qua loa hoặc bỏ trống → model không biết khi nào gọi tool, chọn sai hoặc bỏ qua.
- Tool đa năng làm 5 việc → khó cho model, khó bảo trì. Tách nhỏ.
- Trả về khối dữ liệu khổng lồ (cả nghìn dòng) → tốn context, model rối. Lọc/tóm tắt trước khi trả.
- Test thẳng qua agent thay vì Inspector → không phân biệt được lỗi ở tool hay ở prompt.
- Để exception văng ra thay vì trả message lỗi thân thiện với model.