点击上方👆蓝字关注我们!
前置要求
本文面向大模型的小白,但仍需要有以下前置要求,否则会比较难以理解某些概念:
- 与大模型进行过简单交互(无论是聊天还是工作);
- 知道与大模型交互的时候,你对它的提示词是非常关键的,能决定大模型的回答。
本文内容
网络上对 MCP 的描述极为夸张,仿佛它是无所不能的神器,使用 MCP 就能让大模型具备一切能力。本文将由浅入深,主要对以下问题进行解释:
- What:
- 大模型的能力边界是什么?
- 在 MCP 之前,大模型应用是什么样的?
- MCP 解决了什么问题?
- How:
- MCP 是怎么使用的?
- MCP 是怎么做到的?
-
Why:为什么要使用 MCP?
-
Where:它来自哪儿,去向哪儿?
-
Who:谁创造的它?
What
- 大模型的能力边界是什么?
- 在 MCP 之前,大模型应用是什么样的?
- MCP 解决了什么问题?
网上的介绍
模型上下文协议(Model Context Protocol,简称 MCP) 是一种全新的开放协议,专门用于标准化地为大语言模型(LLMs)提供应用场景和数据背景。
你可以把 MCP 想象成 AI 领域的“USB-C 接口”,它能让不同的 AI 模型与外部工具和数据源轻松连接。
本文将清晰地解释 MCP 的价值、工作原理,以及它与传统 API 的关键区别。什么是模型上下文协议(MCP)?它如何比传统 API 更简单地集成 AI:什么是模型上下文协议(MCP)?它如何比传统API更简单地集成AI?
看不懂这一段可以跳过,接下来会详细解释。
MCP 是干啥的
小作业:问问 deepseek:今天几点了?
大模型的能力
我们都知道,大模型能做很多事:
- 最基础的是给你解答问题、做 oncall 机器人。
- 现在大模型已经可以联网搜索并给你最新的答案了。
- 大家用过的 Trae 还可以帮大家写代码,只需要说句话,就能帮你把代码写好,同时还能应用到文件中,不需要你自己复制粘贴。
- 前一阵子很火的 Manus,可以帮大家搭建网站,可以当场写代码 debug、运行代码爬取网上信息。
对,上述都和 MCP 无关,因为它们火的时候,MCP 还没火。
但是我们要思考一下,为什么大模型可以做到上面那些?大模型如何联网的?
模型本质上只是一个输入输出流,你给他输入,他给你输出。我们接触最多的就是文字输入输出了。其他也有多模态的输入输出,比如图像、语音、视频。
所以最原始的大模型是不知道周围环境的,也无法知道最新发生的新闻。比如问问 DeepSeek:现在几点了?他很可能无法给出正确的回答。
那么为了让大模型感知周围的环境,我们需要在提问时,给大模型一些充分的数据。比如下面这样的 case:
看起来很傻,我都知道答案了,还用问大模型吗?
那如果前半句话是程序员帮你加上去的,你只需要问后半句呢?对于不知情的黑盒用户来说,是不是感觉大模型具备了实时获取最新时间的能力了。
至此,我们对大模型的能力边界有了个简单的认识:大模型是个无状态的大模型,是个无状态的参数集合体。他所有的输出,都来自于自身训练的信息(老数据),以及输入时提供的信息(临时数据)。
为了让大模型能感知到环境,我们需要在临时信息中加入足够多的环境信息,不管这个环境信息是由程序员在黑盒里加上的,还是由用户自己加上的。总之,大模型不是神仙,没给到的信息,他就是不知道。
让大模型自己探索环境
上文用一个看起来很傻的例子说明了:必须让大模型知道环境信息,才能根据环境信息得出真实答案。
那么,我们是否可以让大模型自己探索环境。比如我们编写一个 Python 函数,用于获取本地时间:
async def local\_time() -> str:
"""Get the local time of the machine."""
import datetime
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
再将大模型包装一层,将信息流改进一下:
在改进后的工作流中,工程包装帮用户完善了环境信息,甚至在用户不知情的情况下,与大模型多进行了一次调用。其中红色块,是工程包装层调用了 local\_time
函数,并将输出拼贴为输入。上述均为简化版。
经过上述的改造,大模型看起来似乎已经有了自己探索环境的能力了。当然没有,这句话应该说:大模型应用有了自己探索环境的能力了。大模型与大模型应用是有区别的,大模型就是指算法模型,大模型应用才是用户端接触到的东西。
拓展一下
那么大模型应用的联网是如何实现的?类似的,也是由上述过程实现的,由一个工程包装层,将用户请求包装起来,有一个联网的函数,允许大模型进行调用。对于复杂的函数,由大模型生成结构化的入参。
上述就是大模型的 function\_call
能力,大模型会根据上下文给出调用外界函数的入参。对于那些复杂的大模型应用,无非是它们工程嵌入的外界函数比较丰富,能做更多的事。
这与 MCP 什么关系
MCP 全程 Model Context Protocol,模型上下文协议,其实就是提供了一个通用的工程包装方案。
比如上文中的这个部分,不同大模型应用的 prompt 不同:
系统设定:你有一个函数local\_time可以调用,该函数可以获取当前实时的时间。用户提问:现在几点了
有些可能长这样:
系统设定:你有一个函数local\_time可以调用,该函数可以获取当前实时的时间,该函数没有入参,出参是字符串。用户提问:现在几点了
或者这样:
系统设定:你有一个以下函数可以调用:
- local\_time该函数可以获取当前实时的时间,该函数没有入参,出参是字符串。
- xxxxx
用户提问:现在几点了
这些包装的 prompt 都不一样,如果涉及更多的外界函数,比如预定火车票等复杂函数,入参格式必须非常精准。各家的大模型应用都有各家自己的工程包装。
在 MCP 之前与之后
而 MCP 就是这样的一个协议,它提出一个比较通用的方案,将工程包装这个过程规范化,透明化。大家不用关注怎么包装 prompt,只需要关注大模型自身的能力和外界的函数。
在 MCP 推出之前,各厂商处于各自为政的状态,分别对自身的大模型应用进行优化,例如 Trae 和 Manus。这些应用在未运用 MCP 的情况下,已完成对外界函数的整合。不过,它们均是独立实现外部接口的接入,若要扩展功能,就必须重新实现新的接口接入。这种方式极为复杂,缺乏高效性与简洁性。
在 MCP 推出之后,由于各厂商均遵循 MCP 协议,大模型应用厂商仅需依照 MCP 的调用方式,即可使用所有符合该协议的接口。此外,鉴于 MCP 协议具有开放性,会不断有人开发新的外部接口,从而使大模型应用的功能扩展变得极为便捷,如同使用插件一般。此时再回顾本章开头的内容,是否能够理解“MCP 协议是 AI 领域的‘USB - C 接口’”这一表述了呢?
How
- MCP 是怎么使用的?
- MCP 是怎么做到的?
MCP 是怎么使用的
至此我们已经对 MCP 的应用场景有了一个大概了解。那么 MCP 如何使用呢?
MCP 只是一个协议,如 TCP/IP 一样,无法直接使用,必须先实现它。本章主要介绍一些 MCP 的概念。
MCP 主要由三个部分组成:
- MCP host
- MCP client
- MCP server
下图展示了它们的位置,调用方是 host,host 使用 client 对 server 进行调用。
MCP server 是 MCP 架构中的关键组件,它可以提供 3 种主要类型的功能:
- 资源(Resources):类似文件的数据,可以被客户端读取,如 API 响应或文件内容。
- 工具(Tools):可以被 LLM 调用的函数(需要用户批准)。
- 提示(Prompts):预先编写的模板,帮助用户完成特定任务。
其中的工具功能,是我们的重点。
SDK
MCP 是依赖实现再使用的,官方提供了多种语言的 SDK,帮助开发者快速实现 server层,而普通的用户不需要开发 server 层,不需要关注这些。
从实战中了解 MCP
新手通常使用 Claude Desktop 进行配置,网络上相关教程众多,在此不逐一列举。以下提供一个官方教程链接:https://modelcontextprotocol.io/quickstart/user
我所使用的是一个开源库,链接为 https://github.com/mark3labs/mcphost。
该库为 MCP host,能够调用大模型和 MCP server,以完成用户的部分指令。具体使用方法可查看介绍文档,在此仅作简要介绍。
安装必要环境
参考:https://github.com/mark3labs/mcphost
写一个 MCP server
我们写一个查询 12306 车票的 MCP server。代码如下:
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
from typing import List # 导入 List
from mcp\_agent.agents.agent import Agent
# Initialize FastMCP server
mcp = FastMCP("12306")
@mcp.tool()
async def get\_station\_code\_mapping(station\_name: str) -> str:
"""
根据输入的地名获取 12306 的车站代码。
:param station\_name: 车站地名
:return: 对应的车站代码,如果未找到则返回空字符串
"""
# hardcode
code = {
"北京": "BJP",
"上海": "SHH",
"上海虹桥": "SHH",
"广州": "GZY",
"苏州": "SZH",
}
return code
def optimize\_train\_ticket\_info(response):
"""
优化火车票查询结果,输出简洁明了的信息
:param response: 火车票查询的 HTTP 返回数据
:return: 优化后的火车票信息列表
"""
if response.get("status") and response.get("httpstatus") == 200:
data = response["data"]
result = data["result"]
station\_map = data["map"]
optimized\_info = []
for train in result:
parts = train.split("|")
train\_number = parts[3]
start\_station\_code = parts[6]
end\_station\_code = parts[7]
from\_station\_code = parts[6]
to\_station\_code = parts[7]
from\_station = station\_map.get(from\_station\_code, from\_station\_code)
to\_station = station\_map.get(to\_station\_code, to\_station\_code)
departure\_time = parts[8]
arrival\_time = parts[9]
duration = parts[10]
seat\_info = {
"二等座": parts[30],
"一等座": parts[31],
"商务座": parts[32]
}
info = {
"车次": train\_number,
"出发站": from\_station,
"到达站": to\_station,
"出发时间": departure\_time,
"到达时间": arrival\_time,
"历时": duration,
"座位信息": seat\_info
}
optimized\_info.append(info)
return optimized\_info
return []
# 在 query\_train\_tickets 函数中调用优化函数
@mcp.tool()
async def query\_train\_tickets(train\_date: str, from\_station: str, to\_station: str, purpose\_codes: str) -> Any:
"""
查询指定日期、出发站、到达站和乘客类型的火车票信息。
:param train\_date: 乘车日期,格式为 YYYY-MM-DD
:param from\_station: 出发站代码
:param to\_station: 到达站代码
:param purpose\_codes: 乘客类型代码,例如 "ADULT"
:return: 请求的响应数据,如果请求失败则返回 None
"""
if purpose\_codes == "":
purpose\_codes = "ADULT"
import httpx
url = "https://kyfw.12306.cn/otn/leftTicket/queryR"
params = {
"leftTicketDTO.train\_date": train\_date,
"leftTicketDTO.from\_station": from\_station,
"leftTicketDTO.to\_station": to\_station,
"purpose\_codes": purpose\_codes
}
# 添加 cookie 信息
cookies = {
"\_uab\_collina": "174296998906932728134402",
"JSESSIONID": "A059735A6E230BFE0E6C4E4282CD2137",
"route": "c5c62a339e7744272a54643b3be5bf64",
"BIGipServerotn": "1307574794.38945.0000",
"BIGipServerpool\_passport": "283378186.50215.0000",
"guidesStatus": "off",
"highContrastMode": "defaltMode",
"cursorStatus": "off",
"BIGipServerportal": "3168010506.17695.0000",
"\_jc\_save\_fromStation": "%u5317%u4EAC%2CBJP",
"\_jc\_save\_toStation": "%u5929%u6D25%2CTJP",
"\_jc\_save\_fromDate": "2025-03-26",
"\_jc\_save\_toDate": "2025-03-26",
"\_jc\_save\_wfdc\_flag": "dc"
}
try:
async with httpx.AsyncClient(cookies=cookies) as client:
response = await client.get(url, params=params)
response.raise\_for\_status()
json\_response = response.json()
optimized\_info = optimize\_train\_ticket\_info(json\_response)
return optimized\_info
except httpx.HTTPError as e:
print(f"Error: {e}")
return None
@mcp.tool()
async def local\_time() -> str:
"""Get the local time of the machine."""
import datetime
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if \_\_name\_\_ == "\_\_main\_\_":
# Initialize and run the server
mcp.run(transport='stdio')
配置 MCP host
{
"mcpServers": {
"12306": {
"command": "uv",
"args": [
"--directory",
"<mcp server代码的绝对路径>",
"run",
"12306.py"
]
}
}
}
输出结果
结论
使用 MCP 后,开发大模型应用更为简便。开发者无需手动包装 prompt,仅需关注买车票接口的实现函数,并进行相应配置,即可完成开发。
进一步而言,若 MCP server 有官方实现,上述配置采用固定格式,通过鼠标点击即可完成设置。例如,若 12306 官方提供实现,普通用户无需付出过多精力,便可创建出个性化的智能代理。
MCP 是怎么做到的
MCP 仅提出了一种协议式的解决方案,同时提供了多语言的软件开发工具包(SDK)及丰富的文档。大量开发者已完成众多 MCP 服务器的开发并将其开源,此网站(https://mcp.so/)已收集了 5000 个 MCP 服务器。实际上,MCP 在数据流和调用链方面与普通的 API 调用实现并无差异。
Why:为什么要使用 MCP?
- 它的 trade-off 是什么?
- 为什么这里考虑这样的 trade-off?
MCP 是一种提效方式。我认为 MCP 这样的协议是未来的大趋势。即使 MCP 失败了,也会有一个更通用的协议来替代它。
因为 MCP 将大模型的算法能力与外部环境信息解耦,开发者只需要关注自己的一小部分,即可完成一个 agent 的开发,而普通用户,完全可以做到用已开源的插件定制化自己的 agent。
复杂性不会凭空消失,那么 MCP 将复杂性隐藏到哪儿了呢?
MCP 将原本 agent 开发工作分为两块:调用外部接口和实现外部接口。
调用外部接口让 MCP host/client 完成,也许每个 agent 的 host 实现方式都不一样。而实现外部接口让 MCP server 实现,大部分厂商会实现自己的开源 MCP server,这样就减少了 agent 开发者的工作量了。
当市面上基于 MCP 的 agent 客户端越来越多,各大厂商一定会蜂拥实现自己的官方 MCP server,甚至要推广自己的 server。
比如,美团实现了自己的 MCP server,让用户在手机上说句话就可以点外卖,饿了么必然会跟进实现。
这里的 trade-off 就是,将实现外部接口这一任务分发给各大厂商,相比较原来的方式,是一种优化。
如果不用 MCP,那未来的大模型应用想做到“AI 管家”的角色,要怎么做呢?下图是另一种尝试思路。哪一种更优雅,更高效?
Where:它来自哪儿,去向哪儿?
MCP 协议本身解决的问题就是连接大模型与外部环境。
所以其前置工作有:
- 大模型的 Function Call,函数调用;
- OpenAI 插件计划(限制:只能在 OpenAI 的 ChatGPT Web 使用,不能再其他地方复用);
- LSP(语言服务器协议)。
MCP 的未来(个人猜测):
- 普通用户只需要关注 MCP server,如同下载 App 一样,下载 MCP server 插件,加强自己的 agent。
- 各大厂商优化自己的 MCP server,这是 AI 时代的 App。
- agent 开发者优化 host 的调用链路,如何合理利用 MCP server 才是 agent 的主要关注点。
LSP(语言服务器协议):在 LSP 中,当用户在编辑器中输入时,客户端查询语言服务器以自动完成建议或诊断。
在 MCP 超越 LSP 的地方在于其以智能体为中心的执行模型:LSP 主要是被动的(响应来自 IDE 的请求,基于用户输入),而 MCP 是专门为支持自主的 AI 工作流而设计的。根据上下文,AI 智能体可以决定使用哪些工具、按什么顺序以及如何将它们串在一起来完成任务。MCP 还引入了人工参与的功能,允许人类提供额外的数据并批准执行。
Who:谁创造的它?
MCP 最早由 Anthropic 公司开发,目的是帮助 AI 模型(如 Claude)更容易地连接工具和数据源。
但现在,MCP 已经成为一个开放协议,越来越多的企业和开发者开始采用它,这也让它逐渐成为 AI 与工具互动的新标准。
关于作者
艾家磊,字节跳动后端开发,对大模型有兴趣的小伙伴可以加微信讨论:x64Crack