一文講透MCP的原理及實踐

阿里妹導讀
MCP (Model Context Protocol) 代表了 AI 與外部工具和資料互動的標準建立。透過本文,我們可以瞭解到MCP的本質、價值、使用與開發。
MCP是什麼

MCP 是 Anthropic (Claude) 主導釋出的一個開放的、通用的、有共識的協議標準。

  • MCP 是一個標準協議,就像給 AI 大模型裝了一個 “萬能介面”,讓 AI 模型能夠與不同的資料來源和工具進行無縫互動。它就像 USB-C 介面一樣,提供了一種標準化的方法,將 AI 模型連線到各種資料來源和工具。

  • MCP 旨在替換碎片化的 Agent 程式碼整合,從而使 AI 系統更可靠,更有效。透過建立通用標準,服務商可以基於協議來推出它們自己服務的 AI 能力,從而支援開發者更快的構建更強大的 AI 應用。開發者也不需要重複造輪子,透過開源專案可以建立強大的 AI Agent 生態。
  • MCP 可以在不同的應用 / 服務之間保持上下文,增強整體自主執行任務的能力。

MCP 架構

MCP遵循客戶端 – 伺服器架構,包含以下幾個核心部分:
  • MCP 主機(MCP Hosts)發起請求的 AI 應用程式,比如聊天機器人、AI 驅動的 IDE 等。
  • MCP 客戶端(MCP Clients)在主機程式內部,與 MCP 伺服器保持 1:1 的連線。
  • MCP 伺服器(MCP Servers)為 MCP 客戶端提供上下文、工具和提示資訊。
  • 本地資源(Local Resources)本地計算機中可供 MCP 伺服器安全訪問的資源,如檔案、資料庫。
  • 遠端資源(Remote Resources)MCP 伺服器可以連線到的遠端資源,如透過 API 提供的資料。

為什麼需要 MCP 呢?

舉個例子,例如我們目前還不能同時透過某個 AI 應用來做到聯網搜尋、傳送郵件、釋出自己的部落格等等,這些功能單個實現都不是很難,但是如果要全部整合到一個系統裡面,就會變得遙不可及。可以想象一下日常開發中,有一個IDE ,我們可以透過 IDE 的 AI 來完成下面這些工作。
  • 詢問 AI 來查詢本地資料庫已有的資料來輔助開發
  • 詢問 AI 搜尋 Github Issue 來判斷某問題是不是已知的 bug
  • 透過 AI 將某個 PR 的意見傳送給同事的即時通訊軟體 (例如 Slack) 來 Code Review
  • 透過 AI 查詢甚至修改當前 AWS、Azure 的配置來完成部署
那有了 MCP 呢?其他服務都遵循 MCP 標準的話,就像萬能介面一樣,讓我們開發更高效了。

假設你正在使用一個 AI 程式設計助手來幫助你寫程式碼。這個 AI 助手就是一個 MCP 主機。它需要訪問一些外部資源,比如程式碼庫、文件或者除錯工具。MCP 伺服器就像是一箇中介,它連線了這些資源和 AI 助手。
  • 當你需要查詢某個函式的用法時,AI 助手透過 MCP 客戶端向 MCP 伺服器傳送請求。
  • MCP 伺服器接收到請求後,去程式碼庫或文件中查詢相關資訊。
  • 找到資訊後,MCP 伺服器將結果返回給 AI 助手。
  • AI 助手根據返回的資訊,生成一段程式碼或解釋,展示給你。
使用 MCP 後,你直接對 AI 說:“幫我查一下最近數學考試的平均分,把不及格的同學名單整理到值日表裡,並在微信群提醒他們補考。”AI 會自動完成:用 “萬能插頭” MCP 連線你的電腦,讀取 Excel 成績。用 MCP 連線微信,找到相關聊天記錄。用 MCP 修改線上文件,更新值日表。整個過程不需要你手動操作,資料也不會離開你的裝置,安全又高效。
所以,MCP 厲害的地方在於,不用重複造輪子。過去每個軟體(比如微信、Excel)都要單獨給 AI 做介面,現在 MCP 統一了標準,就像所有電器都用 USB-C 充電口,AI 一個介面就能連線所有工具。而且,資料不用上傳到雲端,AI 直接在本地處理。比如你的成績單隻存在自己電腦裡,AI 透過 MCP 讀取分析,但資料不會外洩。
MCP 會讓 AI 更 “懂” 上下文,比如你讓 AI “總結上週班會的重點”,它能自動調取會議錄音、聊天記錄、筆記文件,綜合這些資訊給你答案,而不是憑空編造。所以,MCP 為 AI 應用提供了一個強大的工具,使其能夠更靈活、更安全地與外部世界互動。
MCP原理

