大模型系列22-MCP

大模型系列22-MCP

  • 玩轉 MCP 協議:用 Cline + DeepSeek 接入天氣服務
    • 什么是 MCP?
    • 環境準備:VScode + Cline + DeepSeek
      • **配置 DeepSeek 模型:**
      • **配置 MCP 工具**
        • **uvx是什么?**
        • **安裝 uv(會自動有 uvx 命令)**
        • **驗證uvx的安裝**:`uvx pycowsay 'hello world'`
        • **通過uvx 手動安裝 fetch 工具**
        • uv 命令找不到
        • **使用 fetch 工具**
    • MCP 示例:查詢紐約天氣
    • 步驟一:配置 MCP Server
    • 步驟二:安裝 uv 和運行測試
    • 步驟三:寫一個 MCP 工具服務 weather.py
    • 步驟四:手動驗證 weather.py
      • 直接通過 Cline 來發起調用
      • 全人肉交互式操作
    • 步驟五:Cline 和 MCP 之間的工具代理
      • 中間日志代理工具
      • mcp server 測試客戶端
    • 步驟六:模型調用工具示例流程
    • 總結:MCP 的本質與價值

玩轉 MCP 協議:用 Cline + DeepSeek 接入天氣服務

本文記錄了使用 MCP(Model Context Protocol)協議,通過 Cline 插件接入 DeepSeek 模型,并構建一個天氣查詢工具的完整過程。包括從安裝環境、插件配置,到實現 MCP Server 的交互流程,適合對 AI 工具鏈、智能體、RAG 和插件生態感興趣的開發者參考。
本文只是一個自學的文章,其內容參考自bilibili:馬克的技術工作坊,三篇 鏈接,這個講的很好,建議大家都可以去看看他的三個系列


在開始之前,我們先借用視頻中的一張圖,來看一下 MCP 處于的位置,以及他是如何和我們的模型進行溝通的。這里的用戶是我們,MCP Server 是手寫的一個 python Weather 工具,它可以接收經緯度,返回天氣情況。模型是 DeepSeek 模型,我事先充值了 10元,以實現 API 調用。Cline 是一個 vscode 插件,它是一個 Agent(也有人將它稱為 MCP Host,我個人將它成為Agent,因為它從功能上就像是一個 Agent 智能體),用于連接 MCP Server 和 模型的橋梁。
在這里插入圖片描述
啟動階段

  • 啟動 MCP Server(如 weather.py)。
  • Cline 啟動后向 MCP Server 打招呼,并請求工具列表。
  • MCP Server 返回可用工具(如 get_forecast, get_alerts)。

用戶提問

  • 用戶輸入自然語言問題,向Cline發問,如:“紐約明天的天氣怎么樣?”
  • Cline將問題以及可用的工具發送給模型

模型接收用戶問題

  • 分析出這是一個需要天氣信息的任務。
  • 判斷出可以調用 get_forecast 工具,并準備參數(如經緯度),將其發給Cline

Cline 調用工具

  • Cline 發送工具調用請求(如 get_forecast)到 MCP Server。
  • MCP Server 執行本地 Python 工具函數,處理請求。

MCP 返回結果

  • MCP Server 將結果返回給 Cline。
  • Cline 將結果傳給模型,模型進行結果理解和語言組織。

模型生成回復

  • 模型將調用結果包裝成自然語言輸出。

用戶收到回答

  • 用戶最終看到如:“紐約明天的天氣是多云,最高 25°C,最低 18°C” 這樣的自然語言回復。

什么是 MCP?

MCP(Model Context Protocol)是一個用于定義模型與外部工具如何交互的協議。它可以告訴大模型有哪些工具可用、工具的參數和返回格式,然后觸發外部 Agent(如Cline)調用工具,實現讓大模型更好的使用工具的能力。說是大模型使用工具,其實是Agent發送給模型自己的工具列表,模型判斷出要使用什么工具,然后模型告訴Agent去調用,Agent執行工具調用之后,然后將調用結果返回給模型,由模型決策后續的行為(或者是直接總結工具的結果返回給用戶,或者是進一步調用工具等)。

簡單理解:MCP 就是“大模型如何用工具”的語言。


環境準備:VScode + Cline + DeepSeek

啟動 VSCode 之后,我們搜索 Cline 插件并安裝。安裝 Cline 插件后,我們可以將本地工具注冊為 MCP 服務,通過 DeepSeek 等大模型驅動調用。這包括配置模型以及配置 MCP 工具兩個步驟。

配置 DeepSeek 模型:

  • 先訪問DeepSeek官網,進行API充值。充值還挺方便的,前天充值的10元,經過這幾天測試驗證MCP的調用,剛才看了下還有 9.57 元。
  • 然后訪問API KEYS,創建一個 API KEY,用于 MCP 的驗證。我們在 Cline 插件中,點擊 Setttings,即可進行如下圖的 API Provider 等配置,填入 API KEY,就可以通過 Cline 使用 DeepSeek 啦
    配置 DeepSeek API

發送一句 “Hi”,測試模型是否接入成功:模型回答了一些內容。
DeepSeek 接入測試

配置 MCP 工具

我們有很多現成的 MCP 工具可以用,如 mcp.somcpmarket.comsmithery.ai

我們先嘗試一個網頁抓取的 MCP 應用:https://mcp.so/server/fetch/modelcontextprotocol
。它叫 fetch,是用于抓取網頁的。

那如何在 Cline 中安裝該fetch工具?我們需要進行如下圖的操作流程,點擊 Cline,打開它的 MCP Servers,然后點擊 Configure MCP Servers,它會自動打開 cline_mcp_settting.json 文件。如果你是第一次開的時候,它的內容為空。
在這里插入圖片描述

