AI Engineer Master Course
Trang chủ / Module 9
🔌 Module 9 / 17 · 2-3 tuần

MCP (Model Context Protocol)

MCP Concepts, MCP Servers, Custom Tools, Database Integration, GitHub/Slack Integrations.

MCPToolingIntegration
📌 Vì sao module này quan trọng

MCP (Model Context Protocol), do Anthropic mở nguồn cuối 2024, đã trở thành chuẩn công nghiệp cho việc kết nối AI model với dữ liệu và công cụ bên ngoài — được cả OpenAI và Google DeepMind hỗ trợ trong SDK của họ từ 2025. Hiểu và tự viết được MCP server là kỹ năng phân biệt "biết dùng AI" với "AI Engineer thực thụ" năm 2026, vì nó là lớp hạ tầng dùng chung cho toàn bộ tool ecosystem của một tổ chức.

🎯 Mục tiêu học tập

  • Hiểu kiến trúc MCP: Host, Client, Server, và 3 loại primitive (Tools, Resources, Prompts)
  • Tự viết được một MCP server cung cấp custom tools cho dữ liệu/hệ thống riêng
  • Kết nối MCP server với database và các dịch vụ ngoài (GitHub, Slack)
  • Hiểu vì sao MCP giải quyết bài toán "M×N tích hợp" tốt hơn cách tích hợp tool truyền thống

MCP Concepts — vì sao cần một chuẩn chung

Trước MCP, mỗi ứng dụng AI phải tự viết tích hợp riêng cho từng nguồn dữ liệu/công cụ (Slack, GitHub, database, Google Drive...) — với M ứng dụng và N công cụ, ta cần M×N lần tích hợp riêng biệt. MCP chuẩn hoá giao thức giao tiếp (dựa trên JSON-RPC) giữa Host (ứng dụng AI, ví dụ Claude Code, Claude Desktop, hoặc agent tự viết), Client (thành phần trong Host quản lý kết nối), và Server (nơi triển khai logic thật kết nối tới dữ liệu/công cụ) — biến bài toán thành M+N: viết một MCP server một lần, mọi Host tương thích MCP đều dùng được.

Ba primitive chính một MCP server cung cấp: Tools (hành động model có thể gọi, giống function calling), Resources (dữ liệu model có thể đọc, giống endpoint GET), và Prompts (template prompt tái sử dụng được, do người dùng chọn kích hoạt).

Tự viết một MCP Server

SDK chính thức có cho Python, TypeScript, và nhiều ngôn ngữ khác. Một MCP server tối giản expose một tool tra cứu đơn hàng có thể trông như sau (Python SDK):

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("orders-server")

@mcp.tool()
def get_order_status(order_id: str) -> dict:
    # Tra cứu trạng thái đơn hàng theo mã đơn.
    order = db.query_order(order_id)
    return {"status": order.status, "eta": order.eta}

if __name__ == "__main__":
    mcp.run(transport="stdio")

Điểm quan trọng: docstring và type hint chính là tài liệu model dùng để quyết định gọi tool nào — viết mô tả rõ ràng, cụ thể còn quan trọng hơn cả logic bên trong.

Transport: stdio vs HTTP/SSE

  • stdio: server chạy local như subprocess, giao tiếp qua stdin/stdout — phù hợp công cụ chạy trên máy cá nhân (Claude Desktop, Claude Code).
  • Streamable HTTP: server chạy như một dịch vụ web độc lập, nhiều client có thể kết nối từ xa — phù hợp triển khai MCP server dùng chung cho cả team/tổ chức, cần thêm cơ chế xác thực (OAuth) vì server này thường public trên mạng nội bộ hoặc internet.

Tích hợp Database, GitHub, Slack

Với database: viết tool bọc quanh câu query đã được kiểm soát chặt (KHÔNG cho model tự sinh SQL thô chạy trực tiếp lên production DB — luôn qua tầng service có validate, giới hạn quyền đọc/ghi, và giới hạn kết quả trả về). Với GitHub/Slack: có thể dùng MCP server chính thức do các nền tảng này công bố, hoặc tự viết bọc quanh REST API của họ nếu cần logic nghiệp vụ riêng (ví dụ chỉ cho phép agent tạo PR draft, không cho merge).

⚠️ Bảo mật

