MCP协议的服务端和客户端Python 示例
下面我将为你提供一个简单的 MCP(Model Context Protocol)协议的服务端和客户端的 Python 示例。这个示例基于 Anthropic 官方的 Python SDK,并展示如何创建一个基本的 MCP 服务端(提供工具和资源)以及一个客户端与之交互。代码将尽量简洁,同时包含注释以便理解。
前提条件
安装依赖:需要安装 mcp Python 包。你可以通过以下命令安装: pip install mcp
Python 版本:确保使用 Python 3.10 或更高版本。
环境:建议在虚拟环境中运行以下代码。
MCP 服务端示例 (server.py
)
这个服务端提供一个工具(计算两个数字之和)和一个资源(返回静态问候语)。
from mcp.server.fastmcp import FastMCP# 创建一个 MCP 服务端实例,命名为 "SimpleServer"
mcp = FastMCP("SimpleServer")# 定义一个工具:计算两个数字之和
@mcp.tool()
def add_numbers(a: int, b: int) -> int:"""Add two numbers together."""return a + b# 定义一个资源:返回一个简单的问候语
@mcp.resource("greeting://hello")
def get_greeting() -> str:"""Return a static greeting message."""return "Hello from MCP Server!"# 运行服务端
if __name__ == "__main__":mcp.run()
说明
FastMCP:这是 MCP SDK 提供的高级接口,简化了服务端创建过程。
@mcp.tool():定义一个可以被客户端调用的工具,类似于函数调用。
@mcp.resource():定义一个只读资源,客户端可以获取其内容。
mcp.run():启动服务端,通过标准输入输出(stdio)与客户端通信。
MCP 客户端示例 (client.py
)
这个客户端连接到上述服务端,列出可用工具并调用它们。
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_clientasync def main():# 定义服务端参数:假设服务端脚本为 server.pyserver_params = StdioServerParameters(command="python",args=["server.py"],env=None)# 使用 stdio_client 连接到服务端async with stdio_client(server_params) as (stdio, write):# 创建客户端会话async with ClientSession(stdio, write) as session:# 初始化会话await session.initialize()# 列出可用工具tools_response = await session.list_tools()print("可用工具:", [tool.name for tool in tools_response.tools])# 调用 add_numbers 工具result = await session.call_tool("add_numbers", {"a": 5, "b": 3})print("工具调用结果 (5 + 3):", result.toolResult)# 获取 greeting 资源(需要 MCP 1.3.0+,若不可用可跳过)# resource = await session.get_resource("greeting://hello")# print("资源内容:", resource.content)# 运行异步客户端
if __name__ == "__main__":asyncio.run(main())
说明
StdioServerParameters:指定如何启动服务端进程(这里通过 python server.py)。
stdio_client:通过标准输入输出连接到服务端。
ClientSession:管理与服务端的会话,初始化后可以调用工具或获取资源。
工具调用:使用 call_tool 方法调用服务端的 add_numbers 工具。
资源获取:get_resource 方法用于获取资源(注释掉的部分需确保 SDK 版本支持)。
如何运行
保存文件:
将服务端代码保存为 server.py。
将客户端代码保存为 client.py。
运行服务端(可选,手动测试用): python server.py
服务端会启动并等待客户端连接。
运行客户端: python client.py
客户端会自动启动服务端并与之交互。预期输出类似: 可用工具: ['add_numbers']
工具调用结果 (5 + 3): 8
注意事项
异步编程:客户端使用 asyncio,因为 MCP 协议依赖异步通信。
版本兼容性:确保 mcp 包是最新的(例如 pip install --upgrade mcp),以支持所有功能。
扩展性:你可以为服务端添加更多工具(如查询天气、访问数据库)或资源(如文件内容、API 数据)。
调试:若遇到问题,可使用 MCP 官方提供的调试工具,例如: mcp inspect server.py
示例扩展
如果你想让服务端更有实用性,可以添加一个动态工具,例如获取当前时间:
# 在 server.py 中添加
from datetime import datetime@mcp.tool()
def get_current_time() -> str:"""Return the current time."""return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
然后在客户端中调用:
# 在 client.py 的 session 块中添加
time_result = await session.call_tool("get_current_time", {})
print("当前时间:", time_result.toolResult)
这个示例展示了 MCP 协议的基本服务端和客户端实现。MCP 的强大之处在于它可以将任意资源或功能以标准化的方式暴露给 AI 模型(如 Claude),非常适合构建 AI Agent 或自动化工作流。
为什么服务端参数要指定服务端的Python 文件?作用是什么?
在你的问题中提到“为什么服务端参数要指定服务端的 Python 文件?作用是什么?”,我将详细解释 StdioServerParameters 中指定服务端 Python 文件(如 server.py)的原因和作用。
为什么需要指定服务端的 Python 文件?
在 MCP(Model Context Protocol)协议的实现中,服务端和客户端通常是通过标准输入输出(stdio)进行通信的独立进程。为了让客户端能够与服务端交互,客户端需要知道如何启动服务端进程并与之建立连接。StdioServerParameters 的作用正是定义这一启动过程,而指定服务端的 Python 文件(如 server.py)是其中的关键一步。
具体原因
服务端是一个独立进程:
MCP 的设计允许服务端作为一个独立的程序运行,而不是嵌入在客户端代码中。这种分离提高了模块化和灵活性。
客户端通过 stdio(标准输入输出)与服务端通信,因此需要明确告诉系统如何启动服务端进程。指定 server.py 就是告诉客户端:“我要启动的服务端代码在这个文件中”。
动态启动服务端:
在 client.py 中,StdioServerParameters 使用 command="python" 和 args=["server.py"] 来构建一个命令(相当于在终端运行 python server.py)。这意味着客户端会动态启动服务端进程,而不是假设服务端已经在运行。
如果不指定服务端文件,客户端无法知道要启动哪个程序,也就无法建立连接。
协议的标准化通信:
MCP 依赖于服务端和客户端之间的标准化协议(通过 JSON 格式的消息在 stdio 上交换)。服务端文件(如 server.py)包含了实现这一协议的具体逻辑(例如定义工具和资源),客户端需要启动它以加载这些功能。
指定服务端文件的作用
通过 StdioServerParameters 指定服务端 Python 文件的具体作用可以总结为以下几点:
定位服务端实现:
服务端文件(server.py)包含了 MCP 服务端的核心逻辑,例如工具(如 add_numbers)和资源(如 get_greeting)的定义。指定这个文件告诉客户端在哪里找到这些功能的具体实现。
启动服务端进程:
StdioServerParameters 的 command 和 args 参数会被 Python 的 subprocess 模块用来启动一个新的进程。例如: python server.py
客户端通过这种方式启动服务端,并通过 stdio 与之通信。
确保通信管道的建立:
当客户端启动服务端时,stdio_client 会建立一个双向通信管道(stdin 和 stdout)。服务端文件必须是一个有效的 MCP 服务端实现(例如使用 FastMCP),否则通信会失败。
指定正确的文件确保客户端连接的是预期的服务端,而不是随机或错误的程序。
灵活性和可移植性:
通过参数指定服务端文件,客户端可以动态适应不同的服务端实现。例如,你可以轻松切换到另一个服务端文件(如 advanced_server.py),只需修改参数,而无需重写客户端代码。
举个例子说明
假设你没有指定服务端文件,而是直接运行客户端代码:
server_params = StdioServerParameters(command="python", args=[], env=None)
客户端会尝试运行 python(不带任何脚本),这会导致错误,因为 Python 解释器需要一个具体的脚本文件(如 server.py)来执行。
结果是客户端无法启动服务端,也无法获取工具或资源。
现在对比正确指定文件的情况:
server_params = StdioServerParameters(command="python", args=["server.py"], env=None)
客户端运行 python server.py,启动了服务端进程。
服务端加载 server.py 中的逻辑(例如 add_numbers 工具),并通过 stdio 返回结果给客户端。
更深层的设计考虑
为什么不用硬编码的方式?
你可能会问:“为什么不在客户端代码中直接嵌入服务端逻辑?”答案是 MCP 的设计目标是解耦服务端和客户端。指定文件的方式让服务端可以独立开发、测试和部署,而客户端只需知道如何启动它。
替代方式:
如果服务端已经作为一个独立进程运行(例如通过 python server.py 手动启动),客户端也可以通过其他方式连接(如网络 socket)。但 StdioServerParameters 是为了简化本地开发,提供一种“开箱即用”的启动机制。
跨平台支持:
指定 command="python" 和 args=["server.py"] 确保了代码在不同操作系统上都能正确运行(假设 Python 已安装)。这比直接写死路径(如 /usr/bin/python server.py)更具通用性。
总结
指定服务端 Python 文件(如 server.py)的作用是:
告诉客户端在哪里找到服务端逻辑。
动态启动服务端进程并建立通信。
保持模块化和灵活性,让客户端无需关心服务端的具体实现细节。
这是 MCP 协议通过 stdio 通信的核心机制之一。如果你有其他疑问,比如想了解更多关于 MCP 的通信细节或如何调试服务端,可以随时告诉我!
这样服务端程序岂不是必须和客户端程序在一台机器上?那么你前面先启动server.py岂不是没意义?
你的问题非常敏锐!确实,通过 StdioServerParameters 指定服务端 Python 文件(如 server.py)并通过客户端动态启动的方式,意味着服务端和客户端默认运行在同一台机器上。这也引出了你提到的第二个问题:如果服务端必须和客户端在同一台机器上,那么我之前建议“先手动启动 server.py”似乎显得多余。让我详细解答这两个问题,并澄清背后的逻辑和设计选择。
问题 1:服务端程序必须和客户端在一台机器上吗?
默认情况:是的
在 MCP 示例中,使用 StdioServerParameters 和 stdio_client 的方式确实假定服务端和客户端运行在同一台机器上。原因如下:
标准输入输出 (stdio) 的限制:stdio 是通过进程间的标准输入输出管道(stdin/stdout)通信的,这种机制通常只适用于同一台机器上的进程。例如,客户端通过 subprocess 启动 python server.py,并直接与其 stdin/stdout 交互。
设计初衷:MCP 的 FastMCP 和 stdio_client 提供了一种简便的本地开发和测试方式,适合快速原型开发或单机环境。这种方式不需要额外的网络配置,直接通过本地进程通信即可。
但不限于此:支持远程部署
MCP 协议本身并不强制要求服务端和客户端在同一台机器上。如果需要跨机器运行,可以通过以下方式实现:
网络通信替代 stdio:
MCP 支持通过网络接口(如 WebSocket、HTTP 或 gRPC)运行服务端。你可以修改服务端代码,使用 mcp.server 提供的网络模块(例如 WebSocketMCP),然后将服务端部署到远程机器上。
客户端相应地使用 mcp.client 的网络客户端(如 WebSocketClient)连接到服务端的 IP 地址和端口。
示例(伪代码)
# server.py
from mcp.server.websocket import WebSocketMCPmcp = WebSocketMCP("SimpleServer", host="0.0.0.0", port=8765@mcp.tool()
def add_numbers(a: int, b: int) -> int:return a + bmcp.run()
# client.py
from mcp.client.websocket import WebSocketClientasync with WebSocketClient("ws://remote-server-ip:8765") as session:result = await session.call_tool("add_numbers", {"a": 5, "b": 3})
独立运行服务端:
你可以在远程机器上手动启动服务端(例如 python server.py),然后客户端通过网络协议连接到它,而不是依赖 StdioServerParameters 动态启动。
结论
默认示例:服务端和客户端在一台机器上,因为 stdio 是最简单的实现方式。
扩展能力:通过网络接口,服务端完全可以部署在另一台机器上,MCP 的设计支持这种灵活性。只是我在示例中选择了最基础的 stdio 方式,便于快速上手。
问题 2:先启动 server.py
岂不是没意义?
为什么我说可以先启动 server.py
?
我在前文提到“运行服务端:python server.py(可选,手动测试用)”,主要是为了说明一种调试或独立运行服务端的方式。这种方式确实和客户端通过 StdioServerParameters 动态启动服务端的逻辑有些冲突。让我澄清背后的意义和适用场景:
手动启动的意义:
调试服务端:如果你只想测试服务端的功能是否正确,可以先手动运行 python server.py,然后用 MCP 提供的工具(如 mcp inspect server.py)检查它暴露的工具和资源。这种方式不需要客户端,直接验证服务端是否按预期工作。
独立运行模式:某些情况下,服务端可能设计为长期运行的进程(例如作为后台服务),而不是每次都被客户端启动。这时手动启动是有意义的。
与动态启动的冲突:
你是对的:如果客户端通过 StdioServerParameters 动态启动 server.py,那么手动先启动 server.py 确实没有意义。因为客户端会重新创建一个服务端进程,而不会连接到已经运行的实例。
在 stdio 模式下,手动启动的服务端和客户端无法直接通信,除非客户端知道如何“附着”到已有进程(但这不是 stdio_client 的默认行为)。
澄清我的意图:
我提到手动启动是为了展示服务端代码的独立性(即它可以单独运行并等待连接)。但在完整的客户端-服务端交互示例中,确实应该依赖客户端动态启动服务端,而不是手动启动。
如果使用网络模式(例如 WebSocket),手动启动服务端然后让客户端连接是完全可行的,但 stdio 模式下不适用。
修正建议
为了避免混淆,在 stdio 模式的完整工作流中,应该省略“先手动启动 server.py”的建议,直接依赖客户端启动服务端。正确的步骤是:
运行 python client.py,客户端会自动启动 server.py 并与之交互。
综合回答
服务端和客户端是否必须在一台机器上?
在 stdio 模式下(示例中使用的模式),是的,因为通信依赖本地进程的 stdin/stdout。
但通过网络接口(如 WebSocket),服务端可以部署在远程机器上,MCP 支持这种分布式部署。
先启动 server.py 是否有意义?
在 stdio 模式下,如果客户端负责动态启动服务端,手动先启动没有实际作用。
手动启动适用于调试服务端或使用网络模式时让服务端独立运行。
改进后的理解
在我的示例中,StdioServerParameters 的设计是为了简化本地开发,确保客户端能“一键启动”整个系统。但如果你的目标是跨机器部署或独立运行服务端,可以切换到网络模式。我之前的表述中“手动启动”的建议确实在 stdio 上下文中显得多余,感谢你指出这一点!
如果你想深入探讨网络模式的实现(例如如何将服务端部署到另一台机器),我可以提供更具体的代码示例。有什么进一步的想法或疑问吗?
远程模式,client 得先连上服务端的某个监听服务,要不然怎么启动服务端程序?
你的疑问非常有道理!在远程模式下,客户端(client)无法像本地 stdio 模式那样通过 StdioServerParameters 直接启动服务端(server)程序,因为它们运行在不同的机器上,客户端没有权限或能力直接在远程机器上执行命令(如 python server.py)。因此,在远程模式下,服务端必须已经作为一个独立的进程运行并监听某个网络接口(如 WebSocket、HTTP 端口),客户端才能通过网络连接到它。
让我详细解释远程模式的逻辑,并提供一个基于 WebSocket 的服务端和客户端示例,解决“要不然怎么启动服务端程序”的问题。
远程模式的核心逻辑
服务端独立运行并监听:
在远程模式下,服务端需要先在目标机器上手动启动(或通过脚本、系统服务等方式部署为常驻进程)。
服务端会监听一个网络接口(例如 WebSocket 的端口),等待客户端连接。
这与 stdio 模式不同,stdio 模式中客户端通过 subprocess 启动服务端,而远程模式下服务端是预先运行的。
客户端连接而非启动:
客户端不再负责启动服务端,而是通过网络协议(如 WebSocket 的 URL,例如 ws://remote-server-ip:8765)连接到已经运行的服务端。
客户端需要知道服务端的地址和端口(通常由开发者提供或通过配置传递)。
为什么客户端无法远程启动服务端?
客户端运行在本地机器上,没有直接访问远程机器文件系统或执行命令的权限。
远程启动服务端需要额外的机制,例如 SSH 调用、远程 API 或云服务的部署脚本,但这超出了 MCP 协议本身的范畴,属于部署层面的问题。
远程模式下的工作流程
部署服务端:
在远程机器上运行服务端程序(例如 python server.py),让它监听某个端口。
确保远程机器的防火墙允许该端口访问。
客户端连接:
客户端通过服务端的 IP 地址和端口建立连接。
一旦连接成功,客户端可以通过 MCP 协议调用服务端的工具或获取资源。
示例代码:基于 WebSocket 的远程模式
服务端代码 (server.py
)
在远程机器上运行,监听
WebSocket 端口。
from mcp.server.websocket import WebSocketMCP# 创建 WebSocket 服务端实例
mcp = WebSocketMCP(name="RemoteServer",host="0.0.0.0", # 监听所有网络接口port=8765 # 监听端口 8765
)# 定义一个工具:计算两个数字之和
@mcp.tool()
def add_numbers(a: int, b: int) -> int:"""Add two numbers together."""return a + b# 定义一个资源:返回问候语
@mcp.resource("greeting://hello")
def get_greeting() -> str:"""Return a static greeting message."""return "Hello from Remote MCP Server!"# 运行服务端
if __name__ == "__main__":print("服务端启动,监听 ws://0.0.0.0:8765")mcp.run()
客户端代码 (client.py
)
在本地机器上运行,连接远程服务端。
import asyncio
from mcp.client.websocket import WebSocketClientasync def main():# 指定远程服务端的 WebSocket 地址server_url = "ws://remote-server-ip:8765" # 替换为实际远程 IP# 创建 WebSocket 客户端并连接服务端async with WebSocketClient(server_url) as session:# 初始化会话await session.initialize()# 列出可用工具tools_response = await session.list_tools()print("可用工具:", [tool.name for tool in tools_response.tools])# 调用 add_numbers 工具result = await session.call_tool("add_numbers", {"a": 5, "b": 3})print("工具调用结果 (5 + 3):", result.toolResult)# 获取 greeting 资源(需要 MCP 1.3.0+)# resource = await session.get_resource("greeting://hello")# print("资源内容:", resource.content)# 运行异步客户端
if __name__ == "__main__":asyncio.run(main())
如何运行
部署服务端:
将 server.py 上传到远程机器(假设 IP 为 192.168.1.100)。
在远程机器上运行: python server.py
服务端启动后会输出: 服务端启动,监听 ws://0.0.0.0:8765
确保防火墙开放 8765 端口,并且远程机器的网络可被客户端访问。
运行客户端:
在本地机器上修改 client.py 中的 server_url,替换为远程服务端的实际 IP,例如: server_url = "ws://192.168.1.100:8765"
运行客户端: python client.py
预期输出: 可用工具: ['add_numbers']
工具调用结果 (5 + 3): 8
解答你的疑问
“client 得先连上服务端的某个监听服务”:
是的,在远程模式下,服务端必须先运行并监听某个网络接口(如 WebSocket 的 8765 端口)。客户端通过这个接口连接,而不是启动服务端。
服务端启动是开发者的责任,通常通过手动运行、脚本或系统服务(如 systemd)实现。
“要不然怎么启动服务端程序?”:
在远程模式下,客户端无法直接启动服务端程序。启动服务端需要:
手动启动:在远程机器上运行 python server.py。
自动化部署:使用 SSH、Docker、云服务(如 AWS Lambda)或其他工具在远程机器上启动服务端。
MCP 协议本身只定义了通信规范,不负责远程进程管理,因此启动服务端是前置步骤。
扩展:如何自动化启动远程服务端?
如果你希望客户端能“间接启动”远程服务端,可以借助外部工具:
SSH:
客户端通过 SSH 登录远程机器,执行 python server.py &(后台运行)。
import paramiko
ssh = paramiko.SSHClient()
ssh.connect("remote-server-ip", username="user", password="pass")
ssh.exec_command("python /path/to/server.py &")
然后再用 WebSocket 连接。
云服务 API:
如果服务端部署在云端(如 AWS EC2),可以用 AWS SDK 启动实例并运行服务端。
但这些方法超出了 MCP 的核心功能,通常由部署流程处理。
总结
远程模式下:服务端必须先在远程机器上运行并监听网络接口,客户端通过 IP 和端口连接。
启动服务端:需要手动或通过外部工具完成,客户端无法直接启动远程服务端。
与 stdio 模式的区别:stdio 模式中客户端动态启动服务端,而远程模式依赖服务端独立运行。
这个 WebSocket 示例展示了典型的远程部署方式。如果还有疑问,比如如何配置网络安全或优化远程通信,我可以进一步帮你解答!
为什么mcp协议和ai有关系,看上去不就是个服务端暴露接口给客户端?
你的观察很敏锐!从表面上看,MCP(Model Context Protocol)协议确实就像一个服务端暴露接口给客户端的通用框架,跟常见的 RPC(远程过程调用)或 API 系统没有太大区别。但 MCP 协议与 AI 的关系并不是偶然的,它的设计和应用场景与 AI(特别是大语言模型,LLM)有深层次的关联。让我详细解释一下为什么 MCP 和 AI 有关系,以及它如何超越普通的“服务端-客户端”模型。
MCP 协议的核心设计与 AI 的关联
MCP 由 Anthropic(一家由前 OpenAI 研究员创立的 AI 公司)提出,它的初衷并不是简单地做一个通用的服务端-客户端通信协议,而是为了解决 AI 系统(尤其是 LLM)在实际应用中的一个关键问题:如何高效、安全地将外部工具和上下文数据集成到 AI 模型中。这种需求来源于 AI 的工作方式和现代 AI Agent 的发展。
- AI 需要外部工具和数据
LLM 的局限性:
大语言模型(如 GPT、Claude)虽然强大,但它们本质上是静态的知识库,训练数据有截止日期,无法直接访问实时信息(如当前天气、最新新闻)或执行具体操作(如发送邮件、查询数据库)。
为了让 LLM 更实用,开发者需要为它们提供“外部能力”,这就是工具(Tools)和资源(Resources)的来源。
MCP 的作用:
MCP 提供了一种标准化的方式,让服务端可以暴露工具(如 add_numbers)和资源(如 greeting://hello),AI 模型可以通过客户端调用这些接口。
例如,一个 AI Agent 可以通过 MCP 调用天气 API、读取文件内容或执行计算,而无需将这些功能硬编码到模型本身。 - AI Agent 的崛起
什么是 AI Agent:
AI Agent 是指能够自主完成任务的 AI 系统,不仅仅是回答问题,而是能与外部世界交互(例如订票、写代码、分析数据)。
这需要 AI 模型与外部系统无缝协作,而 MCP 正是为此设计的桥梁。
MCP 的标准化:
MCP 定义了一套协议(通过 JSON 消息交换),让 AI 模型可以动态发现和调用服务端提供的功能。这种标准化对构建可扩展的 AI Agent 生态至关重要。
例如,Anthropic 的 Claude 模型可以通过 MCP 客户端访问外部工具,而开发者只需实现 MCP 服务端,无需关心模型的具体实现。 - 上下文增强(Context Augmentation)
- AI 的上下文需求:
LLM 的推理能力依赖于输入的上下文(Prompt)。但很多时候,上下文数据来自外部(如用户上传的文件、数据库记录)。
MCP 的资源(Resources)功能允许服务端提供结构化的上下文数据,AI 可以通过 get_resource 获取这些信息,从而增强推理能力。
例子:
假设你问 AI:“分析今天的股票市场趋势。”AI 无法直接知道“今天”的数据,但通过 MCP,它可以调用服务端的 get_stock_data 工具或资源,获取实时数据后再回答。
为什么 MCP 不只是“服务端暴露接口”?
表面上,MCP 的确是服务端暴露接口给客户端,但它与 AI 的关系体现在以下几个独特的设计和目标上:
- 专为 AI 优化
工具和资源的区分:
MCP 不仅提供函数调用(Tools),还支持只读的资源(Resources),这与 AI 的使用模式契合。工具用于执行操作(如计算、写文件),资源用于提供上下文(如文档内容、配置信息)。
普通的 RPC 或 REST API 通常只关注函数调用,而 MCP 的资源概念直接服务于 AI 的上下文需求。
动态发现:
MCP 允许客户端(AI)通过 list_tools 动态查询服务端提供的所有功能。这种“自描述”能力让 AI 可以根据任务需要选择合适的工具,而无需预先知道所有接口细节。 - 安全性与可控性
Anthropic 的背景:
Anthropic 的核心使命是构建安全、可解释的 AI 系统。MCP 继承了这一理念,设计时考虑了如何限制 AI 的权限,避免滥用外部工具。
例如,服务端可以明确定义哪些工具和资源对 AI 可用,防止模型执行危险操作(如删除文件)。
对比普通 API:
普通 REST API 通常是开放的,客户端可以随意调用。而 MCP 的协议更像一个“受控通道”,专为 AI 的调用模式设计,确保安全性和一致性。 - 与 LLM 的集成
协议的轻量化:
MCP 使用 JSON 格式通过 stdio 或 WebSocket 通信,这种轻量化的设计非常适合嵌入到 LLM 的运行时环境中。例如,Claude 可以通过 MCP 客户端直接与服务端交互,无需复杂的中间层。
实际案例:
Anthropic 在发布 MCP 时提到,它是 Claude 生态的一部分。开发者可以通过 MCP 为 Claude 提供自定义工具,从而扩展其能力。
MCP 与 AI 的具体联系:一个例子
假设你有一个 AI 助手,想让它帮你计算两数之和并获取天气信息:
没有 MCP:
你需要手动为 AI 写代码,调用天气 API 或计算函数,然后将结果拼接到 Prompt 中。
每次任务变化,都要修改代码,效率低下。
使用 MCP:
服务端实现
@mcp.tool()
def add_numbers(a: int, b: int) -> int:return a + b@mcp.tool()
def get_weather(city: str) -> str:# 调用外部天气 APIreturn "Sunny, 25°C"
AI(通过 MCP 客户端):
自动发现可用工具:add_numbers 和 get_weather。
调用 add_numbers(5, 3) 得到 8。
调用 get_weather("Shanghai") 得到天气信息。
AI 根据结果生成回答:“5 + 3 = 8,今天上海天气晴朗,25°C。”
这种“即插即用”的能力正是 MCP 与 AI 的核心联系。
为什么不像普通的 API?
你可能会问:“这不就是 REST API 的功能吗?”表面上有些相似,但区别在于:
目标用户:
REST API 通常面向人类开发者或通用客户端,而 MCP 的主要“客户端”是 AI 模型。
上下文驱动:
MCP 强调为 AI 提供动态上下文,而 REST API 更关注数据传输。
生态整合:
MCP 是 Anthropic AI 生态的一部分,专为 LLM 和 AI Agent 设计,而非通用的服务端框架。
总结:MCP 与 AI 的关系
MCP 协议表面上是一个服务端-客户端通信框架,但它的核心价值在于:
为 AI 提供外部能力:通过工具和资源扩展 LLM 的功能。
支持 AI Agent:标准化外部交互,让 AI 更自主。
安全与效率:专为 AI 设计,兼顾上下文增强和权限控制。
所以,虽然它“看上去不就是服务端暴露接口给客户端”,但它的设计初衷、应用场景和实现细节都与 AI(特别是 LLM 和 AI Agent)紧密相关。它不是一个通用的 RPC 替代品,而是 AI 生态中的一块拼图。
如果你还有疑问,比如想了解 MCP 如何具体集成到某个 AI 模型中,我可以进一步举例说明!
但是大模型不是在云端执行么,它调用本地mcp执行工具或者取数据,根本不是模型的能力啊?
你的质疑非常有道理,确实触及了 MCP(Model Context Protocol)与大语言模型(如 Claude)集成的一个关键问题:如果大模型运行在云端(如 Anthropic 的服务器),它如何调用本地的 MCP 服务端来执行工具或获取数据?这看似与“模型能力”无关,反而像是外部系统的功劳。让我详细解答这个问题,澄清其中的逻辑和实现细节,并解释为什么这仍然与模型的能力扩展密切相关。
大模型在云端 vs 本地 MCP 的问题
- 现状:大模型通常在云端
你是对的,像 Claude、GPT-4 这样的大语言模型通常部署在云端服务器上,由提供商(如 Anthropic、OpenAI)托管。
用户通过 API 或 Web 界面与模型交互,输入 Prompt,模型返回结果,所有计算都在云端完成。
在这种情况下,模型本身无法直接访问本地机器的资源或工具,因为它没有本地的运行时环境。
- 本地 MCP 的矛盾
如果 MCP 服务端运行在用户的本地机器上(例如 python server.py),而 Claude 在云端,那么云端的 Claude 如何通过网络调用本地的 MCP 服务端?这似乎存在物理和逻辑上的障碍:
网络方向:云端模型需要主动连接本地服务端,但本地机器通常没有公网 IP,且受防火墙限制。
权限问题:云端模型没有直接访问本地系统的权限。
如何解决:MCP 的实际运行模式
MCP 与 Claude 的集成并不是让云端模型直接调用本地 MCP 服务端,而是通过以下方式实现:
本地客户端作为中间层
- 架构:
用户的本地机器运行一个 MCP 客户端(而不是直接让 Claude 调用本地 MCP 服务端)。
这个客户端负责与本地的 MCP 服务端交互,执行工具或获取数据,然后将结果通过 Anthropic 的 API 发送给云端的 Claude。 - 流程:
用户输入 Prompt(例如“计算 5 + 3 并告诉我本地文件的天气数据”)。
本地 MCP 客户端解析 Prompt,调用本地 MCP 服务端的工具(add_numbers)或资源(读取文件)。
客户端将结果(例如 8 和天气数据)附加到 Prompt,发送给云端 Claude。
云端 Claude 根据这些数据生成最终回答。
代码示例(伪代码):
from anthropic import Anthropic
from mcp.client.websocket import WebSocketClientasync def process_prompt(prompt):# 本地 MCP 客户端连接本地服务端async with WebSocketClient("ws://localhost:8765") as session:await session.initialize()result = await session.call_tool("add_numbers", {"a": 5, "b": 3})weather = await session.call_tool("get_weather", {"city": "Shanghai"})# 将结果附加到 Prompt,发送给云端 Claudeenhanced_prompt = f"{prompt}\n计算结果: {result.toolResult}\n天气: {weather.toolResult}"client = Anthropic(api_key="your-api-key")response = client.completions.create(model="claude-3",prompt=enhanced_prompt,max_tokens=100)return response.completion# 用户调用
print(asyncio.run(process_prompt("总结一下计算和天气")))
关键点:MCP 服务端和客户端都在本地运行,Claude 只接收处理后的数据。这种模式下,模型不直接调用本地工具,而是依赖本地客户端代理。
云端 MCP 服务端
- 架构:
MCP 服务端也可以部署在云端,与 Claude 运行在同一网络环境中(例如 Anthropic 的服务器或用户自己的云服务器)。
云端的 Claude 通过内部网络(或公网)直接调用云端 MCP 服务端。 - 流程:
用户输入 Prompt。
云端 Claude 通过 MCP 客户端连接云端 MCP 服务端,调用工具或获取资源。
服务端返回结果,Claude 生成回答。 - 优点:
无需本地运行任何程序,全部在云端完成。
服务端可以访问云端资源(如数据库、API),但无法直接访问本地数据,除非用户上传。
示例:
Anthropic 可能提供托管的 MCP 服务端,开发者只需注册工具,Claude 就能调用。
混合模式:本地上传 + 云端执行
- 架构:
本地 MCP 客户端将工具结果或数据上传到云端,Claude 在云端处理。
或者,用户直接将本地数据(文件、参数)通过 API 上传,Claude 调用云端 MCP 服务端处理。 - 流程:
用户上传本地文件或参数到 Anthropic API。
云端 Claude 通过 MCP 调用云端服务端,处理这些数据。
返回结果。
例子:
用户上传一个 CSV 文件,Claude 通过 MCP 的 analyze_csv 工具分析并返回结果。
为什么这仍然是模型能力的扩展?
你提到“根本不是模型的能力”,这一点需要澄清。虽然执行工具或获取数据的具体操作由 MCP 服务端完成,但这与模型能力的关系体现在以下几个方面:
- 推理能力的增强
静态模型的局限:Claude 本身只能基于训练数据推理,无法执行实时计算或访问外部信息。
MCP 的作用:通过 MCP,Claude 可以利用外部工具的结果进行更复杂的推理。例如:
没有 MCP:Claude 只能说“我不知道今天的天气”。
有 MCP:Claude 获取天气数据后可以说“上海今天晴天,适合出门”。
能力提升:MCP 将模型从“纯文本生成”升级为“基于动态数据的推理”,这本质上是能力的扩展。 - 任务执行的代理化
AI Agent 概念:现代 AI 的目标不仅是回答问题,而是完成任务(例如订票、分析数据)。MCP 让 Claude 成为一个“指挥者”,通过调用外部工具完成任务。
模型的作用:Claude 的能力在于解析用户意图、决定调用哪些工具、并整合结果生成自然语言回答。这种“决策和整合”正是模型的核心价值。
例子:
用户说:“帮我计算 5 + 3 并订机票。”
Claude 决定调用 add_numbers 和 book_flight(MCP 工具),然后说:“5 + 3 = 8,我已为你订好机票。” - 生态系统的协同
MCP 的设计意图:MCP 不是让模型自己执行所有操作,而是构建一个生态,让模型与外部系统协作。
模型的智能:Claude 的能力体现在如何理解复杂指令、选择合适的工具、处理异常情况(例如工具返回错误时如何应对),这比单纯的文本生成更高级。
为什么不直接在云端硬编码工具?
你可能会问:“为什么不直接在云端为 Claude 内置这些工具?”答案是:
- 灵活性:
MCP 允许开发者自定义工具,而无需 Anthropic 修改 Claude 的核心代码。
例如,你可以用 MCP 添加一个独特的 analyze_my_company_data 工具,这是云端通用模型无法预装的。 - 模块化:
将工具和资源分离到 MCP 服务端,保持模型的轻量化和专注性(推理而非执行)。 - 安全性:
通过 MCP,Anthropic 可以控制 Claude 的权限,避免模型直接访问不受控的外部系统。
澄清你的疑问
“大模型调用本地 MCP 不是模型能力”:
严格来说,执行工具或取数据的代码确实运行在 MCP 服务端,不是模型本身。但模型的能力在于如何利用这些外部输入完成任务。
就好比人类指挥一个机器人:机器人执行具体动作,但决策和指令来自人类。Claude 的“智能”体现在决策和整合,而 MCP 是它的“手臂”。
“云端模型如何调用本地 MCP”:
实际中,Claude 不直接调用本地 MCP。常见模式是:
本地客户端代理:本地运行 MCP,处理工具后将结果发给云端。
云端部署:MCP 服务端也在云端,与 Claude 同环境运行。
总结
MCP 与 Claude 的集成并不是让云端模型直接调用本地服务端,而是通过以下方式实现:
本地代理:本地 MCP 客户端调用服务端,结果上传给云端 Claude。
云端协同:MCP 服务端部署在云端,Claude 直接调用。 这不是模型“自己执行”的能力,而是通过 MCP 扩展了模型的任务处理能力和外部协作能力。MCP 的价值在于让 Claude 从静态知识库变为动态 Agent,而这种扩展正是 AI 发展的方向。
如果你想深入探讨某个模式(例如本地代理的具体实现),我可以进一步提供代码或分析!有什么想法吗?