Chuyển đến nội dung chính

Model Context Protocol (MCP): Hướng dẫn kèm dự án demo

Tìm hiểu cách xây dựng máy chủ MCP bằng Model Context Protocol của Anthropic để kết nối Claude với GitHub và Notion.
Đã cập nhật 5 thg 6, 2026  · 12 phút đọc

Model Context Protocol (MCP) là một tiêu chuẩn mở cho phép các mô hình ngôn ngữ lớn tương tác động với công cụ, cơ sở dữ liệu và API bên ngoài thông qua một giao diện tiêu chuẩn hóa.

Trong bài viết này, tôi sẽ hướng dẫn bạn xây dựng một máy chủ đánh giá PR chạy bằng MCP tích hợp với Claude Desktop. Máy chủ này sẽ:

  • Lấy chi tiết PR và các tệp thay đổi từ GitHub
  • Phân tích thay đổi mã bằng ứng dụng Claude Desktop
  • Tạo tóm tắt và gợi ý đánh giá PR
  • Lưu đánh giá vào Notion

Chúng ta sẽ sử dụng Model Context Protocol (MCP) để tiêu chuẩn hóa giao tiếp giữa máy chủ và Claude Desktop, giúp hệ thống mô-đun và có khả năng mở rộng.

Chúng tôi cập nhật cho độc giả những thông tin mới nhất về AI qua The Median, bản tin miễn phí mỗi thứ Sáu tóm lược các câu chuyện nổi bật trong tuần. Đăng ký để luôn nắm bắt chỉ trong vài phút mỗi tuần:

Model Context Protocol (MCP) là gì?

Model Context Protocol (MCP) là một tiêu chuẩn mở do Anthropic phát triển để cho phép tích hợp dễ dàng và tiêu chuẩn hóa giữa các mô hình AI và công cụ bên ngoài. Nó hoạt động như một bộ kết nối phổ quát, cho phép các mô hình ngôn ngữ lớn (LLM) tương tác động với API, cơ sở dữ liệu và các ứng dụng doanh nghiệp.

Ban đầu được xây dựng để cải thiện khả năng của Claude trong việc tương tác với hệ thống bên ngoài, Anthropic đã quyết định mở mã nguồn MCP vào đầu năm 2024 nhằm khuyến khích áp dụng rộng rãi trong ngành. Bằng cách công khai MCP, họ hướng tới việc tạo ra một khung giao tiếp tiêu chuẩn giữa AI và công cụ, giảm phụ thuộc vào tích hợp độc quyền và tăng tính mô-đun cũng như khả năng tương tác giữa các ứng dụng AI.

MCP tuân theo kiến trúc khách–máy chủ, trong đó:

  • Các khách hàng MCP (ví dụ: Claude Desktop) yêu cầu thông tin và thực thi tác vụ.
  • Máy chủ MCP cung cấp quyền truy cập vào công cụ và nguồn dữ liệu bên ngoài.
  • Ứng dụng máy chủ lưu trữ dùng MCP để giao tiếp giữa mô hình và công cụ.

Nguồn: Model Context Protocol

Vì sao bạn nên dùng MCP cho dự án của mình:

  • Tích hợp AI tiêu chuẩn hóa: MCP cung cấp một cách có cấu trúc để kết nối mô hình AI với công cụ.
  • Linh hoạt: Cho phép chuyển đổi dễ dàng giữa các mô hình AI và nhà cung cấp khác nhau.
  • Bảo mật: Giữ dữ liệu trong hạ tầng của bạn trong khi tương tác với AI.
  • Khả năng mở rộng: MCP hỗ trợ nhiều phương thức truyền như stdio, WebSockets, HTTP SSE và UNIX sockets.

Tổng quan dự án demo MCP: Máy chủ đánh giá PR

Hệ thống đánh giá PR tự động hóa phân tích mã và ghi chép bằng Claude Desktop và Notion.

Quy trình tóm tắt như sau:

  1. Thiết lập môi trường: Nạp thông tin xác thực GitHub và Notion.
  2. Khởi tạo máy chủ: Khởi động máy chủ MCP để giao tiếp với Claude Desktop.
  3. Lấy dữ liệu PR: Truy xuất thay đổi và siêu dữ liệu PR từ GitHub.
  4. Phân tích mã: Claude Desktop trực tiếp phân tích thay đổi mã (không cần công cụ riêng).
  5. Ghi chép vào Notion: Lưu kết quả phân tích vào Notion để theo dõi.