VS Function Call

MCP的誕生標誌著prompt engineering進入了一個新的發展階段,它透過提供更結構化的上下文資訊,顯著提升了模型的能力。在設計prompt時,我們的目標是能夠將更加具體的資訊(如本地檔案、資料庫內容或網路即時資料等)整合進來,從而使模型能夠更好地理解和解決實際問題。
回顧沒有 MCP 的時代,為了解決複雜問題,我們不得不手動從資料庫中篩選資訊或使用工具來檢索相關資訊,並將其逐一新增到 prompt 中。處理簡單問題時如需要大模型做歸納總結這種方法很奏效,但隨著問題複雜度的增加,這種方法變得越來越難以應對。
為了克服這些挑戰,許多大型語言模型(LLM)平臺(例如 OpenAI 和 Google)引入了 function call 功能。這一機制允許模型根據需要呼叫預定義函式以獲取資料或執行特定操作,大大提高了自動化程度。然而,function call 也有其侷限性,包括對平臺的高度依賴以及不同 LLM 平臺間 API 實現的差異,這使得開發者在切換模型時必須重寫程式碼,增加了開發成本。此外,還存在安全性和互動性等方面的挑戰。
實際上,資料和工具一直都在那裡,關鍵在於如何更智慧、更統一地將它們與模型連線起來。Anthropic 基於這樣的需求設計了 MCP,作為 AI 模型的“萬能介面卡”,讓 LLM 能夠輕鬆訪問資料或呼叫工具。具體而言,MCP 的優勢體現在以下幾個方面:
  • 生態系統MCP 提供了豐富的外掛庫,使您的 AI 應用可以直接利用。所有的服務提供商都可以按MCP協議進行接入,開者可以直接使用。可以預見,這個生態會越來越龐大,生態越龐大,AI的能力越強。
  • 相容性大家只要遵循一套協議,都可以實現“萬物互聯”。不限於特定的 AI 模型,任何支援 MCP 的模型都能靈活切換使用。

模型如何智慧選擇Agent/工具

MCP是核心是讓我們能方便地呼叫多個工具,那隨之而來的問題是LLM(模型)是在什麼時候確定使用哪些工具的呢? Anthropic 為我們提供了詳細的解釋,當用戶提出一個問題時:
  • 客戶端(Claude Desktop / Cursor)將問題傳送給 LLM。
  • LLM 分析可用的工具,並決定使用哪一個(或多個)。
  • 客戶端透過 MCP Server 執行所選的工具。
  • 工具的執行結果被送回給 LLM。
  • LLM 結合執行結果,歸納總結後生成自然語言展示給使用者!

