【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?
本文介绍了MCP大模型上下文协议的的概念,并对比了MCP协议和function call的区别,同时用python sdk为例介绍了mcp的使用方式。
1. 什么是MCP?
官网:https://modelcontextprotocol.io/introduction
2025年,Anthropic提出了MCP协议。MCP全称为Model Context Protocol,翻译过来是大模型上下文协议。这个协议的主要为AI大模型和外部工具(比如让AI去查询信息,或者让AI操作本地文件)之间的交互提供了一个统一的处理协议。我们常用的USB TypeC接口(USB-C)统一了USB接口的样式,MCP协议就好比AI大模型中的USB-C,统一了大模型与工具的对接方式。
MCP协议采用了C/S架构,也就是服务端、客户端架构,能支持在客户端设备上调用远程Server提供的服务,同时也支持stdio流式传输模式,也就是在客户端本地启动mcp服务端。只需要在配置文件中新增MCP服务端,就能用上这个MCP服务器提供的各种工具,大大提高了大模型使用外部工具的便捷性。
MCP是开源协议,能让所有AI厂商、AI工具都将MCP集成到自己的客户端中,从而扩大MCP的可用面。毕竟只有用的人越多,协议才能不断发展,不断变得更好。
2. 了解function call
在MCP没有出来之前,我们的AI Agent开发如果想调用外部工具需要针对不同的AI大模型SDK编写不同的代码,其中最为常用的是openai提供的function call的处理逻辑。
本小节参考博客:
- 深入探讨Function Calling:实现外部函数调用的工作原理;
- 来自OpenAI官网的Function calling介绍与最佳实践
2.1. function call demo
2.1.1. 配置工具,AI提供参数
当我们调用 OpenAI Chat Completions 接口时,可以通过tools参数传入可供使用的外部工具。这个工具的调用中就包含了工具的作用,工具需要传入的参数,以及参数的释义。其中tool_choice
字段设置为auto代表让大模型自动选择tools,设置为none时不会调用外部工具。
{"tool_choice": "auto","messages": [{"role": "system","content": "你是一个天气查询助手"},{"role": "user","content": "帮我查询上海的天气"}],"tools": [{"type": "function","function": {"name": "get_weather","description": "获取指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string","description": "城市名"}},"required": ["city"],}}}]
}
对应的python openai代码如下,我们将tools部分放入一个包含dict的list,作为create函数的tools参数即可。同时tool_choice传入auto代表自动选择工具。这里我用了硅基流动提供的Qwen2.5模型作为演示,运行下面这个代码需要修改api_key为正确值。
import openai # 1.75.0
import json # 后续会用到jsondef main():client = openai.OpenAI(api_key="xxxxx",base_url="https://api.siliconflow.cn/v1")tools = [{"type": "function","function": {"name": "get_weather","description": "获取指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string","description": "城市名"}},"required": ["city"],}}}]res = client.chat.completions.create(model="Qwen/Qwen2.5-32B-Instruct",messages=[{"role": "system","content": "你是一个天气查询助手"}, {"role": "user","content": "帮我查询上海的天气"}],tools=tools,tool_choice="auto")print("content:",res.choices[0].message.content)print("tools:",res.choices[0].message.tool_calls)print("message:", res.choices[0].message.to_dict())
运行程序,发出请求后,大模型就会根据用户提出的问题和提供的tools,来为这个tools编写需要提供的参数。此时content会是空,不会输出内容,tool_calls中会包含调用的工具和参数。
❯ uv run main.py
content:
tools: [ChatCompletionMessageToolCall(id='01964be6e485603d6a2a0acbbc7eba91', function=Function(arguments='{"city": "上海"}', name='get_weather'), type='function')]
message: {'content': '', 'role': 'assistant', 'tool_calls': [{'id': '01964be6e485603d6a2a0acbbc7eba91', 'function': {'arguments': '{"city": "上海"}', 'name': 'get_weather'}, 'type': 'function'}]}
对应如下json格式响应,包含了我们的参数
"message": {"role": "assistant","content": "","tool_calls": [{"id": "01964be6e485603d6a2a0acbbc7eba91","type": "function","function": {"name": "get_weather","arguments": "{\n \"city\": \"上海\"\n}"}}]}
2.1.2. 调用工具并让AI二次处理
随后,我们就可以根据这个大模型返回的参数来调用我们的函数,并得到函数的返回结果,再次与大模型进行对话。此时需要按下面的方式维护对话上下文,首先需要将第一次请求AI返回的结果插入到上下文中("role": "assistant"
的json字符串),然后再插入工具调用的数据,格式如下:
{"role": "tool","content": "工具调用结果","tool_call_id": "ai调用工具时返回的id"
}
其中content代表工具调用的结果(字符串形式,内容可以是json),并且需要用tool_call_id
来标识这是哪一个工具调用的请求,必须要和"role": "assistant"
响应中的id对应。
二次AI交互对应python代码如下,在上文提供的python代码之后追加
# 插入结果,再次对话messages.append(res.choices[0].message.to_dict()) # ai第一次返回的数据# 工具调用的参数tool_call = res.choices[0].message.tool_calls[0]arguments = json.loads(tool_call.function.arguments)messages.append({"role": "tool","content": get_weather(arguments['city']),"tool_call_id": tool_call.id})# 二次请求res = client.chat.completions.create(model=model,messages=messages,tools=tools,tool_choice="auto")print("content:", res.choices[0].message.content)print("tools:", res.choices[0].message.tool_calls)print("message:", res.choices[0].message.to_dict())
其中get_weather函数如下,为了测试是写死的值,返回一个json字符串
def get_weather(location):return '{"Celsius": 27, "type": "sunny"}'
最终运行结果,AI成功根据我们工具调用的返回值来输出了对话方式的天气情况,包括温度和晴天。这样我们就完成了一个完整的tools调用和AI再处理的过程了
❯ uv run main.py
content:
tools: [ChatCompletionMessageToolCall(id='01964be6e485603d6a2a0acbbc7eba91', function=Function(arguments='{"city": "上海"}', name='get_weather'), type='function')]
message: {'content': '', 'role': 'assistant', 'tool_calls': [{'id': '01964be6e485603d6a2a0acbbc7eba91', 'function': {'arguments': '{"city": "上海"}', 'name': 'get_weather'}, 'type': 'function'}]}
-------------
content: 上海当前的天气是晴天,温度是27摄氏度。
tools: None
message: {'content': '上海当前的天气是晴天,温度是27摄氏度。', 'role': 'assistant'}
{% hideToggle 本次function call的完整上下文和代码 %}
完整json上下文信息
[{"role": "system","content": "你是一个天气查询助手"},{"role": "user","content": "帮我查询上海的天气"},{"content": "","role": "assistant","tool_calls": [{"id": "01964beeb9ee27098b74149d86560b35","function": {"arguments": "{\"city\": \"上海\"}","name": "get_weather"},"type": "function"}]},{"role": "tool","content": "{\"Celsius\": 27, \"type\": \"sunny\"}","tool_call_id": "01964beeb9ee27098b74149d86560b35"},{"role": "assistant","content": "上海当前的天气是晴天,温度是27摄氏度。"}
]
完整代码如下
import openai # 1.75.0
import jsondef get_weather(location):return '{"Celsius": 27, "type": "sunny"}'def main(model="Qwen/Qwen2.5-32B-Instruct"):client = openai.OpenAI(api_key="xxxx",base_url="https://api.siliconflow.cn/v1")tools = [{"type": "function","function": {"name": "get_weather","description": "获取指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string","description": "城市名"}},"required": ["city"],}}}]messages = [{"role": "system","content": "你是一个天气查询助手"}, {"role": "user","content": "帮我查询上海的天气"}]res = client.chat.completions.create(model=model,messages=messages,tools=tools,tool_choice="auto")print("content:", res.choices[0].message.content)print("tools:", res.choices[0].message.tool_calls)print("message:", res.choices[0].message.to_dict())print("-------------------")# 插入结果,再次对话messages.append(res.choices[0].message.to_dict())tool_call = res.choices[0].message.tool_calls[0]arguments = json.loads(tool_call.function.arguments)messages.append({"role": "tool","content": get_weather(arguments['city']),"tool_call_id": tool_call.id})res = client.chat.completions.create(model=model,messages=messages,tools=tools,tool_choice="auto")print("content:", res.choices[0].message.content)print("tools:", res.choices[0].message.tool_calls)print("message:", res.choices[0].message.to_dict())if __name__ == "__main__":main()
{% endhideToggle %}
2.1.3. tool_choice设置为none
这里也测试一下把tool_choice
设置为none的情况,此时即便传入了tools,AI也不会认为有外部工具,会直接返回文字说明。tool_choice
还有另外一个可选值是required,也就是必须要调用外部工具。
❯ uv run main.py
content: 我无法提供实时数据或即时查询服务,因为我当前的功能不包括访问互联网获取最新信息。你可以通过查询各类天气应用查看上海最新的天气情况,或者提供具体日期,我可以教你如何根据这些信息来判断和理解天气状况。如果你有任何关于天气的一般性问题,或需要了解某些天气条件的影响,也欢迎向我询问。
tools: None
message: {'content': '我无法提供实时数据或即时查询服务,因为我当前的功能不包括访问互联网获取最新信息。你可以通过查询各类天气应用查看上海最新的天气情况,或者提供具体日期,我可以教你如何根据这些信息来判断和理解天气状况。如果你有任何关于天气的一般性问题,或需要了解某些天气条件的影响,也欢迎向我询问。', 'role': 'assistant'}
2.2. function call的问题
如下是一次function call的流程图
graph TDA[编写tools,并提供tools参数配置] --> |调用OpenAI Chat Completions接口,提供tools|C[AI理解tools的作用和参数,并返回调用参数]C --> |脚本处理AI返回的参数,调用tools,获取结果|E[AI获取tools结果,解析并生成自然语言回答]
经过这个流程会发现一个问题,即便是简单的调用一个只有单参数的获取天气的函数,在使用openai这个第三方库的情况下都需要费很大劲,主要是tools的调用操作需要我们自己编写脚本实现,如果我们整个AI处理流程涉及到更多tools函数的时候,就很难处理了。
为了解决这个问题,openai在2025年新开源的OpenAI Agent SDK中提供了更加便捷的tools工具调用的处理,只需要编写一个工具类,在Agent初始化的时候传入,Agent就能自动识别这个工具类中的方法并调用工具(自动给本地的函数传入参数)。示例代码可以参考开源仓库中的example。
不过这还是没有解决一个最根本的问题,那就是外部工具调用方式的不统一。光是我现在知道的Agent开发SDK就有三个(OpenAI、QwenAgent、谷歌ADK),这三个SDK都会有一套自己的tools调用逻辑,而且openai和qwen的SDK更是只支持自家的模型,这样就会导致即便是同一个tools,在使用不同的SDK的时候,也需要针对这个SDK去重新编写一套tools的调用逻辑,很是麻烦。
了解了这个背景之后,想必你能理解MCP协议的重要性了,它规范了tools的调用方式,同一个tools我们只需要编写一次MCP Server,就能够在众多支持mcp的AI Agent里面被调用,解决了针对不同AI模型或SDK对tools进行单独适配的痛点!
3. MCP协议详解
接下来让我们简单了解一下MCP协议是怎么提供统一的tools调用方式的。在这之前,需要先注明几个名词
- MCP Hosts:如 Claude Desktop、IDE 或 AI 工具,希望通过 MCP 访问数据的程序(也就是AI Agent程序)
- MCP Clients:用于维护和服务器链接的一对一工具
- MCP Servers:通过MCP协议提供特定能力
- 本地数据源:MCP服务器可以安装访问本地的文件、数据库和服务
- 远程服务:MCP服务器可通过各类API链接互联网上的外部系统
其中要注意MCP Hosts和Clients的区别,为了更好的区分,后文会用AI来指代MCP Hosts,毕竟MCP工具的输出结果都是会让AI来处理的。
3.1. MCP Server
3.1.1. tools
以官方的MCP Server Demo为例,在tools模式下,Server主要提供的是两个能力,一个是获取当前服务器支持的tools,另外一个就是call tool调用工具的能力。
其中,获取当前支持的tools会返回tools列表、每个tools的inputSchema参数和参数的type/description释义。这一点和function call是类似的,只不过mcp的sdk将其包装成了一个types.Tool类
@app.list_tools()
async def list_tools() -> list[types.Tool]:return [types.Tool(name="fetch",description="Fetches a website and returns its content",inputSchema={"type": "object","required": ["url"],"properties": {"url": {"type": "string","description": "URL to fetch",}},},)]
除了人工编写这部分参数列表,我们还可以参考mcp_server_git的实现,借助pydantic来定义每一个方法的参数列表,并使用schema()
自动获取参数释义。
Tool(name=GitTools.DIFF_STAGED,description="Shows changes that are staged for commit",inputSchema=GitDiffStaged.schema(),
)
举个例子,运行下面的代码,可以得到GitStatus这个类定义的两个入参的名称、类型和说明
from pydantic import BaseModelclass GitStatus(BaseModel):repo_path: strhello_text: str# 运行会提示schema函数已经弃用,建议使用model_json_schema替代
print(GitStatus.model_json_schema())
# 输出
# {'properties': {'repo_path': {'title': 'Repo Path', 'type': 'string'}, 'hello_text': {'title': 'Hello Text', 'type': 'string'}}, 'required': ['repo_path', 'hello_text'], 'title': 'GitStatus', 'type': 'object'}
需要注意的是,这里获取到的title释义是直接根据参数名称来的,并没有人工编写的description那么准确。所以使用这种方式传入inputSchema的时候,需要我们尽可能地标准命名参数名称,让AI能通过参数名称直接推断出这个参数要传入什么内容。
调用函数的操作就和function call类似了,MCP协议传入的同样也是arguments列表,需要我们将其解析并调用我们实际编写的函数
@app.call_tool()
async def fetch_tool(name: str, arguments: dict
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:if name != "fetch":raise ValueError(f"Unknown tool: {name}")if "url" not in arguments:raise ValueError("Missing required argument 'url'")return await fetch_website(arguments["url"]) # 调用实际函数
除了这种复杂的方式,mcp sdk还提供了一个FastMcp,只需要我们在编写的函数上加一个@mcp.tool()
装饰器,就能立马把我们的普通函数变成mcp tools,非常方便。
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo")@mcp.tool()
def add(a: int, b: int) -> int:"""Add two numbers"""return a + bmcp.run() # 运行server
使用这种方式对时候,装饰器会自动去获取我们函数的参数以及参数的类型,并生成types.Tool
返回给客户端。这个装饰器有两个参数,name在不传入的时候默认为函数名称,description在不传入的时候默认为函数的docstring(也就是函数下的"""
注释)
name: Optional name for the tool (defaults to function name)
description: Optional description of what the tool does
从SDK的源码中可以找到,装饰器会调用Tool.from_function
来生成types.Tool
类型
def add_tool(self,fn: Callable[..., Any],name: str | None = None,description: str | None = None,) -> Tool:"""Add a tool to the server."""tool = Tool.from_function(fn, name=name, description=description)existing = self._tools.get(tool.name) # 判断是否有同名函数已经被注册过了if existing:if self.warn_on_duplicate_tools:logger.warning(f"Tool already exists: {tool.name}")return existing # 直接返回已注册的同名函数,不返回本次新注册的函数# 新注册函数self._tools[tool.name] = toolreturn tool
在Tool.from_function
函数中会注册name和description
func_name = name or fn.__name__
if func_name == "<lambda>": # lambda表达式没有函数名,必须传入name参数raise ValueError("You must provide a name for lambda functions")
func_doc = description or fn.__doc__ or ""
3.1.2. prompts和resources
除了最常用的tools,mcp还提供了prompts和resources两种服务方式,其中prompt是用于定义一些常用操作的提示词,此时客户端可以直接去获取这些提示词和ai交互,避免我们针对某一个流程重复编写提示词;resources是定义一个url格式,当我们的交流中出现这个url格式的时候,ai就可以调用这个工具去做一些特定操作,比如请求某个api或者操作数据库。
但很不幸的是,以上都是慕雪的个人简单理解,由于prompt和resources实在没有找到可以参考的博客或如何使用的demo,我并不是很理解它们在AI工具中是怎么被使用以及是在什么时候被使用的。网上针对MCP的教程也主要集中于tools层面。
后续如果对这俩有更多了解了,再回来补充本文。
3.2. 客户端配置本地和远程MCP Server
在MCP SDK中主要提供了两种server启动的方式,一个是stdio流式传输(本地)的方式,另外一个是sse远程API的方式。
TRANSPORTS = Literal["stdio", "sse"]
这两种方式分别对应了两种服务器的配置方式。如果是本地的mcp服务器,需要使用命令来指定mcp服务器代码文件所在路径,并启动它。这个代码可以是github上克隆的仓库,也可以是通过npm或其他方式安装到本地的可执行文件。
3.2.1. 本地(stdio)
以python编写的MCP Server为例,需要通过如下方式启动某一个MCP Server,其中--directory
指定的工作路径,必须指定这个工作路径才能找到mcp-simple-tool
的代码
# 官方 mcp-simple-tool demo
uv run --directory /Users/mothra/data/code/python/openai/mcp-python-sdk/examples/servers/simple-tool mcp-simple-tool
此时是采用stdio方式启动的server,对应配置文件如下(可供Agent SDK调用)
{"mcpServers": {"mcp-simple-tool": {"command": "uv","args": ["run","--directory","/Users/mothra/data/code/python/openai/mcp-python-sdk/examples/servers/simple-tool","mcp-simple-tool"]}}
}
当客户端需要使用这个MCP Server的时候,会自动根据我们配置的命令去尝试在本地启动这个mcp服务端,然后和它交互。所以,如果使用stdio来配置MCP Server但本地却没有uv环境的时候,程序是无法启动的。
以CherryStudio为例,在mcp配置中,以stdio格式添加我们这个配置,点击右上角保存,保存成功则代表配置正常。保存失败则需要检查配置的命令和路径是否出错
此时勾选底部的MCP服务器,和AI对话,给出一个URL,他会自动调用工具去下载这个URL的html文件,并解析和输出他对这个HTML文件的理解。
需要注意的是CherryStudio的mcpServers json配置文件并不是标准mcpServers的格式,多了一些字段,估计是方便前端设计。在AI的初次输出中也会把GdTGt4qMFpnyYqBxaqTrM输出出来,因为在标准mcpServers配置中GdTGt4qMFpnyYqBxaqTrM字段就是mcp服务端的名称。
{"mcpServers": {"GdTGt4qMFpnyYqBxaqTrM": {"isActive": true,"name": "网页获取","type": "stdio","description": "通过url获取网页内容","registryUrl": "","command": "uv","args": ["run","--directory","/Users/mothra/data/code/python/openai/mcp-python-sdk/examples/servers/simple-tool","mcp-simple-tool"]}}
}
标准的mcpServers的格式并不需要那些额外字段,只需要我在前文给出的基础mspServers配置就可以了。以Qwen-Agent为例,只需要把json填入tools中就可以调用mcp服务器了。
from qwen_agent.agents import Assistantdef init_agent_service():llm_cfg = {'model': 'Qwen/Qwen2.5-32B-Instruct','model_server': 'https://api.siliconflow.cn/v1','api_key': 'xxxx'}system = ('你是一个强大的助手,可以帮用户处理问题。')tools = [{"mcpServers": {"mcp-simple-tool": {"command":"uv","args": ["run", "--directory","/Users/mothra/data/code/python/openai/mcp-python-sdk/examples/servers/simple-tool","mcp-simple-tool"]}}}]bot = Assistant(llm=llm_cfg,name='网页查看助手',description='网页查看',system_message=system,function_list=tools,)return botdef main(text='这个网站是什么?https://blog.musnow.top/'):bot = init_agent_service()messages = [{'role': 'user', 'content': text}]# 请求for response in bot.run(messages):print('bot response:', response)if __name__ == "__main__":main()
Qwen最终的输出如下
{'role': 'assistant', 'content': '这个网址是一个个人博客站点,名字叫做“慕雪的寒舍”。站点描述自己为“爱折腾的代码初学者”。\n\n博客主要内容包含了编程学习(如Python、C、ROS等)、博客建站的相关知识以及一些编程相关的项目开发记录。\n\n网站首页还展示了近期发表的文章、公告、分类和标签等信息方便用户查找和浏览。\n\n总之,这是一个包含了编程学习和项目开发记录内容的个人技术博客。', 'reasoning_content': '', 'name': '网页查看助手'}
3.2.2. 远程(sse)
远程调用的配置就需要服务器的url了。首先通过如下方式启动demo,提供命令行参数sse以远程方式启动,port指定端口8000
uv run --directory /Users/mothra/data/code/python/openai/mcp-python-sdk/examples/servers/simple-tool mcp-simple-tool --transport sse --port 8000
此时终端会输出当前进程PID以及服务端的url
INFO: Started server process [17058]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
对应mcpServers配置文件,其中disabled是当前server是否被禁用,设置false为启用这个server,timeout是链接服务端的超时时间。
{"mcpServers": {"exampleServer": {"url": "http://127.0.0.1:8000/sse","disabled": false,"timeout": 30}}
}
在cherrystudio中填写 http://0.0.0.0:8000/sse
作为sse服务地址
同样可以正常调用,输出结果
注意,在QwenAgent SDK中必须使用http://127.0.0.1:8000/sse
才能正常连接这个远程服务器,不能使用http://0.0.0.0:8000/sse
,否则会出现502 Bad Gateway错误,详见issue。
INFO:mcp.client.sse:Connecting to SSE endpoint: http://0.0.0.0:8000/sse/
DEBUG:httpcore.connection:connect_tcp.started host='127.0.0.1' port=7897 local_address=None timeout=5 socket_options=None
DEBUG:httpcore.connection:connect_tcp.complete return_value=<httpcore._backends.anyio.AnyIOStream object at 0x1075a3c70>
DEBUG:httpcore.http11:send_request_headers.started request=<Request [b'GET']>
DEBUG:httpcore.http11:send_request_headers.complete
DEBUG:httpcore.http11:send_request_body.started request=<Request [b'GET']>
DEBUG:httpcore.http11:send_request_body.complete
DEBUG:httpcore.http11:receive_response_headers.started request=<Request [b'GET']>
DEBUG:httpcore.http11:receive_response_headers.complete return_value=(b'HTTP/1.1', 502, b'Bad Gateway', [(b'Connection', b'close'), (b'Content-Length', b'0')])
INFO:httpx:HTTP Request: GET http://0.0.0.0:8000/sse/ "HTTP/1.1 502 Bad Gateway"
DEBUG:httpcore.http11:response_closed.started
DEBUG:httpcore.http11:response_closed.complete
2025-04-19 15:51:29,240 - mcp_manager.py - 206 - INFO - Failed to connect to server: unhandled errors in a TaskGroup (1 sub-exception)
INFO:qwen_agent_logger:Failed to connect to server: unhandled errors in a TaskGroup (1 sub-exception)
2025-04-19 15:51:29,247 - mcp_manager.py - 91 - INFO - Error executing function: 'NoneType' object is not iterable
INFO:qwen_agent_logger:Error executing function: 'NoneType' object is not iterable
修改了tools的配置为远程url,其他代码保持不变
from qwen_agent.agents import Assistant
import logging
logging.basicConfig(level=logging.DEBUG)def init_agent_service():llm_cfg = {'model': 'Qwen/Qwen2.5-32B-Instruct','model_server': 'https://api.siliconflow.cn/v1','api_key': 'xxxx'}system = ('你是一个强大的助手,可以帮用户处理问题。')tools = [{"mcpServers": {"exmaple-server": {"url": "http://127.0.0.1:8000/sse","disabled": False,"timeout": 30}}}]bot = Assistant(llm=llm_cfg,name='网页查看助手',description='网页查看',system_message=system,function_list=tools,)return botdef main(text='这个网站是什么?https://blog.musnow.top/'):bot = init_agent_service()messages = [{'role': 'user', 'content': text}]# 请求for response in bot.run(messages):print('bot response:', response)if __name__ == "__main__":main()
QwenAgent正常调用工具并返回网页的结果
{'role': 'assistant', 'content': '这是一个名为"慕雪的寒舍"的博客网站。博客的作者是慕雪年华。网站上有很多关于编程学习、技术分享的文章,同时,博客还提供了分类、标签、归档等功能来帮助读者查找信息。它看起来像是一个个人的技术博客。', 'reasoning_content': '', 'name': '网页查看助手'}
3.3. 使用MCP协议后的tools调用流程图
使用MCP协议后,流程图就变成了下面这样,此时ai工具就从tools中解放了出来,我们可以随心所欲地添加任何我们需要的MCP Server配置,最终ai能自动调用这些外部工具并处理他们的结果,不再需要像function call一样人工编写脚本处理大模型生成的参数,也不需要在不同Agent SDK中独立为tools编写适配代码了。
graph TD;A[ai工具加载mcp配置] --> |启动和连接mcp服务器|BB[mcp client] --> |链接服务器,获取已有工具,调用工具|C[MCP Server] C -->|返回已有工具,返回工具调用结果|B
在让AI调用外部工具的方面,MCP协议还是非常重要的。
不过需要注意的是,MCP和function call并不是完全独立的关系。目前MCP Host中接入MCP工具有两种方式,一个是prompt的方式,另外一个就是function call方式。也就是说,在部分ai工具中,会直接使用function call功能来让ai识别MCP工具并调用。MCP主要的助力在于统一了调用这些外部工具的配置项和服务端的实现方式,让我们编写的外部工具能做到一次编写多处使用。
5. The end
关于MCP协议的介绍到这里就结束啦,主要介绍的还是MCP TOOLS方面的内容,有任何问题欢迎评论区讨论。
本文首发地址:【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?,首发于 2025-04-19 08:56:01
点我查看更多精彩内容:慕雪的寒舍