我們修改 cline_mcp_settting.json 復制以下內容,該 fetch 工具就會被自動配置到 Cline 中。我們使用 uvx 來啟動這個 fetch 工具。

"mcpServers": {"fetch": {"command": "uvx","args": ["mcp-server-fetch"]}
}

這里如果你看到 fetch 工具旁邊有報錯,先不用慌,它的報錯有以下幾個原因:
(1)沒有安裝 uvx
(2)下載 fetch MCP 工具失敗
(3)安裝了 uvx 但是找不到 uvx 的命令
后面會給出詳細的解決方案。

uvx是什么?

uvx 是 Astral 的 uv 包管理工具 中的一個子命令,用于在隔離環境中運行 Python 腳本或工具,類似于 npx 之于 npm。它就是直接搞一個臨時隔離環境,在隔離環境中執行命令,不會影響現有的 python 環境。

工具作用
uvpip + venv 的超快替代品(用于安裝、構建、運行 Python 項目)
uvx在臨時隔離環境中運行一個 PyPI 包提供的命令行工具(無需手動安裝)
安裝 uv(會自動有 uvx 命令)
curl -Ls https://astral.sh/uv/install.sh | bash 實在是太慢了,替換成 pip 清華源安裝方式
pip install uv -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 
驗證uvx的安裝uvx pycowsay 'hello world'

在這里插入圖片描述

通過uvx 手動安裝 fetch 工具
uvx mcp-server-fetch --index-url https://pypi.tuna.tsinghua.edu.cn/simple

在這里插入圖片描述
Cline 在第一次啟動 fetch 的時候,它會自動下載這個 fetch 工具,但是我當時的嘗試來看,它最終是自動下載不成功(fetch 那個地方會報告 Error),于是我轉為手動下載,執行如下的下載命令即可:
uvx mcp-server-fetch --index-url https://pypi.tuna.tsinghua.edu.cn/simple

uv 命令找不到

如果在 VScode 報找不到 uv 的錯誤(如下圖所示,spawn uv ENOENT 表明 uv 命令找不到,這里圖中是 weather,不過 fetch工具也一樣),這應該是 vscode 沒有讀取到正確的 uv 環境變量路徑。我的操作方案是通過 WSL 命令行運行 code .命令啟動 VSCode,這可以確保環境變量都被正確加載。當執行完畢后,再點擊 Retry Connection 的時候,就不會報告 uv ENONT 的錯誤了。

處理好之后,如果 fetch 還沒有正常,它會有個 Reconnect按鈕,點擊 fetch 處有個 Reconnect 重連,一般就可以成功啦。fetch 工具后面有兩個按鈕(如果都為綠色,表明成功):第一個是 啟用/禁用,第二個是工具是否正常。
在這里插入圖片描述

使用 fetch 工具

我們向 Cline 提問進行抓取網頁

請抓取下面這個網頁的內容,并將其轉換為 markdown 后放到項目目錄里面的 guides.md 文件中:
https://docs.astral.sh/uv/guides/install-python/

如下圖所示,我們這個問題送給 DeepSeek模型 之后,它知道我們要使用 fetch 工具,并返回給 Cline 操作步驟,Cline 收到 fetch工具獲取網頁內容 這個命令后,會調用 fetch 工具獲取網頁內容,并將內容發送給 模型,模型會解析網頁內容,然后返回給 Cline 寫入guides.md文件 的命令,Cline 會執行該寫入命令,最終的內容如 guides.md 所示。

這里面我們忽略了一些細節

  1. 模型怎么知道我們有 fetch 這個工具?模型它自己不知道,其實是我們將上面的抓取問題給 Cline 之后,Cline 會將問題以及 Cline 當前安裝的 MCP 工具命令和參數類型信息全部發送給模型,模型才知道我們有 fetch 這個工具。
  2. 是模型調用的 fetch 工具嗎?不是模型來直接調用工具,是模型收到了問題和工具列表,它分析一通,給出 Cline 的回復是可以調用 fetch 這個命令,Cline 收到模型給出的工具調用指令,然后 Cline 發起 fetch 工具的調用,并將工具的結果返回給模型。
    在這里插入圖片描述

MCP 示例:查詢紐約天氣

假設我們向模型提問:

明天紐約的天氣怎么樣?

因為模型基于歷史數據訓練出來的,它是無法預知明天的天氣怎么樣的,這必須需要依賴 MCP 工具來完成。我們在隨后的內容中講述如何來創建一個簡單的 MCP 工具來獲取天氣信息。假設我們有了一個 MCP weather 的工具,
按照 MCP 的設計,模型會解析出“需要天氣數據”的需求,并調用本地的 weather MCP Server 來完成查詢。


步驟一:配置 MCP Server

我們需要先構建一個 MCP 協議的本地服務,它通過標準輸入輸出 (stdio) 和 cline 通信。

{"mcpServers": {"weather": {"timeout": 60,"command": "uv","args": ["--directory","/mnt/c/workspace/llm/mcp/weather","run","weather.py"],"transportType": "stdio"}}
}

字段說明如下:

字段含義
command使用 uv 啟動 Python 腳本
args啟動時附帶的參數數組:
--directory 指定工作目錄
"run" 是執行命令
"weather.py" 是要運行的腳本。
transportType使用 stdio 方式通信,意味著Cline與工具通過 I/O 管道進行通信
timeout超時時間 60 秒

上述配置相當于 Cline 執行了這樣的一個命令:

uv --directory /mnt/c/workspace/llm/mcp/weather run weather.py

