Sổ tay kỹ thuật

Viết MCP server bằng Python (FastMCP)

how-to#mcp#python#claude-code· cập nhật 2026-11-06

Dùng khi cần tự viết MCP server Python cung cấp tool/resource chỉ đọc cho agent, test bằng MCP Inspector rồi gắn vào Claude Code.

Sửa trang này
Mục lục

Tóm tắt: Cài package mcp, khai báo FastMCP("tên"), gắn @mcp.tool() / @mcp.resource("uri") lên hàm Python có type hint + docstring, chạy mcp.run() (mặc định transport stdio). Test bằng MCP Inspector, rồi claude mcp add để Claude Code dùng. Khái niệm MCP xem MCP tổng quan.

Luồng hoạt động

flowchart LR A["server.py (FastMCP)"] --> B["mcp dev / Inspector"] B -->|OK| C["claude mcp add"] C --> D[Claude Code] D -->|"stdio JSON-RPC"| A A -->|"ping / show"| E[(Router)]

Cấu hình

Tạo project

uv init netops-mcp && cd netops-mcp
uv add "mcp[cli]"

Server tối thiểu

server.py
import ipaddress
import subprocess
 
from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("netops")
 
DEVICES = {
    "pe1-hni": {"mgmt": "198.51.100.11", "role": "pe", "os": "iosxr"},
    "pe2-hcm": {"mgmt": "198.51.100.12", "role": "pe", "os": "junos"},
}
 
 
@mcp.tool()
def ping(target: str, count: int = 3) -> str:
    """Ping một địa chỉ IPv4/IPv6 từ máy chạy server. Chỉ nhận IP, không nhận hostname."""
    ip = ipaddress.ip_address(target)          # ValueError nếu không phải IP -> trả lỗi cho model
    count = max(1, min(count, 10))             # giới hạn để không treo
    out = subprocess.run(
        ["ping", "-c", str(count), "-W", "1", str(ip)],
        capture_output=True, text=True, timeout=30,
    )
    return out.stdout[-1500:] or out.stderr
 
 
@mcp.tool()
def list_devices(role: str | None = None) -> list[dict]:
    """Liệt kê thiết bị trong inventory, lọc theo role (pe, p, rr) nếu có."""
    return [
        {"name": n, **d} for n, d in DEVICES.items() if role is None or d["role"] == role
    ]
 
 
@mcp.resource("inventory://device/{name}")
def device(name: str) -> dict:
    """Thông tin một thiết bị theo tên."""
    return DEVICES[name]
 
 
if __name__ == "__main__":
    mcp.run()  # stdio; HTTP: mcp.run(transport="streamable-http")

Điểm cần nhớ:

  • Tên hàm thành tên tool, docstring thành description, type hint thành JSON Schema inputSchema. Viết docstring cho model đọc (xem Thiết kế tool cho agent).
  • Raise exception trong tool thì client nhận kết quả lỗi (isError), model có thể tự sửa tham số.
  • Với stdio, không print() ra stdout: stdout là kênh JSON-RPC. Log ra stderr (logging mặc định ghi stderr).

Test bằng MCP Inspector

uv run mcp dev server.py                                   # mở Inspector qua CLI của mcp
npx @modelcontextprotocol/inspector uv run server.py       # hoặc chạy Inspector trực tiếp

Trong Inspector: Connect → tab Tools → List Tools → chọn ping → nhập target → Run Tool. Tab Resources để thử inventory://device/pe1-hni.

Thêm vào Claude Code

# stdio, scope local (chỉ mình bạn, project hiện tại)
claude mcp add netops -- uv run --directory /opt/netops-mcp server.py
 
# chia sẻ cho cả team qua file .mcp.json trong repo
claude mcp add netops -s project -- uv run --directory /opt/netops-mcp server.py
 
# truyền biến môi trường
claude mcp add netbox -e NETBOX_TOKEN=$NETBOX_TOKEN -- uv run netbox-mcp
 
# server HTTP
claude mcp add --transport http netops https://mcp.example.net/mcp
 
claude mcp list
claude mcp get netops
claude mcp remove netops

Trong phiên Claude Code, gõ /mcp để xem trạng thái kết nối và danh sách tool.

.mcp.json
{
  "mcpServers": {
    "netops": {
      "command": "uv",
      "args": ["run", "--directory", "/opt/netops-mcp", "server.py"],
      "env": {}
    }
  }
}

Tool chạy với quyền của user chạy server. Chỉ expose lệnh đọc (show, get, ping); nếu cần ghi cấu hình, buộc qua commit confirmed và để Claude Code hỏi xác nhận (không cho vào permissions.allow).

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
/mcp báo failed ngay khi khởi động Lệnh sai đường dẫn, thiếu dependency Chạy y nguyên lệnh trong terminal: uv run --directory ... server.py
Kết nối rồi rớt, lỗi parse JSON Code print() ra stdout Bỏ print, dùng logging (stderr)
Tool không xuất hiện Quên () ở @mcp.tool() hoặc server chưa restart Sửa decorator, restart Claude Code / reconnect trong /mcp
Model truyền sai kiểu tham số Thiếu type hint, docstring mơ hồ Thêm type hint, ví dụ giá trị trong docstring
ModuleNotFoundError: mcp Claude Code chạy python hệ thống, không phải venv Dùng uv run --directory <project> hoặc đường dẫn tuyệt đối tới .venv/bin/python
Tool chạy lâu bị timeout Thiết bị chậm, không giới hạn Đặt timeout cho subprocess/SSH, trả kết quả gọn

Tham khảo