Model Context Protocol (MCP) 详解:连接AI与数据的开放标准
文章目录
引言
在人工智能快速发展的今天,大型语言模型(LLM)已经成为各种应用的核心组件。然而,即使是最先进的模型也面临着一个共同的挑战:如何有效地访问和利用外部数据与工具。Model Context Protocol(MCP)应运而生,它提供了一个开放标准,用于规范化应用程序如何向LLM提供上下文信息,从而解决这一关键问题。
本文将深入探讨Model Context Protocol的核心概念、架构设计、实现细节和实际应用,并通过丰富的代码示例展示如何利用MCP构建强大的AI应用。无论你是AI开发者、数据科学家还是对AI技术感兴趣的读者,这篇文章都将帮助你理解MCP如何改变AI应用与数据交互的方式。
什么是Model Context Protocol?
Model Context Protocol(MCP)是一个开放协议,用于标准化应用程序如何向大型语言模型(LLM)提供上下文。它可以被比喻为"AI应用程序的USB-C端口"。正如USB-C提供了一种标准化的方式来连接设备与各种外设和配件,MCP提供了一种标准化的方式来连接AI模型与不同的数据源和工具。
MCP的核心目标是解决AI助手与数据之间的隔离问题。即使是最先进的模型也受到与数据隔离的限制——它们被困在信息孤岛和传统系统之后。每个新的数据源都需要自己的定制实现,使真正连接的系统难以扩展。MCP通过提供一个通用的开放标准来连接AI系统与数据源,替代了分散的集成方式。
为什么需要MCP?
MCP帮助开发者在LLM之上构建代理和复杂工作流。LLM经常需要与数据和工具集成,而MCP提供:
- 预构建集成列表:不断增长的预构建集成列表,您的LLM可以直接插入使用
- 灵活切换提供商:在LLM提供商和供应商之间切换的灵活性
- 数据安全最佳实践:在您的基础设施内保护数据的最佳实践
MCP的核心优势
MCP提供了几个关键优势:
- 标准化:提供一个通用接口,减少为每个数据源创建自定义连接器的需要
- 安全性:遵循最佳实践,确保数据在您的基础设施内保持安全
- 灵活性:允许在不同的LLM提供商和供应商之间切换
- 可扩展性:随着生态系统的成熟,AI系统将在不同工具和数据集之间保持上下文
- 开放标准:作为一个协作的开源项目和生态系统开发
MCP的架构和工作原理
MCP遵循客户端-服务器架构,其中主机应用程序可以连接到多个服务器:
主要组件
- MCP主机(Hosts):是启动连接的LLM应用程序(如Claude Desktop或IDE),它们希望通过MCP访问数据
- MCP客户端(Clients):在主机应用程序内部维护与服务器的1:1连接的协议客户端
- MCP服务器(Servers):通过标准化的Model Context Protocol暴露特定功能的轻量级程序
- 本地数据源:MCP服务器可以安全访问的计算机文件、数据库和服务
- 远程服务:MCP服务器可以连接的通过互联网可用的外部系统(例如,通过API)
架构流程
在MCP架构中,主机应用程序(如Claude Desktop)包含MCP客户端,这些客户端通过传输层与MCP服务器通信。每个MCP服务器都是一个独立的进程,可以访问特定的数据源或功能。这种设计允许模块化和安全的数据访问,同时保持标准化的通信协议。
MCP的核心概念
1. 服务器(Server)
MCP服务器是协议的核心组件,它暴露数据和功能给LLM应用程序。服务器处理连接管理、协议合规性和消息路由。在Python实现中,FastMCP服务器是与MCP协议交互的主要接口。
服务器可以:
- 定义生命周期管理(启动/关闭)
- 指定依赖关系
- 提供类型安全的上下文
2. 资源(Resources)
资源是MCP如何向LLM暴露数据的方式。它们类似于REST API中的GET端点——它们提供数据,但不应执行重要的计算或产生副作用。资源通常用于:
- 提供静态配置数据
- 访问动态用户数据
- 检索文档或信息
资源使用URI模式定义,可以包含参数,例如:users://{user_id}/profile
3. 工具(Tools)
工具允许LLM通过服务器执行操作。与资源不同,工具预期会执行计算并产生副作用。工具可以:
- 执行计算(如计算BMI)
- 调用外部API(如获取天气数据)
- 修改数据或系统状态
工具通常定义为带有类型化参数和返回值的函数。
4. 提示(Prompts)
提示是可重用的模板,用于定义LLM交互模式。它们可以包含占位符,在运行时填充,使开发者能够创建一致的用户体验。
5. 上下文(Context)
上下文是MCP中的关键概念,它允许在请求处理期间访问元数据和状态。上下文可以包含:
- 请求特定信息
- 生命周期上下文(如数据库连接)
- 用户特定数据
MCP的协议规范和消息格式
MCP使用JSON-RPC 2.0作为其消息格式的基础。所有消息必须是UTF-8编码的。协议定义了三种主要的消息类型:
1. 请求(Requests)
请求从客户端发送到服务器或反之,用于启动操作。
{
"jsonrpc": "2.0",
"id": "request-123",
"method": "tool.call",
"params": {
"name": "add",
"arguments": {"a": 5, "b": 3}
}
}
2. 响应(Responses)
响应作为请求的回复发送,包含操作的结果或错误。
{
"jsonrpc": "2.0",
"id": "request-123",
"result": {
"value": 8
}
}
3. 通知(Notifications)
通知是从客户端发送到服务器或反之的单向消息。接收者不应发送响应。
{
"jsonrpc": "2.0",
"method": "log.info",
"params": {
"message": "操作已完成"
}
}
传输机制
MCP定义了两种标准传输机制:
- stdio:通过标准输入和标准输出进行通信
- Streamable HTTP:通过HTTP流进行通信
客户端应尽可能支持stdio,这是最简单和最通用的传输方式。
MCP的安装和部署
系统要求
要使用MCP,您需要:
- Python 3.10或更高版本
- 对于Python SDK:pip或uv包管理器
- 对于其他SDK:相应语言的包管理器
安装Python SDK
我们推荐使用uv来管理您的Python项目。在uv管理的Python项目中,通过以下方式添加mcp到依赖项:
uv add "mcp[cli]"
或者,对于使用pip的项目:
pip install mcp
安装其他SDK
MCP支持多种语言的SDK:
- TypeScript/JavaScript:
npm install @modelcontextprotocol/typescript-sdk
- Java:通过Maven或Gradle安装
- Kotlin:通过Maven或Gradle安装
- C#:通过NuGet安装
运行独立的MCP开发工具
使用uv运行mcp命令:
uv run mcp
MCP的使用示例
基础服务器示例
以下是一个简单的MCP服务器示例,它暴露了计算器工具和动态问候资源:
# server.py
from mcp.server.fastmcp import FastMCP
# 创建一个MCP服务器
mcp = FastMCP("Demo")
# 添加一个加法工具
@mcp.tool()
def add(a: int, b: int) -> int:
"""加两个数字"""
return a + b
# 添加一个动态问候资源
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""获取个性化问候"""
return f"你好,{name}!"
服务器启动示例
可以使用stdio(默认)或SSE传输启动服务器:
# 使用stdio传输(默认)
uv run mcp-simple-tool
# 使用SSE传输在自定义端口上
uv run mcp-simple-tool --transport sse --port 8800
服务器生命周期管理示例
以下示例展示了如何使用生命周期管理来初始化和清理资源:
# 添加生命周期支持,用于启动/关闭,具有强类型
from contextlib import asynccontextmanager
from collections.abc import AsyncIterator
from dataclasses import dataclass
from fake_database import Database # 替换为您实际的数据库类型
from mcp.server.fastmcp import Context, FastMCP
# 创建一个命名服务器
mcp = FastMCP("My App")
# 指定部署和开发的依赖项
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
@dataclass
class AppContext:
db: Database
@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
"""管理应用程序生命周期,具有类型安全的上下文"""
# 在启动时初始化
db = await Database.connect()
try:
yield AppContext(db=db)
finally:
# 在关闭时清理
await db.disconnect()
# 将生命周期传递给服务器
mcp = FastMCP("My App", lifespan=app_lifespan)
# 在工具中访问类型安全的生命周期上下文
@mcp.tool()
def query_db(ctx: Context) -> str:
"""使用初始化资源的工具"""
db = ctx.request_context.lifespan_context["db"]
return db.query()
资源定义示例
资源是MCP向LLM暴露数据的方式,类似于REST API中的GET端点:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My App")
@mcp.resource("config://app")
def get_config() -> str:
"""静态配置数据"""
return "应用程序配置在这里"
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""动态用户数据"""
return f"用户{user_id}的个人资料数据"
工具实现示例
工具允许LLM通过服务器执行操作,与资源不同,工具预期会执行计算并产生副作用:
import httpx
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My App")
@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
"""计算BMI,给定体重(公斤)和身高(米)"""
return weight_kg / (height_m**2)
@mcp.tool()
async def fetch_weather(city: str) -> str:
"""获取城市的当前天气"""
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.weather.com/{city}")
return response.text
网站获取工具示例
以下是一个简单的MCP服务器,它暴露了一个网站获取工具:
from mcp.server.fastmcp import FastMCP
import httpx
# 创建一个MCP服务器
mcp = FastMCP("网站获取工具")
@mcp.tool()
async def fetch_website(url: str) -> str:
"""获取指定URL的网站内容"""
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
@mcp.tool()
def extract_text(html: str) -> str:
"""从HTML中提取纯文本内容"""
# 这里可以使用BeautifulSoup或其他HTML解析库
# 简化示例
import re
text = re.sub(r'<[^>]+>', '', html)
return text
客户端示例
以下是一个简单的MCP客户端示例,它连接到MCP服务器并使用其工具:
from mcp.client import Client
import asyncio
async def main():
# 连接到MCP服务器
client = Client()
await client.connect("stdio", "path/to/server.py")
# 获取服务器信息
info = await client.get_server_info()
print(f"已连接到服务器: {info.name}")
# 列出可用工具
tools = await client.list_tools()
print(f"可用工具: {[tool.name for tool in tools]}")
# 调用工具
result = await client.call_tool("add", {"a": 5, "b": 3})
print(f"5 + 3 = {result}")
# 读取资源
greeting = await client.read_resource("greeting://世界")
print(greeting)
# 关闭连接
await client.close()
if __name__ == "__main__":
asyncio.run(main())
提示模板示例
MCP还支持提示模板,用于定义LLM交互模式:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("提示示例")
@mcp.prompt("greeting")
def greeting_prompt() -> str:
"""返回一个问候提示模板"""
return """
你好!我是一个AI助手,我可以帮助你回答问题。
今天是{date},很高兴见到你,{name}!
你有什么问题想问我吗?
"""
@mcp.prompt("summary")
def summary_prompt() -> str:
"""返回一个摘要提示模板"""
return """
请为以下文本生成一个简短的摘要:
{text}
摘要:
"""
完整应用示例
以下是一个更完整的MCP应用示例,结合了多种功能:
from mcp.server.fastmcp import FastMCP, Context
import httpx
import json
from typing import List, Dict, Any
import os
# 创建一个MCP服务器
mcp = FastMCP(
"综合示例应用",
description="一个展示MCP多种功能的综合示例",
dependencies=["httpx", "beautifulsoup4"]
)
# 添加资源
@mcp.resource("config://app")
def get_app_config() -> str:
"""获取应用配置"""
config = {
"name": "综合示例应用",
"version": "1.0.0",
"author": "MCP示例团队"
}
return json.dumps(config, ensure_ascii=False, indent=2)
@mcp.resource("data://{dataset_id}")
def get_dataset(dataset_id: str) -> str:
"""获取指定数据集"""
datasets = {
"users": [{"id": 1, "name": "张三"}, {"id": 2, "name": "李四"}],
"products": [{"id": 101, "name": "笔记本电脑"}, {"id": 102, "name": "智能手机"}]
}
if dataset_id in datasets:
return json.dumps(datasets[dataset_id], ensure_ascii=False, indent=2)
else:
return f"数据集 '{dataset_id}' 不存在"
# 添加工具
@mcp.tool()
def calculate(expression: str) -> float:
"""计算数学表达式"""
# 警告:在实际应用中应该使用更安全的方法
return eval(expression)
@mcp.tool()
async def search_web(query: str) -> str:
"""搜索网络获取信息(模拟)"""
# 这是一个模拟实现
await asyncio.sleep(1) # 模拟网络延迟
return f"关于'{query}'的搜索结果:这是一些模拟的搜索结果数据。"
@mcp.tool()
def save_note(ctx: Context, title: str, content: str) -> str:
"""保存用户笔记"""
user_id = ctx.request_context.client_info.get("user_id", "anonymous")
# 在实际应用中,这里会将笔记保存到数据库
return f"已为用户 {user_id} 保存标题为 '{title}' 的笔记"
# 添加提示模板
@mcp.prompt("assistant")
def assistant_prompt() -> str:
"""助手提示模板"""
return """
你是一个有用的AI助手,专门帮助用户解决{domain}问题。
用户信息:{user_info}
请以友好和专业的方式回应用户的请求。
"""
# 主函数
if __name__ == "__main__":
import uvicorn
import sys
# 检查是否指定了传输方式
if len(sys.argv) > 1 and sys.argv[1] == "--web":
# 作为Web服务启动
app = mcp.mount_asgi()
uvicorn.run(app, host="0.0.0.0", port=8000)
else:
# 使用stdio传输启动
mcp.run()
MCP的实际应用场景
MCP被设计用于多种场景,包括:
- 数据集成:连接LLM到企业数据源,如文档库、CRM系统或内部知识库
- 工具使用:允许LLM使用计算工具、API或服务
- 开发环境:增强IDE和开发工具,使AI助手能够理解代码库和项目上下文
- 个人助手:使个人AI助手能够访问用户的本地文件、日历和其他个人数据
- 企业系统:在保持数据安全和隐私的同时,将AI功能集成到企业系统中
最佳实践与性能优化
在使用MCP时,以下是一些最佳实践:
- 资源与工具分离:明确区分资源(提供数据)和工具(执行操作)
- 类型安全:利用Python的类型提示来确保API的一致性和可靠性
- 异步处理:对于I/O密集型操作,使用异步函数提高性能
- 错误处理:实现适当的错误处理,提供有意义的错误消息
- 文档:为所有资源和工具提供清晰的文档字符串
- 安全性:遵循安全最佳实践,特别是在处理用户数据时
- 测试:编写单元测试和集成测试,确保服务器的可靠性
总结与展望
Model Context Protocol(MCP)代表了AI应用与数据交互方式的重要进步。通过提供一个标准化的接口,MCP使开发者能够更容易地将LLM与各种数据源和工具集成,同时保持安全性和灵活性。
随着AI技术的不断发展,MCP的生态系统也在不断扩大,支持更多的语言、框架和用例。作为一个开放标准,MCP的未来发展将由社区驱动,为AI应用的构建提供更强大、更灵活的基础。
无论你是构建个人助手、企业应用还是开发工具,MCP都提供了一种强大的方式来连接AI与数据,释放大型语言模型的全部潜力。