小本本系列:MCP研究(1)

一、什么是MCP
MCP 起源于 2024 年 11 月 25 日 Anthropic 发布的文章:Introducing the Model Context Protocol。
MCP 就是以更标准的方式让 LLM Chat 使用不同工具,这样应该更容易理解“中间协议层”的概念了。Anthropic 旨在实现 LLM Tool Call 的标准。
MCP (Model Context Protocol,模型上下文协议)定义了应用程序和 AI 模型之间交换上下文信息的方式。这使得开发者能够以一致的方式将各种数据源、工具和功能连接到 AI 模型(一个中间协议层),就像 USB-C 让不同设备能够通过相同的接口连接一样。MCP 的目标是创建一个通用标准,使 AI 应用程序的开发和集成变得更加简单和统一。

二、为什么需要MCP
MCP 主解决的一个问题是:如何将 AI 模型和我们已有系统集成。
在跟 LLM 交互的时候,为了显著提升模型的能力需要提供更结构化的上下文信息,例如提供一些更具体、实时的信息(比如本地文件,数据库,一些网络实时信息等)给模型,这样模型更容易理解真实场景中的问题。想象一下没有 MCP 之前会怎么做?我们会人工从数据库中筛选或者使用工具检索可能需要的信息复制到 prompt 中,随着要解决的问题越来越复杂,人工参与这样的数据交互过程变得越来越难以实现。
许多 LLM 如 OpenAI、Google、Qwen 等引入了 function call 功能,这一机制允许模型在需要时调用预定义的函数来获取数据或执行操作显著提升了自动化水平,当前大热门 AI Agent 也具备类似的能力。那么 function calling、AI Agent、MCP 这三者之间有什么区别:
- Function Calling,Function Calling 指的是 AI 模型根据上下文自动执行函数的机制。Function Calling 充当了 AI 模型与外部系统之间的桥梁,不同的模型有不同的 Function Calling 实现,代码集成的方式也不一样。由不同的 AI 模型平台来定义和实现。如果我们使用 Function Calling,那么需要通过代码给 LLM 提供一组 functions,并且提供清晰的函数描述、函数输入和输出,那么 LLM 就可以根据清晰的结构化数据进行推理,执行函数。Function Calling 的缺点在于处理不好多轮对话和复杂需求,适合边界清晰、描述明确的任务。如果需要处理很多的任务,那么 Function Calling 的代码比较难维护。
- Model Context Protocol (MCP),MCP 是一个标准协议,如同电子设备的 Type C 协议(可以充电也可以传输数据),使 AI 模型能够与不同的 API 和数据源无缝交互。MCP 旨在替换碎片化的 Agent 代码集成,从而使 AI 系统更可靠,更有效。通过建立通用标准,服务商可以基于协议来推出它们自己服务的 AI 能力,从而支持开发者更快的构建更强大的 AI 应用。开发者也不需要重复造轮子,通过开源项目可以建立强大的 AI Agent 生态。MCP 可以在不同的应用/服务之间保持上下文,从而增强整体自主执行任务的能力。可以理解为 MCP 是将不同任务进行分层处理,每一层都提供特定的能力、描述和限制。而 MCP Client 端根据不同的任务判断,选择是否需要调用某个能力,然后通过每层的输入和输出,构建一个可以处理复杂、多步对话和统一上下文的 Agent。
- AI Agent,AI Agent 是一个智能系统,它可以自主运行以实现特定目标。传统的 AI 聊天仅提供建议或者需要手动执行任务,AI Agent 则可以分析具体情况,做出决策,并自行采取行动。AI Agent 可以利用 MCP 提供的功能描述来理解更多的上下文,并在各种平台/服务自动执行任务。
基于 AI 做编程和应用开发时,开发者都希望将数据、系统连接到模型的这个环节可以更智能、更统一。Anthropic 基于这样的痛点设计了 MCP,充当 AI 模型的「万能转接头」,让 LLM 能轻松的获取数据或者调用工具。更具体的说 MCP 的优势在于:
- 生态,MCP 提供很多现成的插件,你的 AI 可以直接使用。
- 统一性,不限制于特定的 AI 模型,任何支持 MCP 的模型都可以灵活切换。
- 数据安全,你的敏感数据留在自己的环境中,不必全部上传。
三、怎么使用MCP
在深入学习 MCP 技术原理之前,为了能够对 MCP 比较有体感,可以先了解怎么使用 MCP。Claude Desktop 是比较早支持 MCP 协议的,但是由于众所周知的原因,在国内使用 Claude 并不方便,可以参考它的教程:For Claude Desktop Users - Model Context Protocol。
关于怎么使用 MCP,我打算以 OpenCat 为例介绍一下,如何采用 OpenCat + Qwen
组合使用 MCP:
- 关于 OpenCat 下载使用可以看这里:https://opencat.app/zh-Hans/
- 关于 Qwen API 配置使用可以参考文章「在Obsidian搭建通义千问AI助手」中的搭建步骤
- OpenCat 配置 MCP