MCP server là một biên giới tin cậy mới — luôn áp dụng nguyên tắc least privilege: mỗi tool chỉ có đúng quyền cần thiết, không bao giờ truyền thẳng credential toàn quyền admin cho một tool "tiện cho nhanh".

🔬 Đào sâu — Tự viết MCP Client (không chỉ Server)

Phần lớn tài liệu chỉ dạy viết server vì Host (Claude Desktop, Claude Code...) đã có sẵn client. Nhưng khi tự xây agent riêng (Module 8) cần kết nối MCP server, bạn phải tự viết client. Hiểu luồng này giúp bạn thấy MCP thực chất chỉ là JSON-RPC có cấu trúc, không có gì huyền bí:

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(command="python", args=["orders_server.py"])

async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            tools = await session.list_tools()
            print([t.name for t in tools.tools])

            result = await session.call_tool("get_order_status", {"order_id": "DH8842"})
            print(result.content)

Trong một agent thật (LangGraph/Claude Agent SDK), bước list_tools() chạy một lần lúc khởi tạo để lấy schema, sau đó schema này được đưa thẳng vào tham số tools= của lời gọi LLM — MCP chỉ là lớp chuẩn hoá cách lấy và thực thi tool, không thay thế cơ chế tool calling ở Module 5.

🔬 Đào sâu — Resources & Prompts (2 primitive còn lại)

Ngoài Tools, MCP server còn có thể expose Resources (dữ liệu tĩnh/động model có thể đọc, giống GET endpoint — ví dụ nội dung 1 file cấu hình, danh sách sản phẩm) và Prompts (template được người dùng chủ động chọn kích hoạt, khác Tools ở chỗ do người dùng quyết định chứ không phải model tự quyết):

@mcp.resource("orders://recent")
def recent_orders() -> str:
    # Model có thể "đọc" resource này như đọc 1 file, không cần "gọi hàm" như Tool
    return json.dumps(db.get_recent_orders(limit=20))

@mcp.prompt()
def investigate_order(order_id: str) -> str:
    # Prompt template — người dùng chọn từ menu trong Host (vd. Claude Desktop) để kích hoạt
    return f"Hãy điều tra toàn bộ lịch sử và trạng thái hiện tại của đơn hàng {order_id}, nêu rõ bất thường nếu có."

Phân biệt rõ 3 primitive giúp thiết kế MCP server đúng: dữ liệu tham khảo → Resource; hành động có tác dụng phụ hoặc cần logic xử lý → Tool; kịch bản làm việc soạn sẵn cho người dùng → Prompt.

🔬 Đào sâu — Debug bằng MCP Inspector & triển khai remote

MCP Inspector là công cụ chính thức (chạy qua npx @modelcontextprotocol/inspector) mở giao diện web cho phép gọi thử từng tool/resource/prompt của server một cách độc lập, xem raw JSON-RPC request/response — nên dùng công cụ này để debug server trước khi gắn vào agent thật, tách riêng lỗi "server sai" khỏi lỗi "agent gọi sai".

Khi triển khai MCP server dùng chung cho cả team (không chạy local qua stdio nữa), chuyển sang Streamable HTTP transport và bắt buộc thêm xác thực (OAuth 2.1 theo đặc tả MCP chính thức) — tuyệt đối không expose MCP server có tool nhạy cảm (đọc DB, gọi API nội bộ) ra internet mà không có lớp xác thực, vì bản chất nó là một cổng vào hệ thống backend của bạn.

🏋️ Bài tập thực hành

MCP server nội bộ cho hệ thống của bạn

Viết một MCP server bằng Python SDK expose 3 tool: (1) tra cứu dữ liệu từ PostgreSQL (chỉ SELECT, có giới hạn số dòng), (2) tạo issue trên GitHub repo qua API, (3) gửi thông báo vào một kênh Slack (hoặc webhook giả lập). Kết nối server này với Claude Desktop hoặc một MCP client tự viết, thử yêu cầu agent thực hiện một nhiệm vụ cần dùng cả 3 tool tuần tự.

📚 Tài nguyên học tập

✅ Tự đánh giá hoàn thành

  • Giải thích được kiến trúc Host/Client/Server và 3 primitive của MCP
  • Tự viết được 1 MCP server với ít nhất 3 custom tools
  • Kết nối MCP server với database qua tầng service có kiểm soát quyền
  • Hiểu và áp dụng nguyên tắc least privilege khi thiết kế tool