ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">如果你活跃于技术影响者聚集的社交媒体平台,那么现在想必已经听说过模型上下文协议(MCP)了。
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">诚然,它可能曾受到过度炒作,但了解它仍是十分必要的,因为MCP注定会成为未来AI流水线中的重要组成部分。
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">MCP本身,抑或是基于其演进的各项改进,都将成为未来AI流水线中不可或缺的一环。
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">在本文中,我们将探讨MCP的工作原理,并通过构建一个使用它的智能代理系统来加深理解。
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);"> ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: inherit;color: rgb(1, 155, 252);">让我们开始吧! ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;padding-left: 12px;color: rgb(63, 63, 63);border-radius: 6px;background: color-mix(in srgb, rgb(1, 155, 252) 8%, transparent);">为什么最初要构建MCP这样的东西?
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">假设现在是2024年初,你正在构建一个用于股票市场分析的LLM应用。你可能需要:
• 如果数据存储在不同数据库中,需要为每个数据库编写特定代码。 • 为每个金融数据源(如Yahoo Finance、Bloomberg、SEC文件等)定义自定义API接口。 等等!
这无疑会带来诸多不便!
任何一个部分的微小改动都会迫使你重写代码,导致系统难以扩展。
正因如此,Anthropic开源了MCP,使得自2024年11月之后,基于LLM的应用开发变得更加便捷。
让我们来了解一下是如何实现的。
什么是MCP? 根据官方文档所述:
模型上下文协议(MCP)是一个开放协议,它规范了应用程序如何向LLM提供上下文。
以往,我们通过分散且不稳定的集成方式连接LLM与数据源,而MCP则以统一协议取代了这种局面,从而使连接更可靠,也大大简化了我们的工作。
以下是Anthropic对它的评价:
开发人员现在可以基于一个标准协议进行构建,而不必为每个数据源维护独立的连接器。 可以将MCP想象成AI应用的USB-C接口。 正如USB-C提供了一种标准化方式,将设备连接到各种外部设备和附件一样,MCP也提供了一种标准化的方式,将AI模型连接到不同的数据源和工具。 —— Anthropic 关于 MCP 的说明 (https://www.anthropic.com/news/model-context-protocol)
一言以蔽之:
模型上下文协议是一个“协议”,它允许“模型”获取相关的“上下文”。
使用MCP前后LLM与数据源连接的简化示意图
现在我们理解了MCP解决的问题,接下来学习它的组成部分。
MCP的运作机制 MCP主要由三个核心组件构成:
我们来逐一讲解。
MCP主机 这些是用户直接交互的应用程序。
例如,Claude桌面版、集成开发环境(如Cursor和VS Code),或其他由AI驱动的工具。
这是LLM运行的核心所在。
MCP服务器 这些是小巧、专用的程序,根据其用例公开特定的功能。
这些服务器可以通过可由MCP主机内的LLM调用的 工具(Tools)(https://modelcontextprotocol.io/specification/2025-06-18/server/tools) 安全地连接到 资源(Resources)(https://modelcontextprotocol.io/specification/2025-06-18/server/resources)。
这些资源可以是:
• 基于网络的各种服务,例如数据库、电子邮件、天气、日历等(通过API) 服务器还公开 提示(Prompt)(https://modelcontextprotocol.io/specification/2025-06-18/server/prompts) 模板,客户端可以使用这些模板从MCP服务器获取结构化响应。
MCP客户端 这是主机的一个组件,充当通信层。
每个客户端与一个MCP服务器保持专用连接,负责处理协议的请求-响应模式、错误处理以及 连接生命周期(https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle) 管理。
你可以把它们看作是能够理解主机语言和MCP协议的“翻译官”。
客户端如何与服务器通信? 传输机制(Transport)(https://modelcontextprotocol.io/docs/concepts/transports) 是一种通信通道,负责处理客户端与服务器间信息(消息)交换的底层原理。
MCP中交换的消息共有三种类型,它们均采用 JSON-RPC 2.0(https://www.jsonrpc.org/specification) 格式。
这些消息类型是:
1.请求(Requests): 请求某项内容的消息,期望得到响应。 在下面的示例中,客户端请求服务器为查询“旧金山”调用“天气”工具,并期望得到响应。
{ "jsonrpc":"2.0", "id":1, "method":"tools/call", "params":{ "name":"weather", "arguments":{ "location":"San Francisco" } } }2. 响应(Responses): 回复先前请求的消息。
在下面的示例中,服务器回复了ID为1的请求,提供了查询“旧金山”的具体天气数据:温度(temperature)和状况(condition)。
{ "jsonrpc":"2.0", "id":1, "result":{ "temperature":"72°F", "condition":"sunny" } }3. 通知(Notifications): 告知某事的消息,不期望得到响应。
在下面的示例中,服务器发送了一个进度更新消息,但不期望任何回复(请注意没有id字段)。
{ "jsonrpc":"2.0", "method":"$/progress", "params":{ "message":"Processing data...", "progress":0.8 } }MCP使用以下两种常见的通信机制来交换这些消息:
1.标准输入/输出(stdio) 这种机制允许通过计算机上的标准输入和输出流进行通信。它是一种更简单的机制,最适用于: 2.流式HTTP(Streamable HTTP) 这种机制使用: •HTTP POST请求用于客户端到服务器的通信,以及 • 可选的服务器发送事件(SSE)流用于服务器到客户端的通信。 它最适用于构建基于Web的集成 ,可以支持多个并发客户端和可恢复的连接。 至此,关于MCP的理论知识讲解已告一段落。
接下来,我们将进入代码实践环节!
构建一个基于MCP的物理问答智能代理系统 自MCP发布以来,MCP服务器的数量呈爆炸式增长。
可以在这里找到这些服务器的完整列表:https://github.com/modelcontextprotocol/servers
按照每个服务器的说明,将这些服务器与主机(如Claude桌面版或IDE)集成非常容易。
但既然我们正在学习从基础构建事物,那就不妨自己实现一个MCP服务器吧。
我们将构建一个服务器,其中包含多个用于计算物理问题解决方案的工具。
以下是实现步骤:
1. 使用“uv”创建新项目 本教程中,我们将使用“ uv(https://docs.astral.sh/uv/) ”作为包管理器。
如果你是新手,请按照 此页面上的说明(https://docs.astral.sh/uv/getting-started/installation/) 进行安装。
uv init physics-solver-mcp cdphysics-solver-mcp uv venv .venv source.venv/bin/activate2. 安装依赖 在完成项目初始化后,我们需要安装几个关键的依赖包,包括mcp和crewai-tools。我们通过以下命令安装mcpPython包:
uvaddmcpcrewai"crewai-tools[mcp]" 3. 设置环境变量 我们在项目文件夹中创建一个名为.env的文件,其中包含我们的OpenAI API密钥。
在调用基于gpt-4o-mini模型的代理时将使用此密钥。
OPENAI_API_KEY="YOUR_KEY_GOES_HERE" 4. 创建MCP服务器 我们将使用 FastMCP (https://github.com/jlowin/fastmcp),它是用于在Python中处理模型上下文协议的标准框架。
我们创建一个名为physics_server.py的文件,并编写一些与服务器相关的工具,每个工具都使用@mcp.tool()装饰器进行指定。
"""一个实现模型上下文协议的物理MCP服务器。 此服务器提供物理公式作为工具,可供MCP客户端发现和使用。 """ frommcp.server.fastmcpimportFastMCP mcp = FastMCP("Physics-Server") # 计算动能的工具 @mcp.tool() defkinetic_energy(mass:float, velocity:float) ->dict: """计算运动物体的动能。 公式: KE = 1/2 × 质量 × 速度² 参数: mass: 物体质量,单位千克 (kg)。必须为正值。 velocity: 物体速度,单位米/秒 (m/s)。可为正或负。 返回: 动能,单位焦耳 (J) """ ifmass <=0: raiseValueError("质量必须为正值") ke =0.5* mass * (velocity **2) returnke # 计算重力势能的工具 @mcp.tool() defgravitational_potential_energy(mass:float, height:float, g:float=9.81) ->dict: """计算物体在特定高度处的重力势能。 公式: PE = 质量 × 重力加速度 × 高度 参数: mass: 物体质量,单位千克 (kg)。必须为正值。 height: 物体相对于参考点的高度,单位米 (m)。必须为非负值。 g: 重力加速度,单位米/秒²。默认为 9.81 (地球表面)。 返回: 重力势能,单位焦耳 (J) """ ifmass <=0: raiseValueError("质量必须为正值") ifheight <0: raiseValueError("高度必须为非负值") ifg <=0: raiseValueError("重力加速度必须为正值") pe = mass * g * height returnpe # 减去两个数字的工具 @mcp.tool() defsubtract(a:float, b:float) ->dict: """减去两个数字。 参数: a: 第一个数字。 b: 第二个数字。 返回: a 和 b 之间的差值。 """ returna - b if__name__ =="__main__": mcp.run(transport="stdio")# 使用STDIO传输5. 创建MCP客户端 接下来,我们用 CrewAI(https://www.crewai.com/) 框架搭建一个智能代理系统,该系统将使用我们的MCP服务器工具来解决给定的物理问题。
如果你是CrewAI的新手,可以考虑查阅这篇 关于构建你的第一个AI代理的教程(https://intoai.pub/p/building-your-first-ai-Agent),了解如何使用这个框架。
crewai-tools中的MCPServerAdapter(https://docs.crewai.com/en/mcp/overview#key-concepts-%26-getting-started) 类是连接MCP服务器并使其工具可供CrewAI代理使用的主要方式。
使用Python上下文管理器(with语句)是此类的推荐方法,因为它会自动处理与MCP服务器连接的启动和停止。
fromcrewaiimportAgent, Task, Crew fromcrewai_toolsimportMCPServerAdapter frommcpimportStdioServerParameters importos # 为物理服务器创建一个StdioServerParameters对象 server_params=StdioServerParameters( command="python3", args=["physics_server.py"], env={"UV_PYTHON":"3.11", **os.environ}, ) # 使用StdioServerParameters对象创建MCPServerAdapter withMCPServerAdapter(server_params)astools: print(f"可用的物理工具:{[tool.namefortoolintools]}") agent = Agent( role="物理专家", goal="使用基本能量计算解决物理问题。", backstory="一位经验丰富的物理学家,对经典力学和能量原理有深入了解。能够应用动能和势能概念解决实际问题。", tools=tools, verbose=True, ) task = Task( description="解决这个物理问题: {physics_problem}", expected_output="详细的分步解决方案,显示所有计算、中间值和带有正确单位的最终答案。使用可用的物理工具进行动能和势能计算。", agent=agent, ) crew = Crew( agents=[agent], tasks=[task], verbose=True, ) # 物理问题 crew_inputs = { "physics_problem":""" 一辆质量为800公斤的过山车从50米高的山顶静止启动。 随后,过山车沿山坡下行,到达底部时速度达到25米/秒。 已知: - 汽车质量: 800公斤 - 初始高度: 50米 - 最终速度: 25米/秒 - 重力加速度: 9.81米/秒² 问题: 1. 计算过山车在山顶的初始重力势能。 2. 计算过山车在山底的最终动能。 3. 比较初始势能和最终动能,并解释任何差异。 4. 如果过山车要爬上另一座山,如果其所有动能都转化回势能,它能达到的最大高度是多少? """ } result = crew.kickoff(inputs=crew_inputs) print(result)我们通过以下命令运行MCP客户端。
uvrunclient.py 这是我们得到的结果,运行效果符合预期!
1. Initial Gravitational Potential Energy: 392400 J 2. Final Kinetic Energy: 250000 J 3. The initial potential energy is greater than the final kinetic energy due to energy losses from friction and air resistance. 4. Maximum Height: 31.87 m从日志中可以看到,我们的代理顺利调用了这些MCP服务器工具完成了任务。
关于MCP的讲解到这里就结束了。
你是否在自己的应用程序中使用过它?请在下方评论区分享你的经验