- 选择MCP工具并在prompt种触发使用MCP工具

四、MCP架构分析
MCP 遵循客户端-服务器架构(client-server),其中包含以下几个核心概念:
- MCP 主机(MCP Hosts):发起请求的 LLM 应用程序(例如 Claude Desktop、OpenCat、IDE 或 AI 工具)。
- MCP 客户端(MCP Clients):在 MCP 主机程序内部,与 MCP server 保持 1:1 的连接。
- MCP 服务器(MCP Servers):为 MCP client 提供上下文、工具和 prompt 信息。
- 本地资源(Local Data Source):本地计算机中可供 MCP server 安全访问的资源(例如文件、数据库)。
- 远程资源(Remote Service):MCP server 可以连接到的远程资源(例如通过 API)。

MCP Client
MCP client 充当 LLM 和 MCP server 之间的桥梁,MCP client 的工作流程如下:
- MCP client 首先从 MCP server 获取可用的工具列表。
- 将用户的查询连同工具描述通过 function calling 一起发送给 LLM。
- LLM 决定是否需要使用工具以及使用哪些工具。
- 如果需要使用工具,MCP client 会通过 MCP server 执行相应的工具调用。
- 工具调用的结果会被发送回 LLM。
- LLM 基于所有信息生成自然语言响应。
- 最后将响应展示给用户。
MCP Server
MCP server 是 MCP 架构中的关键组件,它可以提供 3 种主要类型的功能:
- 资源(Resources):类似文件的数据,可以被客户端读取,如 API 响应或文件内容。
- 工具(Tools):可以被 LLM 调用的函数(需要用户批准)。
- 提示(Prompts):预先编写的模板,帮助用户完成特定任务。
这些功能使 MCP server 能够为 AI 应用提供丰富的上下文信息和操作能力,从而增强 LLM 的实用性和灵活性。
五、MCP原理
LLM 调用 MCP 的过程可以分为两个步骤:
- 由 LLM(Qwen)确定使用哪些 MCP Server
- 执行对应的 MCP Server 并对执行结果进行重新处理

