【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
点我查看更多精彩内容:慕雪的寒舍
相关文章:
【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?
本文介绍了MCP大模型上下文协议的的概念,并对比了MCP协议和function call的区别,同时用python sdk为例介绍了mcp的使用方式。 1. 什么是MCP? 官网:https://modelcontextprotocol.io/introduction 2025年,Anthropic提出…...
交换机之配置系统基本信息(Basic Information of the Configuration System for Switches)
交换机之配置系统基本信息 本文章中的信息都是基于一些特定实验环境写的。文章中使用的所有设备最初均采用缺省配置启动。如果不按初始配置,有可能会导致本文中的部分或全部步骤失败。如果您使用的是真实网络设备,请确保您已经了解所有使用过的命令影响。…...
贝叶斯算法学习
贝叶斯算法学习 贝叶斯算法基础与原理应用场景主要分类优缺点简单示例代码实现 贝叶斯算法是基于贝叶斯定理的一种统计学习方法,在机器学习、数据挖掘、自然语言处理等领域有广泛应用。以下是其原理、应用和示例的详细介绍: 贝叶斯算法基础与原理 贝…...
Java 日志:掌握本地与网络日志技术
日志记录是软件开发中不可或缺的一部分,它为开发者提供了洞察应用程序行为、诊断问题和监控性能的手段。在 Java 生态系统中,日志框架如 Java Util Logging (JUL)、Log4j 和 Simple Logging Facade for Java (SLF4J) 提供了丰富的功能。然而,…...
线程池单例模式
线程池的概念 线程池是一种线程使用模式。 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。…...
物联网安全解决方案介绍:有效利用现有的下一代防火墙
管理物联网设备安全的挑战 如今,随着IoT(物联网)的普及,可以集中管理相机、打印机、传感器、电器、机床等各种设备,并分析和有效利用从这些设备收集的数据。这些设备已成为商业环境中不可或缺的一部分,但设备的多样化使其难以管理。与PC、服务器和网络设备不同,识别物联…...
Java社区门诊系统源码 SaaS医院门诊系统源码 可上线运营
Java社区门诊系统源码 SaaS医院门诊系统源码 医院门诊系统适用于:社区卫生服务站、门诊部、诊所、村卫生室等,有上百家医疗机构正常使用;包括医保结算。 系统功能 (一)后端管理系统功能 用户管理:提供用…...
如何在 Windows 10 中使用 WSL 和 Debian 安装 Postgresql 和 Postgis
安装 Postgresql 和 Postgis 的常规方法需要设置多个二进制文件,并且工作流程通常在图形用户界面 (GUI) 上进行。我们希望找到一种在 Windows 10 中安装 Postgresql 和 Postgis 的方法,同时保留 Linux 的 shell 体验。本教程展示了在 Windows 10 中的 De…...
[论文解析]Mip-Splatting: Alias-free 3D Gaussian Splatting
Mip-Splatting: Alias-free 3D Gaussian Splatting 论文地址:https://arxiv.org/abs/2403.17888 源码地址:https://github.com/autonomousvision/mip-splatting 项目地址:https://niujinshuchong.github.io/mip-splatting/ 论文解读 两个主…...
MongoDB 入门使用教程
MongoDB 入门使用教程 MongoDB 是一个开源的 NoSQL 数据库,使用文档(JSON-like)存储数据,与传统的关系型数据库不同,它不依赖表结构和行列的约束。MongoDB 提供了强大的查询能力,支持高效的数据存储和检索…...
PowerBI动态路径获取数据技巧
PowerBI动态路径获取数据技巧 场景一:同事接力赛——不同电脑共用模板 (想象一下:小王做完报表要传给小李,结果路径总对不上怎么办?) 这种情况就像接力赛交接棒,每台电脑的账户名不同࿰…...
【数据结构】优先级队列
目录 1. 优先级队列概念 2. 优先级队列的模拟实现 2.1 堆的概念 2.2 堆的存储方式 2.3 堆的创建 2.3.1 向下调整的时间复杂度 2.3.2 建堆时间复杂度 2.3.3 向上调整的时间复杂度 2.4 堆的插入与删除 3. 堆的应用 4. 常用接口介绍 4.1 PriorityQueue的特性 4.2 Pri…...
Myweb项目——面试题总结
一.项目描述 项⽬概述:本项⽬在云服务上开发了⼀个后端服务器与前端⻚⾯为⼀体的⾳乐专辑 鉴赏⽹站,旨在为⽤⼾提供丰富的⾳乐专辑信息展⽰和优 质的浏览体验。 主要内容及技术: 后端开发:利⽤ C 语⾔构建后端服务器,…...
用高德API提取广州地铁线路(shp、excel)
目录 结果示例html文件——直观看出输出的效果excel文件——包括地铁的具体信息完整代码网络上现有的地铁数据要么过于老旧且不便于更新,要么过于定制化限定于具体的城市无法灵活调整得到自己真正想要的那部份数据。而使用高德的API可以非常方便得到全国各地的地铁数据,而且可…...
leetcode110 平衡二叉树
一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。 递归:…...
二、信息时代社会结构的转变
到了信息时代,以及在核武器的前提下,上述的社会结构的逻辑,就有了一个根 本性的转变,就是暴力的成本和收益,都在下降。 暴力的成本在降低。比如说枪支,它的制造和分发都变得非常容易。现在我们都 知道有 3D 打印,它就好像工业时代的印刷机,印刷圣经或者书籍,使知识更加 普及和容…...
Vue2+ElementUI实现无限级菜单
使用Vue2和ElementUI实现无限级菜单,通常菜单数据以树形结构存储,每个菜单包含多个子菜单 ,子菜单又可以继续包含更深层次的子菜单项。所以,需要使用递归形式,完成子项菜单的渲染。 这里,结合Element UI界面的el-menu和el-submenu组件来构建菜单结构,有子菜单时使用el-s…...
YTJ笔记——FFT、NCC
FFT算法 快速傅里叶算法 为了计算两个多项式相乘(卷积),将多项式系数表示法转换成点值表示法 F(g(x)∗f(x))F(g(x))F(f(x)) 超详细易懂FFT(快速傅里叶变换)及代码实现_傅立叶变换编程-CSDN博客 NCC算法…...
模型 隐含前提
系列文章分享模型,了解更多👉 模型_思维模型目录。隐藏的思维地基,决策的无声推手。 1 隐含前提模型的应用 1.1 金融投资决策—科技股估值隐含前提的验证 行业:金融投资 应用方向:投资逻辑验证 背景解读࿱…...
大模型微调与蒸馏的差异性与相似性分析
大模型微调与蒸馏的差异性分析 一、定义与核心目标差异 大模型微调 在预训练大模型基础上,通过少量标注数据调整参数,使模型适应特定任务需求。核心目标是提升模型在特定领域的性能,例如医疗影像分析或金融预测。该技术聚焦于垂直场景的精度…...
Ubuntu下安装vsode+qt搭建开发框架(一)
Ubuntu下安装vsode+qt搭建开发框架(一) g++的编译环境,这里不介绍,可点击这里查看 查看一下当前的g++环境 g++ --version g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copyin…...
单片机-89C51部分:3、创建工程
飞书文档https://x509p6c8to.feishu.cn/wiki/Mzhnwb1qni6YkykJtqXc17XMnre 创建工程 首先创建一个文件夹,用来存放工程文件,文件夹名称最好为英文,例如Demo1。 打开软件KEIL,上方菜单栏选择Project ->new uVision Project&am…...
从零实现 registry.k8s.io/pause:3.8 镜像的导出与导入
以下是为 registry.k8s.io/pause:3.8 镜像的导出与导入操作定制的完整教程,适用于 Kubernetes 集群中使用 containerd 作为容器运行时的场景。本教程包含详细步骤、常见问题解析及注意事项。 从零实现 registry.k8s.io/pause:3.8 镜像的导出与导入 背景说明 Kuber…...
详解Adobe Photoshop 2024 下载与安装教程
Adobe Photoshop下载安装和使用教程 Adobe Photoshop,简称“PS”,是由Adobe Systems开发和发行的图像处理软件。Photoshop主要处理以像素所构成的数字图像。使用其众多的编修与绘图工具,可以有效地进行图片编辑和创造工作,…...
thinking-intervention开源程序用于DeepSeek-R1等推理模型的思维过程干预,有效控制推理思考过程
一、软件介绍 文末提供程序和源码下载 thinking-intervention开源程序用于DeepSeek-R1等推理模型的思维过程干预,有效控制推理思考过程。基于论文 《Effectively Controlling Reasoning Models through Thinking Intervention》 实现的思维干预技术,用于…...
Qt 5.15 编译路径吐槽点
在Qt 5.15以前,勾选“Shadow build”会自动将编译文件放在同一个目录下(区分编译器类型、Qt版本、debug和release等),可将代码文件和编译文件区分开,用户不用操心。但是奇葩的是,这个功能Qt 5.15居然失效了…...
【机器学习-线性回归-3】深入浅出:简单线性回归的概念、原理与实现
在机器学习的世界里,线性回归是最基础也是最常用的算法之一。作为预测分析的基石,简单线性回归为我们理解更复杂的模型提供了完美的起点。无论你是机器学习的新手还是希望巩固基础的老手,理解简单线性回归都至关重要。本文将带你全面了解简单…...
【MCP Node.js SDK 全栈进阶指南】中级篇(5):MCP客户端高级开发
在前面的系列文章中,我们主要关注了服务器端的开发,包括基础服务器构建、资源设计、身份验证与授权以及错误处理与日志系统。本篇文章将转向客户端,探讨MCP TypeScript-SDK在客户端开发中的高级应用。 客户端开发是MCP应用的重要组成部分,它连接了用户与服务器,提供了交互…...
RASP技术在DevOps中的安全应用
随着新技术的不断演进,DevOps开发模式不断被利用,Web应用程序的开发相比过往更高效。随之而来的是保护这些应用程序同样面临着巨大挑战,黑客的攻击手段不断多变,而DevOps团队成员却不都是安全专家,难以保证应用程序的安…...
2025.04.26-饿了么春招笔试题-第三题
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. 魔法网格变换计数 问题描述 LYA是一位魔法研究学者,她正在研究一种特殊的魔法网格。这个网格有 n n n...
Linux线程与进程:探秘共享地址空间的并发实现与内
Linux系列 文章目录 Linux系列前言一、线程的概念二、线程与地址空间2.1 线程资源的分配2.2 虚拟地址到物理地址的转换 三 、线程VS进程总结 前言 在Linux操作系统中,线程作为CPU调度的基本单位,起着至关重要的作用。深入理解线程控制机制,是…...
数据结构手撕--【堆】
目录 编辑 定义结构体: 初始化: 插入数据: 删除: 取堆顶元素: 堆销毁: 判断堆是否为空: TopK问题: 堆其实是完全二叉树 物理结构:二叉树的层序遍历(…...
MySQL基本命令--系统+用户+表
前言:在当今数据驱动的时代,关系型数据库依然是构建信息系统的中坚力量,而MySQL作为开源领域中最广泛使用的数据库管理系统之一,以其高性能、稳定性和易用性赢得了开发者和企业的青睐。无论是在小型博客系统中承担数据存储职责&am…...
4.23-4.26学习总结 HTML—CSS常见标签和样式
页部导航栏: flex样式: 表单标签: (25行是设置点击按钮) 表单项标签: 搜索表单区域: 底部版权区域: 总结:...
使用Django框架表单
使用Django框架表单 文章目录 使用Django框架表单[toc]1.使用Form类构建表单2.表单字段与Widget控件 1.使用Form类构建表单 【创建项目和应用】 PS C:\Users\ls> cd E:\Python\ PS E:\Python> django-admin.exe startproject FormSite PS E:\Python> cd .\FormSite\…...
OpenCV第6课 图像处理之几何变换(缩放)
1.简述 图像几何变换又称为图像空间变换,它将一幅图像中的坐标位置映射到另一幅图像中的新坐标位置。几何变换并不改变图像的像素值,只是在图像平面上进行像素的重新安排。 根据OpenCV函数的不同,本节课将映射关系划分为缩放、翻转、仿射变换、透视等。 2.缩放 2.1 函数…...
Python AI图像生成方案指南
1. 简介 AI图像生成是当前最热门的AI应用领域之一,Python提供了多种工具和库来实现这一功能。本指南将介绍几种主流的AI图像生成方案及其Python实现方法。 2. 主流AI图像生成技术 2.1 生成对抗网络(GANs) 原理:由生成器和判别器组成的对抗系统 特点&am…...
【C++】stack、queue和priority_queue的模拟实现
文章目录 前言一. stack1.1 stack的介绍1.2 stack的使用1.3 stack的模拟实现 二. queue2.1 queue的介绍2.2 queue的使用2.3 queue的模拟实现 三. deque3.1 deque的原理介绍3.2 deque的缺陷3.3 为什么选择deque作为stack和queue的底层默认容器 四. priority_queue(优…...
Jmeter数据库url开关设置+常用Beanshell
1、数据库url开关设置 (79 90) jdbc:mysql://test.lemonban.com:3306/future?allowMultiQueries-true&characterEncodingUTF-8 多条查询开关:allowMultiQueriestrue 字符集配置:characterEncodingUTF-8 2、用BeanShell提取Map中的方…...
NtripShare 2025第一季度主要技术进展
GNSS方面 1、开源GNSS接收机配置软件基础版本。 2、商业版本GNSS接收机配置软件,增加PPP、文件保存、前端解算(静态、RTK-Static),前端坐标转换。 3、GNSS接收机配置软件全面适配米尔T133i硬件方案。 视觉检测方面 1、做出第…...
Linux系统编程之内存映射
概述 内存映射是操作系统提供的一种机制,使得文件或设备的内容可以直接映射到进程的虚拟地址空间中。这意味着,我们可以像访问数组一样读写文件内容,而不需要显式地调用I/O函数进行数据传输。内存映射适用于多种应用场景,包括但不…...
一文详解Adobe Photoshop 2025安装教程
Adobe Photoshop下载安装和使用教程 Adobe Photoshop,简称“PS”,是由Adobe Systems开发和发行的图像处理软件。Photoshop主要处理以像素所构成的数字图像。使用其众多的编修与绘图工具,可以有效地进行图片编辑和创造工作,…...
ShenNiusModularity项目源码学习(23:ShenNius.Admin.Mvc项目分析-8)
用户列表页面用于检索、新建、编辑、删除系统用户,同时设置用户角色。该页面对应的文件Index.cshtml位于ShenNius.Admin.Mvc项目的Areas\Sys\Views\User内,同目录下还有Modify.cshtml(新建、编辑用户)、SetRole.cshtml(…...
vue中 vue.config.js反向代理
新建一个node 服务 1 npm init -y //创建一个package.json 2.npm i express 3. 新建一个app.js 4.键入代码 const express require("express") const app express()app.get("/user",(req,res)>{res.send({"name":"good"…...
AIGC赋能智慧医疗:从影像诊断到个性化治疗的革命性突破
一、医疗AIGC技术架构 1.1 医疗场景技术挑战 医疗环节 行业痛点 AIGC解决方案 影像诊断 人工阅片效率低 多模态病灶分割与分级系统 病历管理 结构化程度低 语音转文本智能编码 药物研发 周期长成本高 分子生成与虚拟筛选 个性化治疗 方案标准化不足 基因组学临床数据融合模型 1…...
Yarn 安装与使用教程
Yarn 安装与使用教程 Yarn 是一个由 Facebook 开发的 JavaScript 包管理工具,它比传统的 npm 更加高效、可靠,并且在性能上有所提升。Yarn 主要解决了 npm 安装速度慢、并发性差、缓存机制不完善等问题,它提供了更快的安装速度、更稳定的依赖…...
机器学习之二:指导式学习
正如人们有各种各样的学习方法一样,机器学习也有多种学习方法。若按学习时所用的方法进行分类,则机器学习可分为机械式学习、指导式学习、示例学习、类比学习、解释学习等。这是温斯顿在1977年提出的一种分类方法。 有关机器学习的基本概念,…...
【学习笔记】检索增强生成(RAG)技术
检索增强生成(RAG)技术:原理、实现与发展趋势 1. RAG技术概述 检索增强生成(Retrieval-Augmented Generation,RAG)是一种将信息检索与生成模型相结合的技术,旨在增强大型语言模型的知识能力和…...
计算机视觉——对比YOLOv12、YOLOv11、和基于Darknet的YOLOv7的微调对比
概述 目标检测领域取得了巨大进步,其中 YOLOv12、YOLOv11 和基于 Darknet 的 YOLOv7 在实时检测方面表现出色。尽管这些模型在通用目标检测数据集上表现卓越,但在 HRSC2016-MS(高分辨率舰船数据集) 上对 YOLOv12 进行微调时&…...
Pygame跨平台打包:将游戏发布到Windows、Mac和Linux
Pygame跨平台打包:将游戏发布到Windows、Mac和Linux 引言 在游戏开发的世界中,Pygame 是一个非常受欢迎的库,它使得使用 Python 编写 2D 游戏变得简单而有趣。然而,当你完成了一个游戏并希望将其发布给更多的玩家时,如何将你的游戏打包成可以在不同操作系统上运行的可执…...