一文带你了解什么是MCP
一. 什么是MCP
MCP(Model Context Protocol 模型上下文协议)是一个用于将 AI 应用程序连接到外部系统的开源标准。它通过标准化大语言模型与外部工具的交互方式,解决了AI 智能体应用开发中的痛点,正迅速成为 AI 领域的核心基础设施标准。Anthropic 于2024年底首次提出该协议,随后被 OpenAI、阿里、腾讯、字节跳动等全球顶尖科技企业所采纳,使其迅速成为 AI 基础设施的关键支柱。
MCP 的核心原理是将互联网服务(高德、谷歌)或本地操作系统 API(文件系统、数据库、终端)封装成 AI 智能体能够理解和使用的工具,让 AI 智能体能够自由地调用这些工具实现复杂的业务逻辑和功能。
二. MCP的核心架构
MCP 采用客户端-服务器架构,其中 MCP 主机(例如 Claude Code 或 Claude Desktop 这样的 AI 应用程序)会建立与一个或多个 MCP 服务器的连接。MCP 主机通过为每个 MCP 服务器创建一个 MCP 客户端来实现这一点。每个 MCP 客户端都与其对应的 MCP 服务器保持专用连接。
- MCP Host:协调并管理一个或多个 MCP Client的 AI 应用。
- MCP Client:一个维护与 MCP Server连接并从 MCP Server获取上下文以供 MCP Host 使用的组件。
- MCP Server:为 MCP 客户端提供上下文的程序。