为了更加深入理解 MCP 原理,我打算从 MCP 协议的源码进行分析:
LLM 如何确定使用哪些工具
根据 MCP 官方提供的 client example 进行源码分析,其中删除了一些不影响阅读逻辑的异常控制代码,通过阅读代码,可以发现模型是通过 prompt 来确定当前有哪些工具,通过将工具的具体使用描述以文本的形式传递给模型,供模型了解有哪些工具以及结合实时情况进行选择,具体情况可以参考代码中的注释:
###################
# 此处省略无关的代码 #
###################
async def start(self):
# 1.初始化所有的 mcp server
for server in self.servers:
await server.initialize()
# 2.获取所有的 tools 命名为 all_tools
all_tools = []
for server in self.servers:
tools = await server.list_tools()
all_tools.extend(tools)
# 3.将所有的 tools 的功能描述格式化成字符串供 LLM 使用,tool.format_for_llm() 我放到了这段代码最后,方便阅读。
tools_description = "\n".join(
[tool.format_for_llm() for tool in all_tools]
)
# 4.基于 prompt 和当前所有工具的信息询问 LLM(Qwen)应该使用哪些工具。
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"
"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."
)
messages = [{"role": "system", "content": system_message}]
while True:
# 5.将用户输入消息进行聚合
messages.append({"role": "user", "content": user_input})
# 6.将 system_message 和用户消息输入一起发送给 LLM
llm_response = self.llm_client.get_response(messages)
# ...
class Tool:
"""Represents a tool with its properties and formatting."""
def __init__(
self, name: str, description: str, input_schema: dict[str, Any]
) -> None:
self.name: str = name
self.description: str = description
self.input_schema: dict[str, Any] = input_schema
# 把工具的名字 / 工具的用途(description)和工具所需要的参数(args_desc)转化为文本
def format_for_llm(self) -> str:
"""Format tool information for LLM.
Returns:
A formatted string describing the tool.
"""
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)}
还有一个地方需要分析清楚即 Tool 的描述和代码中的 input_schema
是怎么获取的?这个问题需要看一下 MCP 协议的实现(这里我分析的是 Python SDK 源代码),MCP Python SDK 使用装饰器 @mcp.tool()
来装饰函数时,对应的 name
和 description
等其实直接源自用户定义函数的函数名以及函数的 docstring 等,源代码如下:
@classmethod
def from_function(
cls,
fn: Callable,
name: str | None = None,
description: str | None = None,
context_kwarg: str | None = None,
) -> "Tool":
"""Create a Tool from a function."""
func_name = name or fn.__name__ # 获取函数名
if func_name == "<lambda>":
raise ValueError("You must provide a name for lambda functions")
func_doc = description or fn.__doc__ or "" # 获取函数 docstring
is_async = inspect.iscoroutinefunction(fn)
... # 更多请参考原始代码...
可以看出 LLM 使用工具是通过 prompt engineering,即提供所有工具的结构化描述和 few-shot 的 example 来确定该使用哪些工具。另一方面,大模型厂商(如Qwen)肯定做了专门的训练,确保大模型更能理解工具的 prompt 以及输出结构化的 tool call json 代码。
工具执行与结果反馈机制
工具的执行就比较简单和直接,把 system prompt(指令与工具调用描述)和用户消息一起发送给模型,然后接收模型的回复。当模型分析用户请求后,它会决定是否需要调用工具:
- 无需工具时:模型直接生成自然语言回复。
- 需要工具时:模型输出结构化 JSON 格式的工具调用请求。
如果回复中包含结构化 JSON 格式的工具调用请求,则客户端会根据这个 json 代码执行对应的工具。具体的实现逻辑都在 {python} process_llm_response
中,代码,逻辑非常简单。
如果模型执行了 tool call,则工具执行的结果 result 会和 system prompt 和用户消息一起重新发送给模型,请求模型生成最终回复。
如果 tool call 的 json 代码存在问题或者模型产生了幻觉怎么办呢?通过阅读代码发现,我们会 skip 掉无效的调用请求。
执行相关的代码与注释如下:
async def start(self):
# 省略无关的代码
while True:
# 假设这里已经处理了用户消息输入.
messages.append({"role": "user", "content": user_input})
# 获取 LLM 的输出
llm_response = self.llm_client.get_response(messages)
# 处理 LLM 的输出(如果有 tool call 则执行对应的工具)
result = await self.process_llm_response(llm_response)
# 如果 result 与 llm_response 不同,说明执行了 tool call (有额外信息了)
# 则将 tool call 的结果重新发送给 LLM 进行处理。
if result != llm_response:
messages.append({"role": "assistant", "content": llm_response})
messages.append({"role": "system", "content": result})
final_response = self.llm_client.get_response(messages)
logging.info("\nFinal response: %s", final_response)
messages.append(
{"role": "assistant", "content": final_response}
)
# 否则代表没有执行 tool call,则直接将 LLM 的输出返回给用户。
else:
messages.append({"role": "assistant", "content": llm_response})
结合这部分原理分析:
- 工具文档至关重要 - 模型通过工具描述文本来理解和选择工具,因此精心编写工具的名称、docstring 和参数说明至关重要。
- 由于 MCP 的选择是基于 prompt 的,所以任何模型其实都适配 MCP,只要你能提供对应的工具描述。但是当你使用非 Claude 模型时,MCP 使用的效果和体验难以保证(没有做专门的训练)。
六、MCP开发实践
我开源了一个 MCP Repo,以 OpenCat 为 MCP Host,支持 Weather 数据查询的 MCP Server 和 MCP Client([email protected]/edonyzpc/MCP.git)。
1. MCP Server
MCP Server 的开发可以参考 Server 章节:MCP/servers/weather-server-mcp


2. MCP Client
MCP Client 的开发可以参考 Client 章节:MCP/clients/mcp-client

七、扩展研究
MCP的初学者还缺少两部分:1. MCP的来龙去脉;2.MCP协议的观测和调试。篇幅限制,留待下次分解,Stay Tuned
References
- Introduction - Model Context Protocol
- 🚀一秒看懂MCP
- What is Model Context Protocol? Why it matters? – Purnananda Behera
- Crypto AI的下一个催化剂?详解「AI届新宠儿」MCP协议
- 一文看懂:MCP(大模型上下文协议)
- MCP (Model Context Protocol),一篇就够了
- Model Context Protocol (MCP) - Anthropic
- MCP 终极指南
- 🔗What is Model Context Protocol? (MCP) Architecture Overview
Public discussion