我們通過修改 cline_mcp_settting.json 文件,給 Cline 增加了一個獲取天氣的 MCP 工具。不過這里我們還沒有這樣的 weather.py 文件,因此 Cline 的工具列表中 weather 工具并沒有正常工作,我們將在隨后的章節中增加相應的代碼,以完成一個獲取天氣的工具應用。


步驟二:安裝 uv 和運行測試

前面已經介紹過 uv 的細節,如果你已經安裝過,請忽略。
使用 pip 安裝 uv

pip install uv -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple

驗證安裝:

uvx pycowsay 'hello world'

步驟三:寫一個 MCP 工具服務 weather.py

以下是核心實現,使用 fastmcp 框架 + httpx 請求 OpenWeather 或 NWS 數據。

from fastmcp import FastMCP
import httpx
from typing import Anymcp = FastMCP("weather")
NWS_API_BASE = "https://api.weather.gov"async def make_nws_request(url: str) -> dict[str, Any] | None:...@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:...@mcp.tool()
async def get_alerts(state: str) -> str:...if __name__ == "__main__":mcp.run(transport='stdio')

工具被 @mcp.tool() 裝飾后,我們就可以通過 MCP 協議和該工具進行交互了。mcp.tool裝飾器將代碼轉換為 tool

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:"""Get weather forecast for a location.Args:latitude: Latitude of the locationlongitude: Longitude of the location"""

轉換為這樣的一個 tool

{"name": "get_forecast","description": "Get weather forecast for a location.\n\n    Args:\n        latitude: Latitude of the location\n        longitude: Longitude of the location\n    ","inputSchema": {"properties": {"latitude": {"title": "Latitude","type": "number"},"longitude": {"title": "Longitude","type": "number"}},"required": ["latitude","longitude"],"type": "object"}}

MCP 協議規定了每個mcp server有哪些函數以及每個函數的調用方法,它并沒有規定和模型的交互方式

weather.py 全部代碼如下:

from typing import Any
import httpx
from fastmcp import FastMCP# Initialize FastMCP server
mcp = FastMCP("weather")# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"async def make_nws_request(url: str) -> dict[str, Any] | None:"""Make a request to the NWS API with proper error handling."""headers = {"User-Agent": USER_AGENT,"Accept": "application/geo+json"}async with httpx.AsyncClient() as client:try:response = await client.get(url, headers=headers, timeout=30.0)response.raise_for_status()return response.json()except Exception:return Nonedef format_alert(feature: dict) -> str:"""Format an alert feature into a readable string."""props = feature["properties"]return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""@mcp.tool()
async def get_alerts(state: str) -> str:"""Get weather alerts for a US state.Args:state: Two-letter US state code (e.g. CA, NY)"""url = f"{NWS_API_BASE}/alerts/active/area/{state}"data = await make_nws_request(url)if not data or "features" not in data:return "Unable to fetch alerts or no alerts found."if not data["features"]:return "No active alerts for this state."alerts = [format_alert(feature) for feature in data["features"]]return "\n---\n".join(alerts)@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:"""Get weather forecast for a location.Args:latitude: Latitude of the locationlongitude: Longitude of the location"""# First get the forecast grid endpointpoints_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"points_data = await make_nws_request(points_url)if not points_data:return "Unable to fetch forecast data for this location."# Get the forecast URL from the points responseforecast_url = points_data["properties"]["forecast"]forecast_data = await make_nws_request(forecast_url)if not forecast_data:return "Unable to fetch detailed forecast."# Format the periods into a readable forecastperiods = forecast_data["properties"]["periods"]forecasts = []for period in periods[:5]:  # Only show next 5 periodsforecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""forecasts.append(forecast)return "\n---\n".join(forecasts)if __name__ == "__main__":# Initialize and run the servermcp.run(transport='stdio')

weather.py 代碼放到對應的目錄/mnt/c/workspace/llm/mcp/weather下,先手動命令行執行:uv --directory /mnt/c/workspace/llm/mcp/weather run weather.py,確認可以執行成功。
在這里插入圖片描述
在 cline 中找到 Installed MCP 工具后,對 weather 點擊 Reconnect 即可看到 weather 正確連接了。
在這里插入圖片描述


步驟四:手動驗證 weather.py

當我們開發了一個 weather.py 的工具,我們有兩種驗證它的方案,一種是直接通過 Cline 來發起調用;另外一種是全人肉交互式操作。通過 Cline 發起調用可以感受到模型調用工具的樂趣;而通過全人肉的方式相當于能夠扒開 Cline 的內部實現,更清晰的理解工具調用的整體流程圖。

直接通過 Cline 來發起調用

從該圖可以看出,模型識別到了問題以及 weather 工具,且回復使用工具 weather來獲取天氣信息,并給出坐標。
在這里插入圖片描述
weather 工具收到坐標后,從NWS_API_BASE = “https://api.weather.gov” 網站獲取到天氣信息后,將內容(下圖的一堆英文天氣信息)返回給 Cline,Cline將英文天氣內容發給模型,模型經過一番總結后,給出了最終的回復:

紐約明天(5月26日)天氣預報:
- 白天:大部分時間晴朗,最高氣溫約71°F(22°C),西北風2-7 mph
- 夜間:部分多云,最低氣溫約57°F(14°C),西南風約3 mph

在這里插入圖片描述

全人肉交互式操作