Bước 1: Thiết lập môi trường

Trước khi bắt đầu, hãy đảm bảo bạn đã cài đặt Python 3.10+. Sau đó, chúng ta thiết lập môi trường và cài đặt trình quản lý gói uv. Trên Mac hoặc Linux: 

curl -LsSf https://astral.sh/uv/install.sh | sh  # Mac/Linux

Trên Windows (PowerShell):

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Tiếp theo, tạo một thư mục dự án mới và khởi tạo bằng uv:

uv init pr_reviewer
cd pr_reviewer

Bây giờ chúng ta tạo và kích hoạt môi trường ảo. Trên Mac hoặc Linux:

uv venv
source .venv/bin/activate

Trên Windows:

.venv\Scripts\activate

Giờ hãy cài đặt các phụ thuộc cần thiết:

uv add "mcp[cli]" requests python-dotenv notion-client

Chúng ta sẽ dùng uv thay cho conda trong dự án này vì nó nhanh hơn, nhẹ hơn và tập trung vào quản lý gói Python. Tóm tắt những gì vừa làm:

  • Thiết lập môi trường phát triển máy chủ MCP đánh giá PR GitHub bằng trình quản lý gói uv
  • Khởi tạo thư mục dự án mới tên pr_reviewer và chuyển vào đó.
  • Tạo môi trường ảo và kích hoạt để đảm bảo cài đặt gói được cách ly.

Bước 2: Cài đặt phụ thuộc

Sau khi thiết lập môi trường, chúng ta cấu hình phụ thuộc với khóa API và các yêu cầu khác. Tạo tệp requirements.txt và thêm các gói Python sau:

# Core dependencies for PR Analyzer
requests>=2.31.0          # For GitHub API calls
python-dotenv>=1.0.0      # For environment variables
mcp[cli]>=1.4.0          # For MCP server functionality
notion-client>=2.3.0      # For Notion integration

# Optional: Pin versions for stability
# requests==2.31.0
# python-dotenv==1.0.0
# mcp[cli]==1.4.0
# notion-client==2.3.0

Tệp requirements.txt chứa tất cả phụ thuộc cốt lõi cần cho dự án. Để cài đặt, chạy một trong các lệnh sau (ưu tiên dùng uv nếu bạn đã cài trước đó).

uv pip install -r requirements.txt
pip install -r requirements.txt

Giờ đây, môi trường của bạn đã có đầy đủ các phụ thuộc cần thiết.

Bước 3: Thiết lập biến môi trường

Tiếp theo, tạo tệp .env chứa tất cả khóa và token cần thiết cho dự án này. 

Tạo token GitHub

Để tạo token GitHub:

  • Đăng nhập tài khoản GitHub và vào phần cài đặt.
  • Đi tới developer settings → Personal Access Tokens.
  • Nhấp Generate New Token và chọn phiên bản classic.
  • Đặt tên cho token và bật các quyền sau:
    • read:org
    • read:repo_hook
    • repo
  • Nhấp Generate để tạo token.
  • Sao chép và lưu trữ token an toàn, vì nó sẽ được dùng để xác thực trong các yêu cầu API.

Tích hợp Notion

  • Đăng nhập vào trang tích hợp của Notion.
  • Tạo một tích hợp mới và điền biểu mẫu. Giữ “type” là Internal và thêm nó vào workspace của bạn. 

  • Lưu tích hợp và sao chép liên kết của tích hợp (phần UUID ở cuối URL là Mã định danh duy nhất toàn cầu mà bạn cần ghi lại và dùng làm Notion Page ID).
https://www.notion.so/profile/integrations/internal/UUID
  • Sau khi tạo tích hợp, nhấp vào nó, và trong phần cấu hình, sao chép Internal Integration Secret, đây sẽ là khóa API Notion của bạn.

Tạo tệp .env

Giờ hãy tạo tệp .env và thêm nội dung sau cùng với khóa API và token đã tạo ở trên.

