MCP 課程文件
MCP 客戶端
並獲得增強的文件體驗
開始使用
MCP 客戶端
現在我們有了帶有標籤工具的 MCP 伺服器,我們需要建立一個可以與這些工具互動的客戶端。MCP 客戶端充當我們的 webhook 處理程式和 MCP 伺服器之間的橋樑,使我們的代理能夠使用 Hub 標籤功能。
出於本專案的目的,我們將構建一個 API 和一個 Gradio 應用程式。API 將用於測試 MCP 伺服器和 webhook 監聽器,Gradio 應用程式將用於透過模擬的 webhook 事件測試 MCP 客戶端。
出於教育目的,我們將在同一個倉庫中構建 MCP 伺服器和 MCP 客戶端。在實際應用中,您可能會為 MCP 伺服器和 MCP 客戶端分別建立一個倉庫。實際上,您可能只構建其中一個元件。
理解 MCP 客戶端架構
在我們的應用程式中,MCP 客戶端整合到主 FastAPI 應用程式 (app.py
) 中。它建立並管理與 MCP 伺服器的連線,為工具執行提供無縫介面。
基於代理的 MCP 客戶端
我們使用內建 MCP 支援的 huggingface_hub
Agent 類。這在一個元件中提供了語言模型功能和 MCP 工具整合。
1. 代理配置
讓我們從設定代理配置並理解每個元件開始
from huggingface_hub.inference._mcp.agent import Agent
from typing import Optional, Literal
# Configuration
HF_TOKEN = os.getenv("HF_TOKEN")
HF_MODEL = os.getenv("HF_MODEL", "microsoft/DialoGPT-medium")
DEFAULT_PROVIDER: Literal["hf-inference"] = "hf-inference"
# Global agent instance
agent_instance: Optional[Agent] = None
我們從必要的匯入和配置開始。全域性 agent_instance
變數確保我們只建立一次代理並在多個請求中重用它。這對於效能很重要,因為代理初始化可能很耗時。
現在讓我們實現建立和管理代理的函式
async def get_agent():
"""Get or create Agent instance"""
print("🤖 get_agent() called...")
global agent_instance
if agent_instance is None and HF_TOKEN:
print("🔧 Creating new Agent instance...")
print(f"🔑 HF_TOKEN present: {bool(HF_TOKEN)}")
print(f"🤖 Model: {HF_MODEL}")
print(f"🔗 Provider: {DEFAULT_PROVIDER}")
該函式首先檢查是否已經存在代理例項。這種單例模式可以防止不必要的重新建立並確保一致的狀態。
讓我們繼續代理建立
try:
agent_instance = Agent(
model=HF_MODEL,
provider=DEFAULT_PROVIDER,
api_key=HF_TOKEN,
servers=[
{
"type": "stdio",
"config": {
"command": "python",
"args": ["mcp_server.py"],
"cwd": ".",
"env": {"HF_TOKEN": HF_TOKEN} if HF_TOKEN else {},
},
}
],
)
print("✅ Agent instance created successfully")
print("🔧 Loading tools...")
await agent_instance.load_tools()
print("✅ Tools loaded successfully")
except Exception as e:
print(f"❌ Error creating/loading agent: {str(e)}")
agent_instance = None
這是重要部分!讓我們分解代理配置
代理引數
model
:將對工具使用進行推理的語言模型provider
:如何訪問模型(Hugging Face 推理提供者)api_key
:Hugging Face API 金鑰
MCP 伺服器連線
type: "stdio"
:透過標準輸入/輸出連線到 MCP 伺服器command: "python"
:將我們的 MCP 伺服器作為 Python 子程序執行args: ["mcp_server.py"]
:要執行的指令碼檔案env
:將 HF_TOKEN 傳遞給伺服器程序
stdio
連線型別意味著代理將您的 MCP 伺服器作為子程序啟動,並透過標準輸入/輸出與其通訊。這非常適合開發和單機部署。
load_tools()
呼叫至關重要 - 它發現 MCP 伺服器提供了哪些工具,並使它們可供代理的推理引擎訪問。
這完成了我們帶有適當錯誤處理和日誌記錄的代理管理函式。
工具發現與使用
一旦建立了代理並載入了工具,它就可以自動發現和使用 MCP 工具。這就是代理方法的真正強大之處。
可用工具
代理自動發現我們的 MCP 工具
get_current_tags(repo_id: str)
- 檢索現有儲存庫標籤add_new_tag(repo_id: str, new_tag: str)
- 透過拉取請求新增新標籤
代理不僅僅是盲目地呼叫這些工具——它會根據您給出的提示來推理何時以及如何使用它們。
工具執行示例
代理如何智慧地使用工具:
# Example of how the agent would use tools
async def example_tool_usage():
agent = await get_agent()
if agent:
# The agent can reason about which tools to use
response = await agent.run(
"Check the current tags for microsoft/DialoGPT-medium and add the tag 'conversational-ai' if it's not already present"
)
print(response)
請注意,我們如何給代理一個自然語言指令,它會找出
- 首先呼叫
get_current_tags
檢視現有標籤 - 檢查
conversational-ai
是否已存在 - 如果不存在,則呼叫
add_new_tag
新增它 - 提供其操作的摘要
這比直接呼叫工具智慧得多!
與 Webhook 處理整合
現在讓我們看看 MCP 客戶端如何整合到我們的 webhook 處理管道中。這就是一切的結合點。
1. 標籤提取和處理
這是處理 webhook 事件並使用我們的 MCP 代理的主要函式
async def process_webhook_comment(webhook_data: Dict[str, Any]):
"""Process webhook to detect and add tags"""
print("🏷️ Starting process_webhook_comment...")
try:
comment_content = webhook_data["comment"]["content"]
discussion_title = webhook_data["discussion"]["title"]
repo_name = webhook_data["repo"]["name"]
# Extract potential tags from the comment and discussion title
comment_tags = extract_tags_from_text(comment_content)
title_tags = extract_tags_from_text(discussion_title)
all_tags = list(set(comment_tags + title_tags))
print(f"🔍 All unique tags: {all_tags}")
if not all_tags:
return ["No recognizable tags found in the discussion."]
第一部分從評論內容和討論標題中提取並組合標籤。我們使用集合來去除任何在兩個地方都出現的重複標籤。
同時處理評論和討論標題增加了我們捕獲相關標籤的機會。使用者可能會在標題中提及標籤,例如“缺少 pytorch 標籤”,或者在評論中提及“這需要 #transformers”。
接下來,我們獲取代理並處理每個標籤
# Get agent instance
agent = await get_agent()
if not agent:
return ["Error: Agent not configured (missing HF_TOKEN)"]
# Process each tag
result_messages = []
for tag in all_tags:
try:
# Use agent to process the tag
prompt = f"""
For the repository '{repo_name}', check if the tag '{tag}' already exists.
If it doesn't exist, add it via a pull request.
Repository: {repo_name}
Tag to check/add: {tag}
"""
print(f"🤖 Processing tag '{tag}' for repo '{repo_name}'")
response = await agent.run(prompt)
# Parse agent response for success/failure
if "success" in response.lower():
result_messages.append(f"✅ Tag '{tag}' processed successfully")
else:
result_messages.append(f"⚠️ Issue with tag '{tag}': {response}")
except Exception as e:
error_msg = f"❌ Error processing tag '{tag}': {str(e)}"
print(error_msg)
result_messages.append(error_msg)
return result_messages
這裡的關鍵在於,我們為每個標籤給代理一個清晰、結構化的提示。然後代理會
- 瞭解它需要首先檢查當前標籤
- 與我們要新增的新標籤進行比較
- 如果需要,建立拉取請求
- 返回其操作的摘要
這種方法自動處理工具編排的複雜性。
2. 標籤提取邏輯
讓我們檢查一下饋入 MCP 處理的標籤提取邏輯
import re
from typing import List
# Recognized ML/AI tags for validation
RECOGNIZED_TAGS = {
"pytorch", "tensorflow", "jax", "transformers", "diffusers",
"text-generation", "text-classification", "question-answering",
"text-to-image", "image-classification", "object-detection",
"fill-mask", "token-classification", "translation", "summarization",
"feature-extraction", "sentence-similarity", "zero-shot-classification",
"image-to-text", "automatic-speech-recognition", "audio-classification",
"voice-activity-detection", "depth-estimation", "image-segmentation",
"video-classification", "reinforcement-learning", "tabular-classification",
"tabular-regression", "time-series-forecasting", "graph-ml", "robotics",
"computer-vision", "nlp", "cv", "multimodal",
}
這份精選的已識別標籤列表有助於我們專注於相關的 ML/AI 標籤,並避免向儲存庫新增不適當的標籤。
現在是提取函式本身
def extract_tags_from_text(text: str) -> List[str]:
"""Extract potential tags from discussion text"""
text_lower = text.lower()
explicit_tags = []
# Pattern 1: "tag: something" or "tags: something"
tag_pattern = r"tags?:\s*([a-zA-Z0-9-_,\s]+)"
matches = re.findall(tag_pattern, text_lower)
for match in matches:
tags = [tag.strip() for tag in match.split(",")]
explicit_tags.extend(tags)
# Pattern 2: "#hashtag" style
hashtag_pattern = r"#([a-zA-Z0-9-_]+)"
hashtag_matches = re.findall(hashtag_pattern, text_lower)
explicit_tags.extend(hashtag_matches)
# Pattern 3: Look for recognized tags mentioned in natural text
mentioned_tags = []
for tag in RECOGNIZED_TAGS:
if tag in text_lower:
mentioned_tags.append(tag)
# Combine and deduplicate
all_tags = list(set(explicit_tags + mentioned_tags))
# Filter to only include recognized tags or explicitly mentioned ones
valid_tags = []
for tag in all_tags:
if tag in RECOGNIZED_TAGS or tag in explicit_tags:
valid_tags.append(tag)
return valid_tags
此函式使用多種策略提取標籤
- 顯式模式:“tags: pytorch, transformers” 或 “tag: nlp”
- 話題標籤:“#pytorch #nlp”
- 自然提及:“This transformers model does text-generation”
驗證步驟確保我們只建議適當的標籤,防止新增垃圾郵件或不相關的標籤。
效能考量
在構建生產 MCP 客戶端時,效能對於保持響應式 webhook 處理至關重要。讓我們看看我們做出的一些考慮。
1. 代理單例模式
代理一次建立並重復使用,以避免
- 重複的 MCP 伺服器啟動開銷
- 工具載入延遲
- 連線建立成本
這種模式對於需要快速響應的 webhook 處理程式至關重要。
2. 非同步處理
所有 MCP 操作都是非同步的,以便
- 併發處理多個 webhook 請求
- 避免阻塞主 FastAPI 執行緒
- 提供響應迅速的 webhook 響應
非同步性質允許您的 webhook 處理程式在後臺處理標籤時接受新請求。
3. 後臺任務處理
FastAPI 內建了 BackgroundTasks
類,可用於在後臺執行任務。這對於執行長時間執行的任務而不會阻塞主執行緒非常有用。
from fastapi import BackgroundTasks
@app.post("/webhook")
async def webhook_handler(request: Request, background_tasks: BackgroundTasks):
"""Handle webhook and process in background"""
# Validate webhook quickly
if request.headers.get("X-Webhook-Secret") != WEBHOOK_SECRET:
return {"error": "Invalid secret"}
webhook_data = await request.json()
# Process in background to return quickly
background_tasks.add_task(process_webhook_comment, webhook_data)
return {"status": "accepted"}
此模式可確保 webhook 響應速度快(不到 1 秒),同時允許在後臺進行復雜的標籤處理。
Webhook 端點應在 10 秒內響應,否則平臺可能會將其視為超時。使用後臺任務可確保您始終能夠快速響應,同時非同步處理複雜任務。
後續步驟
實現了 MCP 客戶端後,我們現在可以
- 實現 Webhook 監聽器 - 建立接收 Hub 事件的 FastAPI 端點
- 整合所有元件 - 將 webhooks、客戶端和伺服器連線成一個完整的系統
- 新增測試介面 - 建立用於開發和監控的 Gradio 介面
- 部署和測試 - 在生產環境中驗證完整系統
在下一節中,我們將實現 webhook 監聽器,它將觸發我們的 MCP 支援的標籤代理。
huggingface_hub
中的 Agent 類同時提供了 MCP 工具整合和語言模型推理,使其非常適合構建像我們的 PR 代理這樣的智慧自動化工作流。