先理解第一步模型如何確定該使用哪些工具?我們可以參考MCP官方提供的client example為講解示例,並對相關程式碼進行了簡化處理(移除了不影響邏輯理解的異常控制程式碼部分)。透過分析這段程式碼,可以看出模型是依靠prompt來識別當前可用的工具有哪些。具體做法是,我們將各個工具的使用描述以文字形式傳遞給模型,從而使模型能夠了解有哪些工具可供選擇,並基於即時情況做出最佳選擇。參考程式碼中的註釋部分:
... # 省略了無關的程式碼asyncdefstart(self):# 初始化所有的 mcp serverfor server in self.servers:await server.initialize()# 獲取所有的 tools 命名為 all_tools     all_tools = []for server in self.servers:         tools = await server.list_tools()         all_tools.extend(tools)# 將所有的 tools 的功能描述格式化成字串供 LLM 使用# tool.format_for_llm() 我放到了這段程式碼最後,方便閱讀。     tools_description = "\n".join(         [tool.format_for_llm() for tool in all_tools]     )# 詢問 LLM(Claude) 應該使用哪些工具。     system_message = ("You are a helpful assistant with access to these tools:\n\n"f"{tools_description}\n""Choose the appropriate tool based on the user's question. ""If no tool is needed, reply directly.\n\n""IMPORTANT: When you need to use a tool, you must ONLY respond with ""the exact JSON object format below, nothing else:\n""{\n"'    "tool": "tool-name",\n''    "arguments": {\n''        "argument-name": "value"\n'"    }\n""}\n\n""After receiving a tool's response:\n""1. Transform the raw data into a natural, conversational response\n""2. Keep responses concise but informative\n""3. Focus on the most relevant information\n""4. Use appropriate context from the user's question\n""5. Avoid simply repeating the raw data\n\n""Please use only the tools that are explicitly defined above."     )     messages = [{"role""system""content": system_message}]whileTrue:# Final... 假設這裡已經處理了使用者訊息輸入.         messages.append({"role""user""content": user_input})# 將 system_message 和使用者訊息輸入一起傳送給 LLM         llm_response = self.llm_client.get_response(messages)     ... # 後面和確定使用哪些工具無關classTool:"""Represents a tool with its properties and formatting."""def__init__(         self, name: str, description: str, input_schema: dict[strAny]     ) -> None:         self.name: str = name         self.description: str = description         self.input_schema: dict[strAny] = input_schema# 把工具的名字 / 工具的用途(description)和工具所需要的引數(args_desc)轉化為文字defformat_for_llm(self) -> str:"""Format tool information for LLM.         Returns:             A formatted string describing the tool.         """         args_desc = []if"properties"in self.input_schema:for param_name, param_info in self.input_schema["properties"].items():                 arg_desc = (f"- {param_name}{param_info.get('description''No description')}"                 )if param_name in self.input_schema.get("required", []):                     arg_desc += " (required)"                 args_desc.append(arg_desc)returnf""" Tool: {self.name} Description: {self.description} Arguments:{chr(10).join(args_desc)} """
那 tool 的描述和程式碼中的 input_schema 是從哪裡來的呢?透過進一步分析 MCP 的 Python SDK 原始碼可以發現:大部分情況下,當使用裝飾器 @mcp.tool() 來裝飾函式時,對應的 name 和 description 等其實直接源自使用者定義函式的函式名以及函式的 docstring 等。這裡僅擷取一小部分片段,想了解更多請參考原始程式碼。
@classmethoddeffrom_function(     cls,     fn: Callable,     name: str | None = None,     description: str | None = None,     context_kwarg: str | None = None, ) -> "Tool":"""Create a Tool from a function."""     func_name = name or fn.__name__ # 獲取函式名if func_name == "<lambda>":raise ValueError("You must provide a name for lambda functions")     func_doc = description or fn.__doc__ or""# 獲取函式 docstring     is_async = inspect.iscoroutinefunction(fn)     ... # 更多請參考原始程式碼...
總結:模型是透過 prompt engineering,即提供所有工具的結構化描述和 few-shot 的 example 來確定該使用哪些工具另一方面,Anthropic 肯定對 Claude 做了專門的訓練,畢竟是自家協議,Claude 更能理解工具的 prompt 以及輸出結構化的 tool call json 程式碼。

工具執行與結果反饋機制

工具的執行就比較簡單和直接了。承接上一步,我們把 system prompt(指令與工具呼叫描述)和使用者訊息一起傳送給模型,然後接收模型的回覆。當模型分析使用者請求後,它會決定是否需要呼叫工具:
  • 無需工具時:模型直接生成自然語言回覆。
  • 需要工具時:模型輸出結構化 JSON 格式的工具呼叫請求。
如果回覆中包含結構化 JSON 格式的工具呼叫請求,則客戶端會根據這個 json 程式碼執行對應的工具。具體的實現邏輯都在 process_llm_response 中,程式碼、邏輯非常簡單。
如果模型執行了 tool call,則工具執行的結果 result 會和 system prompt 和使用者訊息一起重新發送給模型,請求模型生成最終回覆。如果 tool call 的 json 程式碼存在問題或者模型產生了幻覺怎麼辦呢?透過閱讀程式碼 發現,我們會 skip 掉無效的呼叫請求。執行相關的程式碼與註釋如下:
... # 省略無關的程式碼asyncdefstart(self):     ... # 上面已經介紹過了,模型如何選擇工具whileTrue:# 假設這裡已經處理了使用者訊息輸入.         messages.append({"role""user""content": user_input})# 獲取 LLM 的輸出         llm_response = self.llm_client.get_response(messages)# 處理 LLM 的輸出(如果有 tool call 則執行對應的工具)         result = await self.process_llm_response(llm_response)# 如果 result 與 llm_response 不同,說明執行了 tool call (有額外資訊了)# 則將 tool call 的結果重新發送給 LLM 進行處理。if result != llm_response:             messages.append({"role""assistant""content": llm_response})             messages.append({"role""system""content": result})             final_response = self.llm_client.get_response(messages)             logging.info("\nFinal response: %s", final_response)             messages.append(                 {"role""assistant""content": final_response}             )# 否則代表沒有執行 tool call,則直接將 LLM 的輸出返回給使用者。else:             messages.append({"role""assistant""content": llm_response})
根據上述原理分析,可以看出工具文件至關重要。模型依賴於工具描述文字來理解和選擇適用的工具,這意味著精心編寫的工具名稱、文件字串(docstring)以及引數說明顯得尤為重要。鑑於MCP的選擇機制基於prompt實現,理論上任何模型只要能夠提供相應的工具描述就能與MCP相容使用。
MCP Server 開發實踐
對絕大部分 AI 開發者來說,除了瞭解MCP的原理,我們更關心 Server 的實現。因此,我這裡準備透過一個最簡單的示例來介紹如何實現一個 MCP Server。MCP servers 可以提供三種主要型別的功能:
  • Tools(工具):可以被 LLM 呼叫的函式或外部服務介面,需要使用者授權後執行。
  • Resources(資源):提供類似檔案的結構化資料,供LLM讀取和分析。如本地檔案內容、遠端api返回的json資料、資料庫查詢結果等。
  • Prompts(提示):預先編寫的模板,幫助使用者完成特定任務