GITHUB_TOKEN=your_github_token
NOTION_API_KEY=your_notion_api_key
NOTION_PAGE_ID=your_notion_page_id

Bước 4: Tích hợp GitHub

Hãy cấu hình mô-đun tích hợp GitHub để quản lý và truy xuất các thay đổi PR từ một kho GitHub.

Tạo tệp github_integration.py và viết đoạn mã sau (chúng ta sẽ giải thích bên dưới).

import os
import requests
import traceback
from dotenv import load_dotenv

# Load environment variables
load_dotenv()
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')

def fetch_pr_changes(repo_owner: str, repo_name: str, pr_number: int) -> list:
    """Fetch changes from a GitHub pull request.
    
    Args:
        repo_owner: The owner of the GitHub repository
        repo_name: The name of the GitHub repository
        pr_number: The number of the pull request to analyze
        
    Returns:
        A list of file changes with detailed information about each change
    """
    print(f" Fetching PR changes for {repo_owner}/{repo_name}#{pr_number}")
    
    # Fetch PR details
    pr_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/pulls/{pr_number}"
    files_url = f"{pr_url}/files"
    headers = {'Authorization': f'token {GITHUB_TOKEN}'}
    
    try:
        # Get PR metadata
        pr_response = requests.get(pr_url, headers=headers)
        pr_response.raise_for_status()
        pr_data = pr_response.json()
        
        # Get file changes
        files_response = requests.get(files_url, headers=headers)
        files_response.raise_for_status()
        files_data = files_response.json()
        
        # Combine PR metadata with file changes
        changes = []
        for file in files_data:
            change = {
                'filename': file['filename'],
                'status': file['status'],  # added, modified, removed
                'additions': file['additions'],
                'deletions': file['deletions'],
                'changes': file['changes'],
                'patch': file.get('patch', ''),  # The actual diff
                'raw_url': file.get('raw_url', ''),
                'contents_url': file.get('contents_url', '')
            }
            changes.append(change)
        
        # Add PR metadata
        pr_info = {
            'title': pr_data['title'],
            'description': pr_data['body'],
            'author': pr_data['user']['login'],
            'created_at': pr_data['created_at'],
            'updated_at': pr_data['updated_at'],
            'state': pr_data['state'],
            'total_changes': len(changes),
            'changes': changes
        }
        
        print(f"Successfully fetched {len(changes)} changes")
        return pr_info
        
    except Exception as e:
        print(f"Error fetching PR changes: {str(e)}")
        traceback.print_exc()
        return None

# Example usage for debugging
# pr_data = fetch_pr_changes('owner', 'repo', 1)
# print(pr_data) 

Hàm fetch_pr_changes() truy xuất và trả về các thay đổi từ một pull request trên GitHub. Nó nhận ba tham số là repo_owner, repo_namepr_number, và trả về danh sách có cấu trúc các thay đổi tệp cùng siêu dữ liệu PR.

Mã sử dụng thư viện requests để gửi các yêu cầu HTTP GET có xác thực, lấy cả siêu dữ liệu PR tổng quát và thay đổi ở cấp độ tệp:

  • Yêu cầu API đầu tiên truy xuất thông tin cấp cao của PR, bao gồm tiêu đề, mô tả, tác giả, dấu thời gian và trạng thái hiện tại.
  • Yêu cầu API thứ hai lấy chi tiết về từng tệp thay đổi trong PR, như tên tệp, trạng thái sửa đổi, số dòng thêm/bớt và URL để truy cập nội dung tệp.

Khi dữ liệu đã được lấy về, hàm cấu trúc và kết hợp siêu dữ liệu PR với các thay đổi tệp vào một dictionary. Các thay đổi tệp được lưu trong danh sách, mỗi mục chứa thông tin chi tiết về một tệp. Cấu trúc dữ liệu cuối cùng bao gồm tiêu đề PR, mô tả, tác giả, dấu thời gian, trạng thái, tổng số tệp thay đổi và phân tích chi tiết các sửa đổi.

Bước 5: Triển khai máy chủ MCP