手動啟動 weather.py:uv --directory /mnt/c/workspace/llm/mcp/weather run weather.py
在這里插入圖片描述
使用 MCP 協議和該工具進行交互,由于它是通過標準輸入輸出來交互的,因此我們可以直接將下面的內容逐條粘貼到上述命令窗口中,和它進行交互:

 // 初始化請求
{"method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "cline", "version": "1.0"}}, "jsonrpc": "2.0", "id": 0}// 通知:已初始化
{"method": "notifications/initialized", "jsonrpc": "2.0"}// 工具列表請求
{"method": "tools/list", "jsonrpc": "2.0", "id": 1}// 發起獲取天氣的請求 
{"method":"tools/call","params":{"name":"get_forecast","arguments":{"latitude":40.7128,"longitude":-74.006}},"jsonrpc":"2.0","id":4}

上述命令會得到以下的輸出,合并起來就有三大步:
(1):和MCP工具進行握手
(2):獲取其支持的工具列表:get_alerts, get_forecast
(3):調用 get_forecast 工具
在這里插入圖片描述

步驟五:Cline 和 MCP 之間的工具代理

你可能會有疑問,如何才能知道用什么格式的內容和工具進行交互呢?它的輸入的格式為什么是這個樣子呢?我們可以通過在 Cline 和 MCP 工具中間增加一個中間代理,來截獲 Cline 到 weather 的消息協議,進而就可以手動來發起工具調用了。
在這里插入圖片描述

中間日志代理工具

使用 GPT 寫了一個代碼,用于在 Cline 和 MCP工具之間加一個代理。 mcp_logger.py 是一個 中間日志代理工具,用于在運行 MCP Server(如 weather.py)時,記錄Cline與工具之間的輸入輸出交互內容,主要功能如下:

  1. 攔截標準輸入(stdin)傳給 MCP Server 的數據(例如 Cline 發來的 JSON-RPC 請求),并記錄為 "輸入 >>> ...";

  2. 攔截 MCP Server 標準輸出(stdout)返回給 Cline 的響應結果,并記錄為 "輸出 >>> ...";

  3. 所有的交互式日志都會追加寫入 mcp_io_log.txt 文件,帶有時間戳;

  4. 保持原有輸入輸出鏈路不中斷:數據一邊轉發,一邊被記錄。

中間日志代理工具用法示例:

python mcp_logger.py uv --directory /mnt/c/workspace/llm/mcp/weather run weather.py

這樣一來,無需改動原有 MCP 工具 weather.py 服務代碼,就能實時查看ClineMCP的通信過程,方便調試和分析。這個工具 mcp_logger.py 的代碼如下:

import sys
import asyncio
import datetime
import inspectLOG_FILE = "/mnt/c/workspace/llm/mcp/weather/mcp_io_log.txt"def log_to_file(prefix: str, message: str):timestamp = datetime.datetime.now().isoformat()with open(LOG_FILE, "a", encoding="utf-8") as f:f.write(f"[{timestamp}] {prefix} >>> {message}\n")async def forward_stream(src_reader, dst_writer, tag, decode_bytes=False):while True:line = await src_reader.readline()if not line:breakif isinstance(line, bytes) and decode_bytes:decoded = line.decode("utf-8", errors="replace").rstrip()log_to_file(tag, decoded)await maybe_await(dst_writer, decoded + "\n")else:log_to_file(tag, line.decode("utf-8", errors="replace").rstrip()if isinstance(line, bytes) else line.rstrip())await maybe_await(dst_writer, line)async def maybe_await(fn, arg):"""Call fn(arg), await if it's async."""result = fn(arg)if inspect.isawaitable(result):await resultasync def main():if len(sys.argv) < 2:print("Usage: python mcp_logger.py <real-server-command...>", file=sys.stderr)sys.exit(1)child_cmd = sys.argv[1:]proc = await asyncio.create_subprocess_exec(*child_cmd,stdin=asyncio.subprocess.PIPE,stdout=asyncio.subprocess.PIPE)loop = asyncio.get_running_loop()stdin_reader = asyncio.StreamReader()await loop.connect_read_pipe(lambda: asyncio.StreamReaderProtocol(stdin_reader), sys.stdin)async def write_to_proc_stdin(data):if isinstance(data, str):data = data.encode("utf-8")proc.stdin.write(data)await proc.stdin.drain()def write_to_stdout(data):sys.stdout.write(data)sys.stdout.flush()await asyncio.gather(forward_stream(stdin_reader, write_to_proc_stdin,"輸入", decode_bytes=False),forward_stream(proc.stdout, write_to_stdout,"輸出", decode_bytes=True))if __name__ == "__main__":asyncio.run(main())

現在我們在 Cline 中不再直接配置 weather.py 這個工具了,而是用mcp_logger.py 將它包裝成一個新的 weather 工具。如下,我們將 mcp_logger.py 配置到 cline_mcp_settings.json 文件中,它將 weather.py 作為參數。

    "weather": {"disabled": false,"timeout": 60,"command": "python","args": ["/mnt/c/workspace/llm/mcp/weather/mcp_logger.py","uv","--directory","/mnt/c/workspace/llm/mcp/weather","run","weather.py"],"transportType": "stdio"}

最終的啟動命令為:
python mcp_logger.py uv --directory /mnt/c/workspace/llm/mcp/weather run weather.py

配置完畢 Cline 后,我們再次向 Cline 提問“紐約明天的天氣怎么樣”,mcp_logger.py 會將和工具交互的內容記錄到/mnt/c/workspace/llm/mcp/weather/mcp_io_log.txt文件。這里面的流水賬就是和工具交互的幾大步驟,更多細節可以參看 mcp server 測試客戶端 章節。

[2025-05-24T18:53:27.437433] 輸入 >>> {"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"Cline","version":"3.17.4"}},"jsonrpc":"2.0","id":0}
[2025-05-24T18:53:28.024624] 輸出 >>> {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":true}},"serverInfo":{"name":"weather","version":"1.9.1"}}}
[2025-05-24T18:53:28.026294] 輸入 >>> {"method":"notifications/initialized","jsonrpc":"2.0"}
[2025-05-24T18:53:28.027355] 輸入 >>> {"method":"tools/list","jsonrpc":"2.0","id":1}
[2025-05-24T18:53:28.031537] 輸出 >>> {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"get_alerts","description":"Get weather alerts for a US state.\n\n    Args:\n        state: Two-letter US state code (e.g. CA, NY)\n    ","inputSchema":{"properties":{"state":{"title":"State","type":"string"}},"required":["state"],"type":"object"}},{"name":"get_forecast","description":"Get weather forecast for a location.\n\n    Args:\n        latitude: Latitude of the location\n        longitude: Longitude of the location\n    ","inputSchema":{"properties":{"latitude":{"title":"Latitude","type":"number"},"longitude":{"title":"Longitude","type":"number"}},"required":["latitude","longitude"],"type":"object"}}]}}
[2025-05-24T18:53:28.034546] 輸入 >>> {"method":"resources/list","jsonrpc":"2.0","id":2}
[2025-05-24T18:53:28.037251] 輸出 >>> {"jsonrpc":"2.0","id":2,"result":{"resources":[]}}
[2025-05-24T18:53:28.038989] 輸入 >>> {"method":"resources/templates/list","jsonrpc":"2.0","id":3}
[2025-05-24T18:53:28.041609] 輸出 >>> {"jsonrpc":"2.0","id":3,"result":{"resourceTemplates":[]}}
[2025-05-24T18:55:20.618362] 輸入 >>> {"method":"tools/call","params":{"name":"get_forecast","arguments":{"latitude":40.7128,"longitude":-74.006}},"jsonrpc":"2.0","id":4}
[2025-05-24T18:55:22.201629] 輸出 >>> {"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"\nToday:\nTemperature: 63°F\nWind: 12 to 16 mph W\nForecast: Isolated rain showers after noon. Partly sunny. High near 63, with temperatures falling to around 61 in the afternoon. West wind 12 to 16 mph. Chance of precipitation is 20%.\n\n---\n\nTonight:\nTemperature: 53°F\nWind: 10 to 15 mph W\nForecast: Isolated rain showers before 10pm. Partly cloudy, with a low around 53. West wind 10 to 15 mph. Chance of precipitation is 20%.\n\n---\n\nSunday:\nTemperature: 67°F\nWind: 10 to 14 mph W\nForecast: Mostly sunny, with a high near 67. West wind 10 to 14 mph.\n\n---\n\nSunday Night:\nTemperature: 55°F\nWind: 7 to 12 mph NW\nForecast: Partly cloudy, with a low around 55. Northwest wind 7 to 12 mph.\n\n---\n\nMemorial Day:\nTemperature: 69°F\nWind: 7 mph N\nForecast: Mostly sunny, with a high near 69. North wind around 7 mph.\n"}],"isError":false}}

mcp server 測試客戶端

使用 GPT 寫了一個用于測試 mcp server 的客戶端,它用于和 MCP weather 工具進行交互,將上述的工具握手和獲取天氣的流程自動執行一遍,將它命名為 test_weather.py,代碼如下:

import subprocess
import json
import timedef send_json_and_read(proc, obj):"""發送一行 JSON 并讀取一行響應"""line = json.dumps(obj)print("👉 發送:", line)proc.stdin.write(line + "\n")proc.stdin.flush()while True:response = proc.stdout.readline()if response.strip():print("? 響應:", response.strip())return json.loads(response.strip())# 啟動 MCP server 子進程
proc = subprocess.Popen(["python", "weather.py"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True,
)# 1. initialize
send_json_and_read(proc, {"method": "initialize","params": {"protocolVersion": "2024-11-05","capabilities": {},"clientInfo": {"name": "cline","version": "1.0"}},"jsonrpc": "2.0","id": 0
})# 2. notifications/initialized (通知類,不需要響應)
print("👉 發送通知: notifications/initialized")
proc.stdin.write(json.dumps({"method": "notifications/initialized","jsonrpc": "2.0"
}) + "\n")
proc.stdin.flush()# 3. tools/list
send_json_and_read(proc, {"method": "tools/list","jsonrpc": "2.0","id": 1
})# 4. resources/list
send_json_and_read(proc, {"method": "resources/list","jsonrpc": "2.0","id": 2
})# 5. resources/templates/list
send_json_and_read(proc, {"method": "resources/templates/list","jsonrpc": "2.0","id": 3
})# 6. 調用 get_forecast(注意你原來寫的是 tools/call,其實 FastMCP 使用的是 tools/invoke)
# {"method":"tools/call","params":{"name":"get_forecast","arguments":{"latitude":40.7128,"longitude":-74.006}},"jsonrpc":"2.0","id":4}
send_json_and_read(proc, {"method": "tools/call","params": {"name": "get_forecast","arguments": {"latitude": 40.7128,"longitude": -74.0060}},"jsonrpc": "2.0","id": 4
})proc.terminate()

它的輸出內容如下:
在這里插入圖片描述
這張圖展示了一個完整的 MCP 工具交互過程:客戶端通過 JSON-RPC 協議依次向 weather.py 發送 initialize 握手請求,獲取工具列表(tools/list)、資源信息(resources/listresources/templates/list),然后調用 get_forecast 工具查詢紐約市天氣,并成功接收到格式化的預報結果,表明 MCP 天氣服務端已完整實現并正確響應所有標準請求。

  • 客戶端發送 initialize 請求,協商協議版本并獲取服務端信息,服務端返回名稱為 "weather"、版本為 "1.9.1" 的響應。
  • 客戶端發送 notifications/initialized 通知,表示初始化完成,準備開始交互。
  • 客戶端通過 tools/list 請求獲取工具列表,服務端返回包含 get_alertsget_forecast 兩個工具,并提供每個工具的說明和參數結構。
  • 客戶端請求 resources/listresources/templates/list,用于發現可用資源和模板,服務端分別返回空數組,表示當前未注冊資源或模板。
  • 客戶端使用 tools/call 方法調用 get_forecast 工具,并傳入紐約市的經緯度參數(latitude: 40.7128,longitude: -74.006)。
  • 服務端成功響應工具調用,返回包含天氣預報文本的結構化結果,字段包括 content(文本數組)和 isError: false,表明請求處理成功。

步驟六:模型調用工具示例流程

前面的內容給出了 Cline 如何和 工具進行溝通的。那下一個問題就是 Cline 是如何和模型交互的呢?不同的MCP Agent 使用不同的格式和模型進行溝通,Cline 是使用xml格式來交互的。

和前面的 中間日志代理工具 類似,我們可以在 Cline 和 模型 中間加一個 本地代理服務器,這個本地服務器就用來打日志,將 Cline 和 DeepSeek 交互的輸入和輸出打印出來。

中轉服務器 llm_logger.py 代碼

from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
import httpx
import json
import uuid
import datetime
import osapp = FastAPI()
LOG_FILE = "/mnt/c/workspace/llm/mcp/weather/llm_io_log.txt"# 啟動時清空日志文件
if os.path.exists(LOG_FILE):with open(LOG_FILE, "w", encoding="utf-8") as f:f.write("")print(f"清空日志文件:{LOG_FILE}")def log_to_file(log_id, direction, content):"""將一條日志寫入文件"""timestamp = datetime.datetime.now().isoformat()with open(f"{LOG_FILE}", "a", encoding="utf-8") as f:f.write(direction + "\n" + content)f.write("\n")print(direction + "\n" + content)@app.post("/chat/completions")
async def proxy_request(request: Request):log_id = uuid.uuid4().hex  # 每個會話唯一標識body_bytes = await request.body()body_str = body_bytes.decode('utf-8')log_to_file(log_id, "模型輸入:", body_str)  # 記錄請求體body = await request.json()async def event_stream():collected_text = ""  # 收集最終自然語言響應async with httpx.AsyncClient(timeout=None) as client:async with client.stream("POST","https://api.deepseek.com/chat/completions",json=body,headers={"Content-Type": "application/json","Accept": "text/event-stream","Authorization": request.headers.get("Authorization"),},) as response:async for line in response.aiter_lines():if line.startswith("data:"):try:data_json = json.loads(line[len("data:"):].strip())delta = data_json.get("choices", [{}])[0].get("delta", {})content_piece = delta.get("content")if content_piece:collected_text += content_pieceexcept json.JSONDecodeError:passyield f"{line}\n"# 所有內容收集完畢,寫入日志log_to_file(log_id, "模型輸出:", collected_text)return StreamingResponse(event_stream(), media_type="text/event-stream")if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8001)

啟動中轉服務器
使用下面的命令來啟動中轉服務器:uvicorn llm_logger:app --port 8001

配置中轉服務器
配置本地的中轉服務器的地址,以及 DeepSeek的 API KEY 和模型ID
在這里插入圖片描述

llm_logger.py 實現了一個OpenAI API 兼容的代理服務器,用于中轉與 DeepSeek 等模型服務之間的對話請求,它接收 /chat/completions 請求 并使用 httpx.AsyncClient 將請求轉發給到 DeepSeek 接口https://api.deepseek.com/chat/completions,同時保留原始 Authorization 頭,用以傳遞 API KEY。它可作為調試 MCP 流程中「模型調用工具前后的原始問答」記錄工具; 與 Cline 搭配使用,實現“模型調用 + 工具調用 + 日志追蹤”完整鏈路。

可直接運行該腳本,通過 uvicorn 啟動 FastAPI 服務監聽 8001 端口:

python your_file.py
# 或手動 uvicorn 啟動
uvicorn your_file:app --host 0.0.0.0 --port 8001

詢問天氣情況:
Cline 發起天氣詢問請求,同時攜帶的還有 “role”: “system” 的內容,它的里面有 weather 工具的信息,講述它的功能以及參數類型等等。

## weather (`python /mnt/c/workspace/llm/mcp/weather/mcp_logger.py uv --directory /mnt/c/workspace/llm/mcp/weather run weather.py`)\n\n### Available Tools\n- get_alerts: Get weather alerts for a US state.\n\n    Args:\n        state: Two-letter US state code (e.g. CA, NY)\n    \n    Input Schema:\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"state\": {\n          \"title\": \"State\",\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"state\"\n      ]\n    }\n\n- get_forecast: Get weather forecast for a location.\n\n    Args:\n        latitude: Latitude of the location\n        longitude: Longitude of the location\n    \n    Input Schema:\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"latitude\": {\n          \"title\": \"Latitude\",\n          \"type\": \"number\"\n        },\n        \"longitude\": {\n          \"title\": \"Longitude\",\n          \"type\": \"number\"\n        }\n      },\n      \"required\": [\n        \"latitude\",\n        \"longitude\"\n      ]\n    }\n\n====\n\n

在這里插入圖片描述
DeepSeek 模型收到這個問題之后,從工具列表中找到了 weather,建議 Cline use_mcp_tool發起 weather 服務中 get_forecast 的調用,同時還給出了工具調用參數紐約的經緯度坐標

模型輸出:
<thinking>
1. 用戶詢問的是紐約明天的天氣
2. 系統信息顯示有一個已連接的MCP天氣服務器(weather)
3. 該服務器提供了獲取天氣預報的工具(get_forecast)
4. 需要先獲取紐約的經緯度坐標才能查詢天氣預報
5. 可以使用MCP天氣服務器的get_forecast工具來完成任務
</thinking><use_mcp_tool>
<server_name>weather</server_name>
<tool_name>get_forecast</tool_name>
<arguments>
{"latitude": 40.7128,"longitude": -74.0060
}
</arguments>
</use_mcp_tool>

Cline 收到回復之后,Cline發起 tool/call 工具調用并得到 紐約 的天氣信息

[2025-05-26T00:08:00.053419] 輸入 >>> {"method":"tools/call","params":{"name":"get_forecast","arguments":{"latitude":40.7128,"longitude":-74.006}},"jsonrpc":"2.0","id":7}
[2025-05-26T00:08:01.741230] 輸出 >>> {"jsonrpc":"2.0","id":7,"result":{"content":[{"type":"text","text":"\nThis Afternoon:\nTemperature: 68°F\nWind: 14 mph NW\nForecast: Isolated rain showers after 5pm. Partly sunny, with a high near 68. Northwest wind around 14 mph. Chance of precipitation is 20%.\n\n---\n\nTonight:\nTemperature: 55°F\nWind: 7 to 12 mph NW\nForecast: Isolated rain showers before 10pm. Partly cloudy, with a low around 55. Northwest wind 7 to 12 mph. Chance of precipitation is 20%.\n\n---\n\nMemorial Day:\nTemperature: 71°F\nWind: 7 mph N\nForecast: Mostly sunny, with a high near 71. North wind around 7 mph.\n\n---\n\nMonday Night:\nTemperature: 57°F\nWind: 6 mph SW\nForecast: Partly cloudy, with a low around 57. Southwest wind around 6 mph.\n\n---\n\nTuesday:\nTemperature: 70°F\nWind: 3 to 10 mph E\nForecast: Partly sunny. High near 70, with temperatures falling to around 68 in the afternoon. East wind 3 to 10 mph.\n"}],"isError":false}}

Cline 將從工具收到的天氣信息發送給 DeepSeek 模型
在這里插入圖片描述
DeepSeek模型將收到的信息轉換成易讀的形式,返回給Cline,這里attempt_completion表明是最終結果,不需要繼續交互了。
在這里插入圖片描述

總結:MCP 的本質與價值

MCP 作為工具調用協議,其核心目標是:

  • 發現函數(工具):tools/list 中規定了所有函數名、輸入結構、用途說明

  • 標準化調用方式:統一了調用請求/響應格式

  • 模型工具解耦:并不關心模型如何組織 prompt,由 Agent(如 cline)決定

通過 VScode + Cline + DeepSeek + MCP,可以快速構建出一個結構清晰、交互統一、支持自然語言指令的工具調用系統。無論是天氣查詢、網頁抓取,還是其他插件式擴展,MCP 都為構建 AI 應用打開了一種新方式。如果你對 Agent 模型感興趣,可以參考我之前的文章:大模型系列18-AI Agents。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82552.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82552.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82552.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Go語言Map的底層原理

概念 map 又稱字典&#xff0c;是一種常用的數據結構&#xff0c;核心特征包含下述三點&#xff1a; &#xff08;1&#xff09;存儲基于 key-value 對映射的模式&#xff1b; &#xff08;2&#xff09;基于 key 維度實現存儲數據的去重&#xff1b; &#xff08;3&#x…

循環神經網絡(RNN):原理、架構與實戰

循環神經網絡&#xff08;Recurrent Neural Network, RNN&#xff09;是一類專門處理序列數據的神經網絡&#xff0c;如時間序列、自然語言、音頻等。與前饋神經網絡不同&#xff0c;RNN 引入了循環結構&#xff0c;能夠捕捉序列中的時序信息&#xff0c;使模型在不同時間步之間…

java 項目登錄請求業務解耦模塊全面

登錄是統一的閘機&#xff1b; 密碼存在數據庫中&#xff0c;用的是密文&#xff0c;后端加密&#xff0c;和數據庫中做對比 1、UserController public class UserController{Autowiredprivate IuserService userservicepublic JsonResult login(Validated RequestBody UserLo…

【手寫數據庫核心揭秘系列】第9節 可重入的SQL解析器,不斷解析Structure Query Language,語言翻譯好幫手

可重入的SQL解析器 文章目錄 可重入的SQL解析器一、概述 二、可重入解析器 2.1 可重入設置 2.2 記錄狀態的數據結構 2.3 節點數據類型定義 2.4 頭文件引用 三、調整后的程序結構 四、總結 一、概述 現在就來修改之前sqlscanner.l和sqlgram.y程序,可以不斷輸入SQL語句,循環執…

微軟開源bitnet b1.58大模型,應用效果測評(問答、知識、數學、邏輯、分析)

微軟開源bitnet b1.58大模型,應用效果測評(問答、知識、數學、邏輯、分析) 目 錄 1. 前言... 2 2. 應用部署... 2 3. 應用效果... 3 1.1 問答方面... 3 1.2 知識方面... 4 1.3 數字運算... 6 1.4 邏輯方面... …

用HTML5+JavaScript實現漢字轉拼音工具

用HTML5JavaScript實現漢字轉拼音工具 前一篇博文&#xff08;https://blog.csdn.net/cnds123/article/details/148067680&#xff09;提到&#xff0c;當需要將拼音添加到漢字上面時&#xff0c;用python實現比HTML5JavaScript實現繁瑣。在這篇博文中用HTML5JavaScript實現漢…

鴻蒙OSUniApp 開發的動態背景動畫組件#三方框架 #Uniapp

使用 UniApp 開發的動態背景動畫組件 前言 在移動應用開發中&#xff0c;動態背景動畫不僅能提升界面美感&#xff0c;還能增強用戶的沉浸感和品牌辨識度。無論是登錄頁、首頁還是活動頁&#xff0c;恰到好處的動態背景都能讓產品脫穎而出。隨著鴻蒙&#xff08;HarmonyOS&am…

云原生技術架構技術探索

文章目錄 前言一、什么是云原生技術架構二、云原生技術架構的優勢三、云原生技術架構的應用場景結語 前言 在當今的技術領域&#xff0c;云原生技術架構正以一種勢不可擋的姿態席卷而來&#xff0c;成為了眾多開發者、企業和技術愛好者關注的焦點。那么&#xff0c;究竟什么是…

AWS之AI服務

目錄 一、AWS AI布局 ??1. 底層基礎設施與芯片?? ??2. AI訓練框架與平臺?? ??3. 大模型與應用層?? ??4. 超級計算與網絡?? ??與競品對比?? AI服務 ??1. 機器學習平臺?? ??2. 預訓練AI服務?? ??3. 邊緣與物聯網AI?? ??4. 數據與AI…

lwip_bind、lwip_listen 是阻塞函數嗎

在 lwIP 協議棧中&#xff0c;lwip_bind 和 lwip_listen 函數本質上是非阻塞的。 通常&#xff0c;bind和listen在大多數實現中都是非阻塞的&#xff0c;因為它們只是設置套接字的屬性&#xff0c;不需要等待外部事件。阻塞通常發生在接受連接&#xff08;accept&#xff09;、…

【后端高階面經:消息隊列篇】28、從零設計高可用消息隊列

一、消息隊列架構設計的核心目標與挑戰 設計高性能、高可靠的消息隊列需平衡功能性與非功能性需求,解決分布式系統中的典型問題。 1.1 核心設計目標 吞吐量:支持百萬級消息/秒處理,通過分區并行化實現橫向擴展。延遲:端到端延遲控制在毫秒級,適用于實時業務場景。可靠性…

【運維實戰】Linux 內存調優之進程內存深度監控

寫在前面 內容涉及 Linux 進程內存監控 監控方式包括傳統工具 ps/top/pmap ,以及 cgroup 內存子系統&#xff0c;proc 內存偽文件系統 監控內容包括進程內存使用情況&#xff0c; 內存全局數據統計&#xff0c;內存事件指標&#xff0c;以及進程內存段數據監控 監控進程的內…

決策樹 GBDT XGBoost LightGBM

一、決策樹 1. 決策樹有一個很強的假設&#xff1a; 信息是可分的&#xff0c;否則無法進行特征分支 2. 決策樹的種類&#xff1a; 2. ID3決策樹&#xff1a; ID3決策樹的數劃分標準是信息增益&#xff1a; 信息增益衡量的是通過某個特征進行數據劃分前后熵的變化量。但是&…

java基礎學習(十四)

文章目錄 4-1 面向過程與面向對象4-2 Java語言的基本元素&#xff1a;類和對象面向對象的思想概述 4-3 對象的創建和使用內存解析匿名對象 4-1 面向過程與面向對象 面向過程(POP) 與 面向對象(OOP) 二者都是一種思想&#xff0c;面向對象是相對于面向過程而言的。面向過程&…

TCP 三次握手,第三次握手報文丟失會發生什么?

文章目錄 RTO(Retransmission Timeout)注意 客戶端收到服務端的 SYNACK 報文后&#xff0c;會回給服務端一個 ACK 報文&#xff0c;之后處于 ESTABLISHED 狀態 因為第三次握手的 ACK 是對第二次握手中 SYN 的確認報文&#xff0c;如果第三次握手報文丟失了&#xff0c;服務端就…

deepseek告訴您http與https有何區別?

有用戶經常問什么是Http , 什么是Https &#xff1f; 兩者有什么區別&#xff0c;下面為大家介紹一下兩者的區別 一、什么是HTTP HTTP是一種無狀態的應用層協議&#xff0c;用于在客戶端瀏覽器和服務器之間傳輸網頁信息&#xff0c;默認使用80端口 二、HTTP協議的特點 HTTP協議…

openresty如何禁止海外ip訪問

前幾天&#xff0c;我有一個徒弟問我&#xff0c;如何禁止海外ip訪問他的網站系統&#xff1f;操作系統采用的是centos7.9&#xff0c;發布服務采用的是openresty。通過日志他發現&#xff0c;有很多類似以下數據 {"host":"172.30.7.95","clientip&q…

理解 Redis 事務-20 (MULTI、EXEC、DISCARD)

理解 Redis 事務&#xff1a;MULTI、EXEC、DISCARD Redis 事務允許你將一組命令作為一個單一的原子操作來執行。這意味著事務中的所有命令要么全部執行&#xff0c;要么全部不執行。這對于在需要一起執行多個操作時保持數據完整性至關重要。本課程將涵蓋 Redis 事務的基礎知識…

Milvus分區-分片-段結構詳解與最佳實踐

導讀&#xff1a;在構建大規模向量數據庫應用時&#xff0c;數據組織架構的設計往往決定了系統的性能上限。Milvus作為主流向量數據庫&#xff0c;其獨特的三層架構設計——分區、分片、段&#xff0c;為海量向量數據的高效存儲和檢索提供了堅實基礎。 本文通過圖書館管理系統的…

Kettle 遠程mysql 表導入到 hadoop hive

kettle 遠程mysql 表導入到 hadoop hive &#xff08;教學用 &#xff09; 文章目錄 kettle 遠程mysql 表導入到 hadoop hive創建 對象 執行 SQL 語句 -mysql 導出 CSV格式CSV 文件遠程上傳到 HDFS運行 SSH 命令遠程登錄 run SSH 并執行 hadoop fs -put 建表和加載數據總結 創…