MCP (Model Context Protocol) 服务技术详解

1. MCP 技术概述

MCP 是 Anthropic 推出的模型上下文协议,它定义了大语言模型(LLM)与外部工具、数据源之间的标准化通信方式。MCP 采用客户端-服务器架构,支持两种主要传输机制。

MCP 的核心优势体现在三个方面:标准化接口让工具开发一次即可对接多种模型,双向通信支持实时数据交换,而传输灵活性则允许根据场景选择本地或远程部署方式。

2. 服务器端实现详解

2.1 FastMCP 框架

服务器端使用 FastMCP 框架快速构建 MCP 服务。代码通过 @mcp.tool() 装饰器将普通函数转换为 MCP 工具。

from mcp.server.fastmcp import FastMCP
import requests

mcp = FastMCP()

FastMCP 是 MCP 官方提供的高级框架,它自动处理协议解析、工具注册、请求路由等底层细节。开发者只需关注业务逻辑的实现。

2.2 工具定义

每个工具都是一个带有 @mcp.tool() 装饰器的 Python 函数。以下是一个天气查询工具的示例:

@mcp.tool()
def get_weather(city: str) -> str:
    """查询某个城市或区县的天气"""
    api_key = "your_api_key_here"
    url = f"https://api.example.com/weather?city={city}&key={api_key}"
    response = requests.get(url)
    return response.json()["forecasts"]

安全提示:生产环境中,API 密钥应通过环境变量或密钥管理服务获取,切勿硬编码在源代码中:

import os
api_key = os.environ.get("WEATHER_API_KEY")

工具函数需要包含类型注解docstring,MCP 会自动提取这些信息生成函数签名描述,供大模型理解工具的用途和参数规范。

2.3 传输方式配置

MCP 服务器支持两种传输协议:

stdio 模式(本地进程通信):

if __name__ == "__main__":
    mcp.run(transport='stdio')  # 本地stdio调用

stdio 方式通过标准输入输出进行进程间通信,适用于本地工具集成,延迟最低,不需要网络配置。

SSE 模式(远程 HTTP 通信):

if __name__ == "__main__":
    mcp.settings.port = 8001  # 可自定义端口
    mcp.run(transport='sse')

SSE(Server-Sent Events)方式通过 HTTP 长连接实现双向通信,适合远程服务部署。服务器在指定端口监听 SSE 连接,客户端通过 URL 访问。

3. 客户端实现详解

3.1 客户端架构

客户端 MCPClient 类管理所有服务器会话和连接资源:

class MCPClient:
    def __init__(self):
        self.sessions = []       # 存储服务器的会话及其上下文
        self.model_name = "deepseek-chat"
        self.server_url = "https://your-mcp-server.example.com/sse"
        self.client = AsyncOpenAI(
            base_url="https://api.example-llm.com",
            api_key=os.environ.get("LLM_API_KEY")
        )

安全提示:API 密钥和服务器地址等敏感信息应通过环境变量或配置文件注入,避免硬编码。客户端维护三个层级的上下文对象:SSE 流上下文ClientSession 会话上下文、以及会话实例本身,形成嵌套的异步上下文管理器结构。

3.2 会话初始化

async def init_session(self):
    streams_context = sse_client(url=self.server_url)
    streams = await streams_context.__aenter__()
    session_context = ClientSession(*streams)
    session = await session_context.__aenter__()
    await session.initialize()
    
    self.sessions = [session, session_context, streams_context]
    
    response = await session.list_tools()
    for tool in response.tools:
        print(tool)

初始化流程分为四个步骤:首先是创建 SSE 客户端并建立 HTTP 长连接,然后初始化 ClientSession 进行协议握手,接着发送 initialize 消息完成版本协商,最后调用 list_tools 获取可用工具列表。

3.3 工具发现与映射

客户端从服务器获取工具列表后,需要将其转换为符合 OpenAI Function Calling 规范的格式:

for tool in response.tools:
    function = {
        "type": "function",
        "function": {
            "name": tool.name,
            "description": tool.description,
            "parameters": tool.inputSchema,
        },
    }
    available_tools.append(function)

这种转换是 MCP 的关键设计:MCP 工具描述格式OpenAI Function Calling 格式之间的映射,使客户端能够将 MCP 工具无缝对接任意支持 Function Calling 的大模型。

3.4 工具调用流程

工具调用遵循"两阶段"模式:

async def process_query(self, query: str):
    messages = [{"role": "user", "content": query}]
    
    # 第一阶段:模型决定调用哪些工具
    response = await self.client.chat.completions.create(
        model=self.model_name,
        messages=messages,
        tools=available_tools,
    )
    
    message = response.choices[0].message
    
    # 第二阶段:执行工具调用
    if message.tool_calls:
        for tool_call in message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            result = await session.call_tool(function_name, function_args)
            results.append(result.content)

第一阶段中,大模型分析用户意图并决定调用哪些工具及参数。第二阶段中,客户端通过 MCP 协议向服务器发送 call_tool 请求,执行实际的功能调用。

3.5 结果整合与流式输出

工具执行结果需要再次发送给大模型进行总结:

response = await self.client.chat.completions.create(
    model=self.model_name,
    messages=messages,
    tools=available_tools,
    stream=True
)
async for chunk in response:
    text = chunk.choices[0].delta.content
    if text:
        yield text

客户端使用流式响应方式,将大模型的最终回答逐步输出,提供更好的用户体验。

4. 传输协议对比

特性 stdio SSE
通信方式 标准输入输出 HTTP 长连接
适用场景 本地进程集成 远程服务部署
延迟 极低 较低
配置复杂度 简单 需要网络配置
资源占用 较高

对于本地工具调用,推荐使用 stdio 模式。对于需要跨网络访问的工具服务,则应使用 SSE 模式部署 MCP 服务器。

5. 核心设计模式

MCP 的核心设计遵循几个重要原则:职责分离让服务器专注工具实现,客户端处理模型交互;协议适配通过格式转换桥接不同模型提供商;异步优先使用 asyncio 实现高并发处理;上下文管理确保连接资源的正确释放。

这套架构使得工具开发者无需关心大模型的细节,只需实现标准的 MCP 工具函数,即可被任何支持 MCP 客户端的应用使用。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