Sau khi đã có đầy đủ phụ thuộc và hàm bổ trợ, chúng ta thiết lập máy chủ MCP. Tạo tệp pr_analyzer.py, tệp này sẽ:

  • Khởi tạo một máy chủ MCP.
  • Định nghĩa các công cụ để lấy chi tiết PR, phân tích mã và lưu kết quả vào Notion.
  • Công khai các công cụ này cho Claude Desktop để tóm tắt, đưa ra phương án và hơn thế nữa.

Hãy thêm mã trước rồi giải thích sau 

import sys
import os
import traceback
from typing import Any, List, Dict
from mcp.server.fastmcp import FastMCP
from github_integration import fetch_pr_changes
from notion_client import Client
from dotenv import load_dotenv

class PRAnalyzer:
    def __init__(self):
        # Load environment variables
        load_dotenv()
        
        # Initialize MCP Server
        self.mcp = FastMCP("github_pr_analysis")
        print("MCP Server initialized", file=sys.stderr)
        
        # Initialize Notion client
        self._init_notion()
        
        # Register MCP tools
        self._register_tools()
    
    def _init_notion(self):
        """Initialize the Notion client with API key and page ID."""
        try:
            self.notion_api_key = os.getenv("NOTION_API_KEY")
            self.notion_page_id = os.getenv("NOTION_PAGE_ID")
            
            if not self.notion_api_key or not self.notion_page_id:
                raise ValueError("Missing Notion API key or page ID in environment variables")
            
            self.notion = Client(auth=self.notion_api_key)
            print(f"Notion client initialized successfully", file=sys.stderr)
            print(f"Using Notion page ID: {self.notion_page_id}", file=sys.stderr)
        except Exception as e:
            print(f"Error initializing Notion client: {str(e)}", file=sys.stderr)
            traceback.print_exc(file=sys.stderr)
            sys.exit(1)
    
    def _register_tools(self):
        """Register MCP tools for PR analysis."""
        @self.mcp.tool()
        async def fetch_pr(repo_owner: str, repo_name: str, pr_number: int) -> Dict[str, Any]:
            """Fetch changes from a GitHub pull request."""
            print(f"Fetching PR #{pr_number} from {repo_owner}/{repo_name}", file=sys.stderr)
            try:
                pr_info = fetch_pr_changes(repo_owner, repo_name, pr_number)
                if pr_info is None:
                    print("No changes returned from fetch_pr_changes", file=sys.stderr)
                    return {}
                print(f"Successfully fetched PR information", file=sys.stderr)
                return pr_info
            except Exception as e:
                print(f"Error fetching PR: {str(e)}", file=sys.stderr)
                traceback.print_exc(file=sys.stderr)
                return {}
        
        @self.mcp.tool()
        async def create_notion_page(title: str, content: str) -> str:
            """Create a Notion page with PR analysis."""
            print(f"Creating Notion page: {title}", file=sys.stderr)
            try:
                self.notion.pages.create(
                    parent={"type": "page_id", "page_id": self.notion_page_id},
                    properties={"title": {"title": [{"text": {"content": title}}]}},
                    children=[{
                        "object": "block",
                        "type": "paragraph",
                        "paragraph": {
                            "rich_text": [{
                                "type": "text",
                                "text": {"content": content}
                            }]
                        }
                    }]
                )
                print(f"Notion page '{title}' created successfully!", file=sys.stderr)
                return f"Notion page '{title}' created successfully!"
            except Exception as e:
                error_msg = f"Error creating Notion page: {str(e)}"
                print(error_msg, file=sys.stderr)
                traceback.print_exc(file=sys.stderr)
                return error_msg
    
    def run(self):
        """Start the MCP server."""
        try:
            print("Running MCP Server for GitHub PR Analysis...", file=sys.stderr)
            self.mcp.run(transport="stdio")
        except Exception as e:
            print(f"Fatal Error in MCP Server: {str(e)}", file=sys.stderr)
            traceback.print_exc(file=sys.stderr)
            sys.exit(1)

if __name__ == "__main__":
    analyzer = PRAnalyzer()
    analyzer.run() 