本教程將主要關注工具(Tools)。

1、使用 LLM 構建 MCP 的最佳實踐

在開始之前,Anthropic 為我們提供了一個基於LLM 的 MCP Server 的最佳開發實踐(https://modelcontextprotocol.io/tutorials/building-mcp-with-llms),Guide裡面特意提到了,該實踐是基於Claude,也可以基於其它LLM。總結如下:
1.引入 domain knowledge (說人話就是,告訴他一些 MCP Server 開發的範例和資料)
  • 訪問 https://modelcontextprotocol.io/llms-full.txt 並複製完整的文件文字。(實測這個太長了,可以忽略)
  • 導航到 MCP TypeScript SDK 或 Python SDK Github 專案中並複製相關內容。
  • 把這些作為 prompt 輸入到你的 chat 對話中(作為 context)。
2.描述你的需求
  • 你的伺服器會開放哪些資源
  • 它會提供哪些工具
  • 它應該給出哪些引導或建議
  • 它需要跟哪些外部系統互動
給出一個 example prompt:
... (這裡是已經引入的 domain knowledge)打造一個 MCP 伺服器,它能夠:- 連線到我公司的 PostgreSQL 資料庫- 將表格結構作為資源開放出來- 提供執行只讀 SQL 查詢的工具- 包含常見資料分析任務的引導
剩下的部分也很重要,但是偏重於方法論,實踐性較弱,這裡就不展開了,推薦直接看連結:https://modelcontextprotocol.io/tutorials/building-mcp-with-llms。

2、手動實踐

本節內容主要參考了官方文件:Quick Start: For Server Developers(https://modelcontextprotocol.io/quickstart/server)。這裡準備了一個簡單的示例,使用 Python 實現一個 MCP Server,用來統計當前桌面上的 txt 檔案數量和獲取對應檔案的名字(你可以理解為一點用都沒有,但是它足夠簡單,主要是為了難以配置環境的讀者提供一個足夠短的實踐記錄)。以下實踐均執行在我的 MacOS 系統上。

Step1. 前置工作

  • 安裝 Claude Desktop。
  • Python 3.10+ 環境
  • Python MCP SDK 1.2.0+

Step2. 環境配置

# 安裝 uvcurl -LsSf https://astral.sh/uv/install.sh | sh# 建立專案目錄uv init txt_countercd txt_counter# 設定 Python 3.10+ 環境echo"3.11" > .python-version# 建立虛擬環境並激活uv venvsource .venv/bin/activate# Install dependenciesuv add "mcp[cli]" httpx# Create our server filetouch txt_counter.py
Question: 什麼是 uv 呢和 conda 比有什麼區別?
Answer: 一個用 Rust 編寫的超快速 (100x) Python 包管理器和環境管理工具,由 Astral 開發。定位為 pip 和 venv 的替代品,專注於速度、簡單性和現代 Python 工作流。

Step3. 構造一個 prompt

"""... (這裡是已經引入的 domain knowledge)"""打造一個 MCP 伺服器,它能夠:- 功能:    - 統計當前桌面上的 txt 檔案數量    - 獲取對應檔案的名字要求:- 不需要給出 prompt 和 resource 相關程式碼。- 你可以假設我的桌面路徑為 /Users/{username}/Desktop
Domain Knowledge 複製於 MCP Python SDK的README檔案(https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/refs/heads/main/README.md

Step4. 實現 MCP Server

以下程式碼由 Claude 3.7 直接生成。當然這裡主要是因為需求足夠簡單,當需要實現一個複雜的 MCP Server 時,可能需要多步的引導和 Debug 才能得到最終的程式碼。
import osfrom pathlib import Pathfrom mcp.server.fastmcp import FastMCP# 建立 MCP Servermcp = FastMCP("桌面 TXT 檔案統計器")@mcp.tool()defcount_desktop_txt_files() -> int:"""Count the number of .txt files on the desktop."""# Get the desktop path    username = os.getenv("USER"or os.getenv("USERNAME")    desktop_path = Path(f"/Users/{username}/Desktop")# Count .txt files    txt_files = list(desktop_path.glob("*.txt"))returnlen(txt_files)@mcp.tool()deflist_desktop_txt_files() -> str:"""Get a list of all .txt filenames on the desktop."""# Get the desktop path    username = os.getenv("USER"or os.getenv("USERNAME")    desktop_path = Path(f"/Users/{username}/Desktop")# Get all .txt files    txt_files = list(desktop_path.glob("*.txt"))# Return the filenamesifnot txt_files:return"No .txt files found on desktop."# Format the list of filenames    file_list = "\n".join([f"- {file.name}"for file in txt_files])returnf"Found {len(txt_files)} .txt files on desktop:\n{file_list}"if __name__ == "__main__":# Initialize and run the server    mcp.run()
任務非常簡單,只需要呼叫非常基本的os就可以完成。

Step5. 測試 MCP Server

$ mcp dev txt_counter.pyStarting MCP inspector...Proxy server listening on port 3000MCP Inspector is up and running at http://localhost:5173
之後進入到給出的連結中,你大概能按下圖進行操作:

Step6. 接入 Claude

最後一步就是把我們寫好的 MCP 接入到 Claude Desktop 中。流程如下:
# 開啟 claude_desktop_config.json (MacOS / Linux)# 如果你用的是 cursor 或者 vim 請更換對應的命令code ~/Library/Application\ Support/Claude/claude_desktop_config.json
在配置檔案中新增以下內容,記得替換相關路徑為實際路徑。
{"mcpServers":{"txt_counter":{"command":"/opt/homebrew/bin/uv","args":["--directory","/Users/yangfan/mcp/txt_counter","run","txt_counter.py"]}}}
uv最好是絕對路徑,推薦使用 which uv 獲取。
配置好後重啟 Claude Desktop,如果沒問題就能看到對應的 MCP Server 了。

Step7. 實際使用

接下來,我們透過一個簡單的 prompt 進行實際測試:
能推測我當前桌面上 txt 檔名的含義嗎?
它可能會請求你的使用許可權,如圖一所示,你可以點選 Allow for This Chat

看起來我們 MCP Server 已經正常工作了!

3、MCP Server Debug

Debug 是一個非常複雜的話題,這裡直接推薦官方的教程:
  • Official Tutorial: Debugging:https://modelcontextprotocol.io/docs/tools/debugging
  • Official Tutorial: Inspector:https://modelcontextprotocol.io/docs/tools/inspector
總結
MCP (Model Context Protocol) 代表了 AI 與外部工具和資料互動的標準建立。透過本文,我們可以瞭解到:
  • MCP 的本質它是一個統一的協議標準,使 AI 模型能夠以一致的方式連線各種資料來源和工具,類似於 AI 世界的"USB-C"介面。
  • MCP 的價值它解決了傳統 function call 的平臺依賴問題,提供了更統一、開放、安全、靈活的工具呼叫機制,讓使用者和開發者都能從中受益。
  • 使用與開發對於普通使用者,MCP 提供了豐富的現成工具,使用者可以在不瞭解任何技術細節的情況下使用;對於開發者,MCP 提供了清晰的架構和 SDK,使工具開發變得相對簡單。
MCP 還處於發展初期,但其潛力巨大。更重要的是生態,基於統一標準下構築的生態也會正向的促進整個領域的發展。我們可以看看MCP工具平臺:https://mcp.so/

通義千問和LangChain搭建對話服務
結合通義千問和LangChain技術構建高效的對話模型,該模型基於自然語言處理技術提升語義理解和使用者互動體驗。    
點選閱讀原文檢視詳情。

相關文章