MCP框架

什么是MCP

MCP(模型上下文协议)是一种开放的技术协议,旨在标准化大型语言模型(LLM)与外部工具和服务的交互方式。你可以把MCP理解成像是一个AI世界的通用翻译官,让AI模型能够与各种各样的外部工具”对话”。

为什么需要MCP

接口标准化缺失——不同大语言模型采用差异化的指令体系,各类工具API也具备独特的数据结构,迫使开发者必须为每个模型与工具的对接场景编写定制化桥接代码

而MCP则采用了一种通用语言格式(JSON - RPC),一次学习就能与所有支持这种协议的工具进行交流。一个通用翻译器,不管什么LLM,用上它就能使用工具 / 数据了。

1746154848456

MCP的工作原理

MCP主要由三个核心部分组成的系统:MCP Host、MCP Client和MCP Server。
1746154848456

MCP Host 是一个 AI 应用,它在运行 MCP 客户端的同时,为执行基于 AI 的任务提供环境。它集成交互工具和数据,以实现与外部服务的顺畅通信。比如: Claude Desktop 用于 AI 辅助内容创建,Cursor 是一个 AI 驱动的 IDE,用于代码补全和软件开发,以及执行复杂任务的 AI Agent。MCP 主机承载 MCP 客户端,并确保与外部 MCP 服务器通信。

MCP Client 在主机环境中充当中介,管理 MCP 主机与一个或多个 MCP 服务器之间的通信。它向 MCP 服务器发起请求,查询可用功能,并获取描述服务器能力的响应。

**MCP Server (服务终端) 就像是各个专业部门或外部服务提供商,每一个都负责特定类型的服务。有的提供数据分析(如财务部),有的提供信息检索(如资料室),还有的提供内容生成(如市场部)。在MCP架构中,每个Server提供特定类型的功能:工具、资源或提示。在MCP出现之前,开发者则需要为每个工具单独编写连接代码,效率低下且难以维护。在MCP之后,秘书只需使用一个统一的通信平台,就能以相同的方式联系所有部门和服务提供商。开发者也只需实现一次MCP接口,就能让AI系统与所有支持该协议的工具进行交互。

MCP Server与Function Calling区别

回顾没有 MCP 的时代,为了解决复杂问题,我们不得不手动从数据库中筛选信息或使用工具来检索相关信息,并将其逐一添加到 prompt 中。处理简单问题时如需要大模型做归纳总结这种方法很奏效,但随着问题复杂度的增加,这种方法变得越来越难以应对。

为了克服这些挑战,许多大型语言模型(LLM)平台(例如 OpenAI 和 Google)引入了 function call 功能。这一机制允许模型根据需要调用预定义函数以获取数据或执行特定操作,大大提高了自动化程度。然而,function call 也有其局限性,包括对平台的高度依赖以及不同 LLM 平台间 API 实现的差异,这使得开发者在切换模型时必须重写代码,增加了开发成本。此外,还存在安全性和交互性等方面的挑战。

模型如何选择工具

1746154848456

MCP官方提供的client example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 省略了无关的代码
async def start(self):
   """启动服务的主流程"""
   # 1. 初始化所有的 MCP 服务器
   for server in self.servers:
       await server.initialize()  

   # 2. 收集所有可用的工具
   # 从所有服务器获取可用工具列表,合并到统一列表
   all_tools = []
   for server in self.servers:
       tools = await server.list_tools()  # 异步获取工具列表
       all_tools.extend(tools)  # 合并多个服务器的工具

   # 3. 格式化工具描述供LLM使用
   # 将每个工具转换为LLM可理解的格式,生成完整的工具描述字符串
   # 每个工具的描述包含名称、功能说明和参数信息
   tools_description = "\n".join(
      [tool.format_for_llm() for tool in all_tools]  # 调用每个工具的格式化方法
  )
   # 4. 构建系统提示消息
   # ----------------------------
   # 包含以下关键信息:
   # - 可用工具列表
   # - JSON响应格式要求
   # - 后续数据处理规范
   system_message = (
       "You are a helpful assistant with access to these tools:\n\n"
       f"{tools_description}\n"
       "Choose the appropriate tool based on the user's question. "
       "If no tool is needed, reply directly.\n\n"
       # JSON 格式要求说明
       "IMPORTANT: When you need to use a tool, you must ONLY respond with "
       "the exact JSON object format below, nothing else:\n"
       "{\n"
       '   "tool": "tool-name",\n'
       '   "arguments": {\n'
       '       "argument-name": "value"\n'
       "   }\n"
       "}\n\n"
       "After receiving a tool's response:\n"
       "1. Transform the raw data into a natural, conversational response\n"
       "2. Keep responses concise but informative\n"
       "3. Focus on the most relevant information\n"
       "4. Use appropriate context from the user's question\n"
       "5. Avoid simply repeating the raw data\n\n"
       
       "Please use only the tools that are explicitly defined above."
  )

   # 5. 初始化消息历史
   messages = [{"role": "system", "content": system _message}]

   # 6. 主处理循环
   while True:
       # 假设这里已经处理了用户消息输入
       # ----------------------------
       messages.append({"role": "user", "content": user_input})
       # 7. 获取LLM响应
       # ----------------------------
       # 将系统消息和用户消息一起发送给LLM
       # 注意:实际实现中需要考虑token长度限制
       llm_response = self.llm_client.get_response(messages)
   
   # ... 后面和确定使用哪些工具无关的代码

class Tool:
   """表示一个可用的工具及其元数据
   
  属性:
      name: 工具名称
      description: 工具功能描述
      input_schema: 输入参数模式(JSON Schema格式)
  """

   def __init__(
       self,
       name: str,
       description: str,
       input_schema: dict[str, Any]
  ) -> None:
       """初始化工具实例
       
      参数:
          name: 工具唯一标识名称
          description: 自然语言描述工具功能
          input_schema: 参数规范(JSON Schema格式)
      """
       self.name: str = name
       self.description: str = description
       self.input_schema: dict[str, Any] = input_schema

   def format_for_llm(self) -> str:
       """将工具信息格式化为LLM可理解的文本
      返回格式示例:
          Tool: weather_lookup
          Description: 查询指定城市的天气信息
          Arguments:
              - city: 城市名称 (required)
              - date: 查询日期(格式:YYYY-MM-DD)
       
      返回:
          格式化后的字符串,包含工具名称、描述和参数信息
      """
       args_desc = []
       # 解析参数模式中的属性
       if "properties" in self.input_schema:
           for param_name, param_info in self.input_schema["properties"].items():
               # 构建参数描述行
               arg_desc = (
                   f"- {param_name}: {param_info.get('description', 'No description')}"
              )
               
               # 标记必填参数
               if param_name in self.input_schema.get("required", []):
                   arg_desc += " (required)"
               
               args_desc.append(arg_desc)
       return f"""
Tool: {self.name}
Description: {self.description}
Arguments:
{chr(10).join(args_desc)}
"""

通过LLM构建MCP

https://modelcontextprotocol.io/tutorials/building-mcp-with-llms

cline使用mcp

https://www.bilibili.com/video/BV1AnQNYxEsy?t=0.4

MCP官方网站

https://mcp.so/

来源部分来自:阿里云开发者