大模型系列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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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 建表和加载数据总结 创…