0 引言
刚接触MCP协议时,作者感觉听抽象的,难以理解,所以想着记录一篇MCP协议的简单理解版本,简单代码+文字的方式梳理一遍整个协议,希望给刚刚学习MCP协议的读者们一个快速并且易懂的MCP协议解析版本,快速入门,如果存在理解不对的地方,随时可以找作者讨论修改!
1 定义
模型上下文协议(Model Context Protocol)是一种标准化的大模型工具管理框架,用于统一工具的定义、注册、发现、调用等功能;
2 协议架构
协议主要存在三种架构:Host / Client / Server, 接下来分别介绍**:**
2.1 Host
MCP Host可以看作是用户可以直接交互程序应用,比如各种AI助手等;
Host的主要功能:
1)基础功能:接收用户的prompt以及调用大模型对prompt进行响应;
2)与MCP相关功能:根据大模型指令通过MCP client调用工具;
2.2 Client
MCP Client是Host内部的通信组件的同时又负责和Server交互;
Client的主要功能:
1)可以通过Host调用Client与Server交互;
2)可以和Server建立连接,发送请求以及接收响应等;
2.3 Server
MCP Server主要作用是存储具体的调用工具的具体信息,以及工具的具体执行,随后返回结果给Client;
2.4 Host / Client / Server 三者联系