MCP 主要分为以下两层 - Data layer:定义基于 JSON-RPC 的 Client-Server 通信协议,涵盖生命周期管理及核心原语如 tools、resources、prompts 和 notifications。
- Transport layer:定义了 Client 和 Server 之间是如何建立连接、如何把消息打包发送,以及授权认证的。
三. MCP的核心原语
MCP 原语是 MCP 中最重要的概念。它们定义了客户端和服务器可以相互提供的内容。这些原语规定了可以与 AI 应用共享的上下文信息类型以及可以操作执行的范围。MCP 定义了服务器可暴露的三种核心原语。
- Tool(工具):AI 应用可调用的可执行函数,用于执行各类操作(如文件操作、API 调用、数据库查询)。
- Resources(资源):为 AI 应用提供上下文信息的数据源(如文件内容、数据库记录、API 响应)。
- Prompts(提示词):借助提示词,服务器可定义可复用的模板与工作流,供客户端便捷地向用户和 LLM 展示,从而高效实现常见 LLM 交互的标准化与复用。
四. MCP的通信模式
MCP 的通信模式主要分为基于本地进程通信的 STDIO 和基于网络的 HTTP 传输。最新的协议版本使用 Streamable HTTP 取代了之前的 HTTP + SSE 方式,当前 MCP 官方推荐的传输方式,尤其适合生产环境和云服务。
| STDIO | SSE | Streamable HTTP | |
|---|---|---|---|
| 核心原理 | 通过本地进程的标准输入(stdin)和标准输出(stdout)进行通信。客户端以子进程的形式启动 MCP 服务器,双方通过管道交换JSON-RPC格式的消息,消息以换行符分隔。 | 客户端与服务器建立独立的网络连接,使用HTTP POST发送请求,通过SSE(Server-Sent Events)长连接接收服务器推送 | 在标准HTTP POST/GET基础上,增加了将响应动态升级为 SSE 流的能力, 支持无状态模式(Stateless Server),无需维持长连接。 |
| 适用场景 | 本地进程间通信(命令行工具、文件系统操作),开发工具插件 | 需要服务器持续推送状态的远程应用。需要实时数据推送的场景(如对话式AI的流式输出) | 所有需要远程访问的场景,尤其是云服务、微服务架构,需要灵活流式响应的场景(如 AI 助手的动态输出) |
| 优点 | 低延迟、无需网络配置,简单可靠 | 支持实时单向推送,适合流式交互。 | 支持连接恢复(无需重新开始)。无需服务器维持长连接,降低资源压力,统一端点(/message),简化接口设计。 |
| 限制 | 仅限本地使用,不支持分布式部署,无法远程 | 服务器需维持长连接,压力大。连接不可恢复,断开后需重新建立会话已逐步被弃用(被 Streamable HTTP 取代) | 实现较 stdio 稍复杂,需要处理 HTTP 和可选的 SSE 逻辑。 |
五. 快速入门
服务器开发
引入包并初始化 MCP 服务
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# uv add "mcp[cli]" httpx
# Python版本 >= 3.10
# Python MCP SDK >= 1.2.0
# 初始化FastMCP服务器
mcp = FastMCP("weather")
# 定义常量
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""向NWS API发出GET请求,处理错误并返回JSON响应"""
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 None
def format_alert(feature: dict) -> str:
"""将警报特征格式化为可读字符串"""
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:
"""获取指定州的天气警报(使用两字母州代码如CA/NY)"""
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data:
return "无法连接到天气服务,请稍后再试。"
if "features" not in data:
return "天气服务返回格式异常。"
if not data["features"]:
return f"✅ {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:
"""获取指定位置的天气预报
Args:
latitude: 位置的纬度
longitude: 位置的经度
"""
# 首先获取预报网格端点
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "无法获取位置信息,请检查经纬度是否正确。"
# 获取预报URL
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "无法获取详细预报信息。"
# 格式化预报时段(显示接下来3个时段)
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]: # Only show next 5 periods
forecast = f"""
{period["name"]}:
Temperature: {period["temperature"]}°{period["temperatureUnit"]}
Wind: {period["windSpeed"]} {period["windDirection"]}
Forecast: {period["detailedForecast"]}
"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
def main():
# 初始化并运行服务
mcp.run(transport="stdio")
if __name__ == "__main__":
main()
客户端开发
import asyncio
import os
import sys
from contextlib import AsyncExitStack
from pathlib import Path
from anthropic import Anthropic
from dotenv import load_dotenv
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# uv add mcp anthropic python-dotenv
# 加载 .env 文件中的环境变量(如 API 密钥)
load_dotenv()
# Claude 模型配置常量
ANTHROPIC_MODEL = "claude-sonnet-4-5"
class MCPClient:
"""
MCP (Model Context Protocol) 客户端
这个客户端负责:
1. 连接到 MCP 服务器(通过 stdio)
2. 获取服务器提供的工具列表
3. 与 Claude API 交互,让 Claude 决定何时调用工具
4. 执行工具调用并将结果返回给 Claude
"""
def __init__(self):
# MCP 会话对象,用于与服务器通信
self.session: ClientSession | None = None
# AsyncExitStack 用于管理多个异步上下文管理器
# 确保所有资源在退出时正确清理
self.exit_stack = AsyncExitStack()
# Anthropic 客户端(延迟初始化)
self._anthropic: Anthropic | None = None
@property
def anthropic(self) -> Anthropic:
"""
延迟初始化 Anthropic 客户端
只有在实际需要调用 Claude API 时才创建客户端,
避免在不需要时占用资源
"""
if self._anthropic is None:
self._anthropic = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
return self._anthropic
async def connect_to_server(self, server_script_path: str):
"""
连接到 MCP 服务器
Args:
server_script_path: 服务器脚本的路径(.py 或 .js 文件)
Raises:
ValueError: 如果文件类型不是 .py 或 .js
"""
# 根据文件扩展名判断服务器类型
is_python = server_script_path.endswith(".py")
is_js = server_script_path.endswith(".js")
if not (is_python or is_js):
raise ValueError("Server script must be a .py or .js file")
# 根据服务器类型配置启动参数
if is_python:
# Python 服务器:使用 uv 运行
# 获取脚本的绝对路径
path = Path(server_script_path).resolve()
# 配置 stdio 服务器参数
server_params = StdioServerParameters(
command="uv", # 使用 uv 包管理器
args=["--directory", str(path.parent), "run", path.name], # 在脚本目录下运行
env=None, # 继承当前环境变量
)
else:
# JavaScript 服务器:直接使用 node 运行
server_params = StdioServerParameters(
command="node", # 使用 Node.js
args=[server_script_path],
env=None,
)
# 启动服务器并建立 stdio 通信通道
# enter_async_context 会自动管理资源的生命周期
stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params)
)
self.stdio, self.write = stdio_transport
# 创建客户端会话
self.session = await self.exit_stack.enter_async_context(
ClientSession(self.stdio, self.write)
)
# 初始化会话(发送初始化请求)
await self.session.initialize()
# 获取服务器提供的工具列表并打印
response = await self.session.list_tools()
tools = response.tools
print("\n已连接到服务器,可用工具:", [tool.name for tool in tools])
async def process_query(self, query: str) -> str:
"""
处理用户查询
流程:
1. 将用户查询发送给 Claude
2. 如果 Claude 决定调用工具,执行工具并返回结果
3. 将工具执行结果返回给 Claude 继续处理
4. 返回最终的响应文本
Args:
query: 用户的查询文本
Returns:
Claude 的最终响应文本
"""
# 初始化消息列表
messages = [{"role": "user", "content": query}]
# 获取服务器上可用的工具列表
response = await self.session.list_tools()
available_tools = [
{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema, # 工具的输入参数模式
}
for tool in response.tools
]
# 第一次调用 Claude API
# 传入工具列表,让 Claude 可以决定是否需要调用工具
response = self.anthropic.messages.create(
model=ANTHROPIC_MODEL,
max_tokens=1000,
messages=messages,
tools=available_tools, # 告诉 Claude 可用的工具
)
# 存储最终响应的文本片段
final_text = []
# 处理 Claude 的响应内容
for content in response.content:
# 情况1:Claude 返回普通文本
if content.type == "text":
final_text.append(content.text)
# 情况2:Claude 决定调用工具
elif content.type == "tool_use":
tool_name = content.name
tool_args = content.input
# 执行工具调用
# 通过 MCP 协议将工具调用请求发送给服务器
result = await self.session.call_tool(tool_name, tool_args)
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
# 将工具调用结果添加到对话历史中
# 先添加 Claude 的工具调用响应(如果有文本)
if hasattr(content, "text") and content.text:
messages.append({"role": "assistant", "content": content.text})
# 添加工具执行结果作为用户消息
# MCP 返回的 result.content 包含工具的执行结果
messages.append({"role": "user", "content": result.content})
# 第二次调用 Claude API
# 将工具执行结果发送给 Claude,让其生成最终响应
response = self.anthropic.messages.create(
model=ANTHROPIC_MODEL,
max_tokens=1000,
messages=messages,
)
# 添加 Claude 的最终响应
final_text.append(response.content[0].text)
# 将所有响应片段合并返回
return "\n".join(final_text)
async def chat_loop(self):
"""
运行交互式聊天循环
用户可以在命令行中输入查询,程序会实时响应
输入 'quit' 退出聊天
"""
print("\nMCP 客户端已启动!")
print("请输入查询内容,输入 'quit' 退出。")
while True:
try:
# 获取用户输入
query = input("\nQuery: ").strip()
# 退出条件
if query.lower() == "quit":
break
# 处理查询并打印响应
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\n错误: {str(e)}")
async def cleanup(self):
"""
清理资源
关闭所有打开的连接和上下文管理器
"""
await self.exit_stack.aclose()
async def main():
"""
主函数
流程:
1. 检查命令行参数(必须提供服务器脚本路径)
2. 创建 MCP 客户端
3. 连接到指定的服务器
4. 检查 API 密钥
5. 启动交互式聊天循环
"""
# 检查是否提供了服务器脚本路径
if len(sys.argv) < 2:
print("使用方法: python client.py <服务器脚本路径>")
sys.exit(1)
client = MCPClient()
try:
# 连接到 MCP 服务器
await client.connect_to_server(sys.argv[1])
# 检查 Anthropic API 密钥是否存在
api_key = os.getenv("ANTHROPIC_API_KEY")
if not api_key:
print("\n未找到 ANTHROPIC_API_KEY。要使用 Claude 查询这些工具,请设置您的 API 密钥:")
print(" export ANTHROPIC_API_KEY=您的-api-密钥")
return
# 启动聊天循环
await client.chat_loop()
finally:
# 确保资源被清理(即使发生异常)
await client.cleanup()
if __name__ == "__main__":
# 运行异步主函数
asyncio.run(main())
六. 总结
MCP 采用模块化设计与多协议兼容的架构,专为复杂 AI 应用场景打造。这一架构从底层保障了系统的高可靠性、易扩展性和强适应性,使 MCP 成为构建企业级 A I应用的理想之选,能够从容应对多样化业务场景下的技术挑战。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)