Đoạn mã trên thiết lập một máy chủ MCP để lấy thay đổi PR trên GitHub và lưu kết quả phân tích vào Notion. Cùng điểm qua các thành phần chính:

  1. Thiết lập môi trường và khởi tạo
    • Mô-đun dotenv nạp biến môi trường, đảm bảo truy cập an toàn khóa API và thông tin xác thực. 
    • Sau đó lớp PRAnalyzer khởi tạo máy chủ MCP bằng hàm FastMCP() với tên github_pr_analysis và cho phép tương tác với ứng dụng Claude Desktop.
    • Khách hàng Notion cũng được thiết lập bằng gói notion-client, sử dụng khóa API Notion và page ID lưu trong tệp .env.
  2. Công cụ MCP: lấy thay đổi PR
    • Hàm fetch_pr() truy xuất siêu dữ liệu pull request từ GitHub bằng phương thức fetch_pr_changes() trong tệp github_integration.py. Nếu thành công, nó trả về một dictionary chứa chi tiết PR.
    • Bạn có thể tự động hóa quy trình này bằng thư viện Webhooks tích hợp trong GitHub để bất kỳ PR mới nào tạo trên kho của bạn sẽ được máy chủ tự động xử lý. 
  3. Công cụ MCP: tạo trang Notion
    • Hàm create_notion_page() tạo một trang Notion chứa kết quả phân tích PR.
    • Nó sử dụng Notion API để tạo trang mới trong workspace Notion được chỉ định.
  4. Chạy máy chủ
    • Phương thức run() khởi động máy chủ MCP bằng mcp.run(transport="stdio"), cho phép tương tác giữa Claude Desktop và các công cụ đánh giá PR.

Bước 6: Chạy máy chủ MCP

Khi đã có đầy đủ các phần mã, hãy chạy máy chủ bằng lệnh sau trong terminal:

python pr_analyzer.py

Terminal for running the MCP server

Terminal for running MCP server

Khi máy chủ đã chạy, mở ứng dụng Claude Desktop, bạn sẽ thấy biểu tượng phích cắm (🔌) trong hộp nhập văn bản. Biểu tượng này cho biết có MCP trong môi trường Claude. Trong cùng hộp, bạn sẽ thấy biểu tượng giống chiếc búa (🔨) hiển thị tất cả MCP khả dụng như hình bên dưới.

Bây giờ, đưa liên kết tới PR bạn muốn phân tích và Claude sẽ lo phần còn lại.

Claude generating summary

Claude sẽ phân tích PR trước rồi cung cấp tóm tắt và đánh giá. Công cụ sẽ hỏi người dùng có muốn tải thông tin lên trang Notion hay không. Dù bạn có thể tự động hóa bước này, mã hiện tại cho phép bạn xem lại bản tóm tắt trước khi tạo trang Notion mới.

Claude uploading summary to notion

Trang Notion đã cập nhật trông như sau:

Notion page created by Claude MCP

Kết luận

Máy chủ đánh giá PR dựa trên MCP của chúng tôi cải thiện phân tích mã và ghi chép, giúp quy trình review hiệu quả và có tổ chức hơn. Bằng cách sử dụng MCP, GitHub API và tích hợp Notion, hệ thống này hỗ trợ phân tích PR tự động, cộng tác dễ dàng và lưu trữ tài liệu có cấu trúc. Với cấu hình này, nhà phát triển có thể nhanh chóng lấy chi tiết PR, phân tích thay đổi mã bằng Claude và lưu insight vào Notion để tham khảo sau.

Để khám phá thêm các công cụ AI gần đây, tôi gợi ý các bài viết sau:


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Tôi là Chuyên gia Google Developers trong lĩnh vực ML (Gen AI), Chuyên gia Kaggle 3x và Đại sứ Women Techmakers với hơn 3 năm kinh nghiệm trong ngành công nghệ. Tôi đồng sáng lập một startup công nghệ y tế vào năm 2020 và hiện đang theo học thạc sĩ khoa học máy tính tại Georgia Tech, chuyên sâu về học máy.

Chủ đề

Học AI với các khóa học này!

Tracks

Phát triển các ứng dụng trí tuệ nhân tạo

21 giờ
Học cách phát triển các ứng dụng được hỗ trợ bởi trí tuệ nhân tạo (AI) bằng cách sử dụng các công cụ phát triển AI mới nhất, bao gồm API OpenAI, Hugging Face và LangChain.
Xem chi tiếtRight Arrow
Bắt đầu khóa học
Xem thêmRight Arrow