Host可以连接多个Client ,但是每一个Client都只负责连接一个Server;
3 核心能力
MCP的架构介绍完毕,接下来介绍MCP的三大核心功能:Tools/Resouces/Prompts;
3.1 Tools--工具调用
我们回忆一下,如果使用普通的生成式大模型,它的特点是只能使用语言对你的问题进行回答,而不能进行调用接口来执行某些具体操作 ,例如帮我获取用户"小林学编程"的CSDN简介,这个问题就不再是一个可直接回答的问题,是需要具体访问CSDN的网站,来获取相关信息,所以为了给大模型补足这一缺点,诞生了Function Call,Function Call的本质就是模型会根据你的问题,让大模型回答应该调用某些工具来执行。
ps:具体的执行在你的应用程序,大模型只是返回你需要执行的工具以及相关输入参数;
给一个Function Call的例子:
Host 与大模型第一轮交互
1)Host拼接prompt = "获取用户"小林学编程"的CSDN简介内容" + 所定义工具列表(包含每个工具有名字、描述、参数定义)
2)大模型根据输入分析出需要使用访问网页相关工具执行,大模型的输出 = 访问网页的工具名称以及相关需要输入的参数;(例如:分析出需要使用访问网页的工具,输入参数是(网页URL,用户名))
Host 与大模型第二轮交互
Host 需要拦截第一次大模型输出,并解析需要调用的工具是什么,输入参数是什么,然后再本地寻找相关工具,执行并返回执行结果;
1)Host输入prompt = "获取用户"小林学编程"的CSDN简介内容" + 所定义工具列表(包含每个工具有名字、描述、参数定义) + "简介内容是XXXX"(也就是调用工具后的输出结果);
2)大模型得到相关内容后返回最终答案:"小林学编程的简介内容是XXXXXXXXX";
Function Call就是定义了大模型可以通过意图识别以及输入的工具列表,输出相关工具的调用逻辑,让Host执行这个工具后,再次与大模型交互,最终输出给用户答案;
Function Call内容理解后,就可以描述MCP的tools功能了!!
MCP的tools能力主要是在Function Call 之上,提供一层标准化的管理框架;
也就是说Function Call所需要定义的工具从Host端,被转移到了MCP Server进行存储,实现了跨语言系统的互通; 给一个例子,例如你的一些工具使用Java语言定义,如果你的应用程序Host使用python代码写的,那么Java定义的工具就没办法使用!!
加上MCP协议后就相当于封装了一层统一访问协议,如果存在这样的结构:使用Java写的Host存在两个Client1/Client2,分别连接Server1/Server2,Server1/Server2分别存储由Java以及Python写的工具,那么Host可以及访问JAVA以及Python写的工具,而不单单只能访问Java写的工具了!!!!
PS:Client初始化时自动从相关Server获取工具列表,然后host端就可以获取所有工具,并拼接进第一次与大模型交互的prompt中;
3.2 Resouces--资源
Resouces是MCP提供的第二种能力,和tools一样也是存储于Server中,但是作用是让Server提前暴露数据给大模型;
我的理解就是提前提供一点基础信息给host端,减少对tools的依赖,也就是说节省一部分大模型调用的损耗;
举一个例子:当用户需要开启对话时,host端其实可以获取到用户ID,然后可以通过Client向Server请求围绕着用户ID的定义的Resouces,例如
java
public void BaseToChat(String userId) {
String level = mcpClient.readResource("customer://users/" + userId + "/level");
String sex = mcpClient.readResource("customer://users/" + userId + "/gender");
String context = level + "\n" + gender;
LLM(systemPrompt, context, userMessage);
}
我们通过Client获取Server中的定义的Resouces,通过输入Server需要执行的URL+用户ID拼接,Server会访问这些URL(最重要的一点Server已经定义了相关Resouces了,才能匹配),并执行相关代码,来提前获取这个用户ID下的会员等级以及性别;
随后通过组装prompt输入给大模型,这样的话,如果存在这些相关问题的提问(我的会员等级是多少?),就省去了大模型返回调用工具的一轮了,直接输出结果就好;
直观一点看就是,当用户提问我的会员等级是多少?:
1)使用Resouces;
提前获取Server资源相关信息后,prompt = "我的会员等级是多少 + 会员等级:XX;性别:XX"--大模型根据提示词可以直接返回结果:您的会员等级为XX;
2)使用tools:
prompt = "我的会员等级是多少" + 工具列表--大模型判断需要调用会员等级工具--prompt = "我的会员等级是多少" + 工具列表 + 调用工具输出的结果"会员等级:XX' --大模型根据提示词可以直接返回结果:您的会员等级为XX;
很明显看出省略了访问大模型的一步!!!减少了额外的推理以及调用延迟;
3.3 Prompt--提示词模板
与前两者相似,prompt也是存储于Server中,可以使得多client共用一份prompt,一般使用场景为作为一开始的System message的封装 或者文档摘要生成等模板化prompt;例如作为prompt封装的消息:
java
String userMessage = String.format("""
你是一个企业知识库助手。请严格遵守以下规则:
【回答规则】
1. 只基于「参考资料」中的内容回答,不要使用你自己的知识。
2. 如果参考资料中没有相关信息,请回答:抱歉,我在知识库中没有找到相关信息,建议您联系人工客服获取帮助。
3. 不要编造、推测或补充参考资料中没有的细节。
【引用规则】
1. 回答中引用参考资料时,使用 [编号] 标注来源,例如 [1]、[2]。
2. 只引用你实际使用的片段,不要空挂引用。
3. 如果多个片段支持同一个观点,可以同时引用,例如 [1][2]。
【格式要求】
1. 先给结论,再给详细解释。
2. 使用简洁的中文回答。
3. 如果信息有冲突,以更新时间较近的片段为准。
参考资料:
%s
问题:%s
""", context, question);
代码中存在的**%s** 就是host借助client向server发送prompt的请求时需要携带的输入参数;
4 结论
1)MCP协议的三层架构为Host/Client/Server,分别负责与大模型交互/与Server交互/执行命令后返回结果;
2)MCP协议的三种核心功能Tools/Resouces/Prompts,Tools与Resouces之间的区别关键,是否是只读操作(例如通过用户ID读取会员等级--就是一个只读操作),若是只读操作可以封装成Resouces进行使用,减少与大模型的交互次数;若还需要一些写操作(如用户想下订单,需要修改数据库等数据),就封装为tools进行使用;
3)Tools/Resouces/Prompts的封装都位于Server中,等待Client的调用 ,体现了MCP的统一调度能力,即不同语言编写的Host,也可以复用其他语言编写的Tools/Resouces/Prompts;