【动手学MCP从0到1】2.1 SDK介绍和第一个MCP创建的步骤详解

news2025/7/23 2:29:41

SDK介绍和第一个MCP

  • 1. 安装SDK
  • 2. MCP通信协议
  • 3. 基于stdio通信
    • 3.1 服务段脚本代码
    • 3.2 客户端执行代码
      • 3.2.1 客户端的初始化设置
      • 3.2.2 创建执行进行的函数
      • 3.2.3 代码优化
  • 4. 基于SSE协议通信

1. 安装SDK

开发mcp项目,既可以使用Anthropic官方提供的SDK,也可以直接通过实现MCP协议的方式来实现。毫无疑问,通过SDK使用MCP是最方便和最节省时间的。安装命令如下:

pip install mcp[cli]

执行结果如下:(为保证版本之间互不干涉,可以新建一个虚拟环境进行安装,此处的mcp_projects就是新建的虚拟环境)

在这里插入图片描述

2. MCP通信协议

MCP是服务器和客户端架构的,服务器和客户端之间的通信协议包括两种:

  • stdio :本地的标准输入与输出,基于进程间通信。客户端将服务端脚本放到子进程中执行。
  • SSE:Server-Sent Event协议,底层基于Http协议。

不管是stdio还是SSE,他们都是采用JSON-RPC协议进行交互

3. 基于stdio通信

我们首先基于stdio来实现一个最简单的mcp应用。stdio是客户端代码中使用一个子进程来运行服务端脚本代码。这里我们实现一个计算两个数之和为例来演示开发过程

3.1 服务段脚本代码

新建一个项目,名为MCP projects,然后添加好python环境,比如虚拟环境是基于Python 3.12版本创建的MCP_projects名称,如下
在这里插入图片描述

然后再项目中命名文件夹名称为server.py,然后输入以下代码

from mcp.server.fastmcp import FastMCP

app = FastMCP("start mcp")

@app.tool()
def plus_tool(a:float, b:float) -> float:
    '''
    计算两个浮点数相加的结果
    :param a: 第一个浮点数
    :param b: 第二个浮点数
    :return: 浮点数
    '''
    return a + b

if __name__ == '__main__':
    app.run(transport="stdio")

代码中:函数中的介绍(description)十分重要,后面大模型调用函数(工具)时候需要按照介绍执行,因此需要尽可能的对函数功能和参数介绍清楚(具体的数据类型和解释)。

以上就是一个简单的server程序,执行一个加法运算。

3.2 客户端执行代码

完成服务端的功能代码编写后,接着就是创建一个客户端来运行服务端脚本代码。

接着在项目文件夹中,创建一个client_stdio.py文件。在编写代码之前需要先安装openai库

pip install openai

这里不是说要使用openai里面的ChatGPT的大模型来执行,而是使用openai提供的流程来加载国内的大模型,比如deepseek。其作为一个标准的大模型引入方式可以进行通用。

3.2.1 客户端的初始化设置

client_stdio.py文件中内容如下:(包括两个内容:①服务端的执行路径和②大模型引入)

from openai import OpenAI

# stdio: 在客户端中,启动一个新的子进程来执行服务端的脚本代码
class MCPClient(OpenAI):
    def __init__(self,server_path):
        self.server_path = server_path
        self.deepseek = OpenAI(
            api_key="<KEY>", #需要填入自己的key
            base_url="https://api.openai.com/v1",
        )

记住使用stdio进行执行服务端的任务是通过创建一个进程来实现的。为了方便后续操作,首先要有一个服务器路径server_path,也就是后面要执行的依据。然后就是把大模型框架加载进来,这里需要打开的deepseek官网的api使用的网址,如下
在这里插入图片描述

对于首次调用大模型的同学,注意看到上面红色框框的提醒,有时候我们会看到有人把base_url的赋值为:https://api.deepseek.com,也有人使用:https://api.deepseek.com/v1。这两种方式都是一样的效果。

在国内,如果使用base_url=https://api.openai.com/v1,运行结果显示下面报错,可以考虑将其赋值变为另外一种使用方式

在这里插入图片描述

然后,我们可以看到,里面如何创建一个客户端client,就是通过引入openai库中的OpenAI函数来实现的,里面有两个需要传递的参数,一个是大模型执行的base_url,就是上面介绍的两种方式都可以,然后另外一个就是api_key,该参数就是使用大模型的秘钥(需要付费),可以直接在官网左侧API keys导航栏创建,然后再充值导航栏中进行充值即可(首次使用可充值少量金额)。
在这里插入图片描述

3.2.2 创建执行进行的函数

创建run函数进行执行。函数中具体包含的步骤归纳如下:

  • 1.创建连接服务端的参数
  • 2.创建读、写通道
  • 3.创建客户端和服务端通信的session对象
  • 4.初始化通信
  • 5.获取服务端的所有工具(函数)
  • 6.将工具封装为Function Calling能识别的对象
  • 7.发送消息给大模型,让大模型选择调用哪个工具(大模型不会自己调用)
  • 8.重新把消息发给大模型,让大模型生成最终响应

(1)创建连接服务端的参数

这部分代码是指定客服端如何来执行服务端的脚本代码,需要指定具体的类型和文件路径。在mcp库中封装好了StdioServerParameters这个类。在PyCharm编辑器中,通过点击这个类名进入到具体介绍的文件中,如下
在这里插入图片描述

引入这个类,主要是两个重要参数commandargs,如下

    def run(self):
        # 1. 创建连接服务器端的参数
        from mcp.client.stdio import StdioServerParameters
        server_parameters = StdioServerParameters(
            command="python",
            args=[self.server_path],
        )

pycharm中代码如下,引入StdioServerParameters这个类的代码为了显示方便放在截图中,实际在编写代码时按照书写习惯会放置在文件的最上方。

在这里插入图片描述

(2)创建读、写通道
(3)创建客户端和服务端通信的session对象

这两个步骤实际上是一步,就是在创建进程时候的输出格式要求。创建进程的过程,在mcp中也封装了对应的类,这个类是stdio_client,其和StdioServerParameters引入方式一致。点击这个类的代码解释文件的内容如下

在这里插入图片描述

可以看出封装的这个函数是基于asynccontextmanager这个装饰器函数,因此在创建进程之前,需要搞清楚这个装饰器函数到底是起到什么作用,进一步点击装饰器函数名称,跳转到对应的解释文档,如下

在这里插入图片描述

函数解释中,详细介绍了这个上下文管理装饰器函数的使用方式,根据上面的介绍,在使用这个装饰器时候,函数会yield一个值出来,然后等价于后续使用with 函数名称 as 变量的方法,其中最后面的变量就是指引到yield后的那个值。

那么回到我们创建进程函数的这个代码文件中,可以找寻一下,是不是函数内部有一个yield的操作,如下
在这里插入图片描述

通过往下拉,可以发现在文件的185行中就出现了yield的现象。注意,这里不是yield出来了两个数据,而是一个元祖数据类型(可以省略括号),如下

在这里插入图片描述

按照StdioServerParameters文档中的介绍,创建进程的代码编写如下

from mcp.client.stdio import stdio_client
async with stdio_client(server=server_parameters) as (read_stream,write_stream):
    from mcp import ClientSession
    session = ClientSession()

Pycharm中的代码如下:(关于客户端对象的创建按照文档的格式可以完成,然后为了保障服务端和客服端之间的通信,也就是session的构建,mcp中封装了一个ClientSession的类,用于交互链接)

在这里插入图片描述

进一步,点击ClientSession的类的解释文件,详细看一下,里面有哪些参数和使用说明,如下
在这里插入图片描述

可以看出,前两个参数就是必须参数,为读、写通道(或者称为读、写流)。此外,需要考虑的是,涉及到I/O(读写,输入和输出)控制,需要考虑资源的管理,比如在读取文件使用,我们使用open函数就可以直接进行读取,但是往往使用with进行上下文管理,为了保证资源释放,避免泄漏。这里创建进程也是同理,为了保证资源利用,也是使用with进行处理。

完善后的代码如下

from mcp.client.stdio import stdio_client
async with stdio_client(server=server_parameters) as (read_stream,write_stream):
    from mcp import ClientSession
    async with ClientSession(read_stream, write_stream) as session:
        session.initialize()

Pycharm中的示意代码
在这里插入图片描述

(4)初始化通信

上面的初始化代码是利用Pycharn中AI自动补全的,通过查找ClientSession类中的初始化函数,可以发现,初始化的函数是异步的,也就是前面添加了async关键字,因此在代码中我们在调用异步函数执行时候,前面对应的要加上await关键字
在这里插入图片描述

此时定义run函数前也就需要添加异步函数的关键字,不然一个函数中不能同时存在异步和同步两种方法。此时前四步的完整代码如下

async def run(self):
    # 1. 创建连接服务器端的参数
    server_parameters = StdioServerParameters(
        command="python",
        args=[self.server_path],
    )
    # 2. 创建读写流
    async with stdio_client(server=server_parameters) as (read_stream,write_stream):
        # 3. 创建客户端与服务端的进程通话
        async with ClientSession(read_stream, write_stream) as session:
            # 4. 初始化请求
            await session.initialize()

以上的4步操作就是标准化的书写方式,可认为就是规定的流程。往后的流程会随着不同的工具而代码不同。

(5)获取服务端的所有工具(函数)

直接获取session中的所有的工具列表,借助list_tools()函数即可。然后此时可以打印出当前客户端可以获取服务端的具体的工具信息,如下

"""
-------------------------------------------------------------------------------
@Project : MCP projects
File    : client_stdio.py
Time    : 2025-06-03 9:40
author  : musen
Email   : xianl828@163.com
-------------------------------------------------------------------------------
"""
import asyncio

from openai import OpenAI
from mcp.client.stdio import StdioServerParameters,stdio_client
from mcp import ClientSession

# stdio: 在客户端中,启动一个新的子进程来执行服务端的脚本代码
class MCPClient(OpenAI):
    def __init__(self,server_path):
        self.server_path = server_path
        self.deepseek = OpenAI(
            api_key="<KEY>",
            base_url="https://api.openai.com/v1",
        )

    async def run(self):
        # 1. 创建连接服务器端的参数
        server_parameters = StdioServerParameters(
            command="python",
            args=[self.server_path],
        )
        # 2. 创建读写流
        async with stdio_client(server=server_parameters) as (read_stream,write_stream):
            # 3. 创建客户端与服务端的进程通话
            async with ClientSession(read_stream, write_stream) as session:
                # 4. 初始化请求
                await session.initialize()
                # 5. 获取服务端的所有的tools
                response = await session.list_tools()
                print(response)

if __name__ == "__main__":
    client = MCPClient('./server.py')
    asyncio.run(client.run())

为了方便核对代码及流程,对于第一次打印输出结果的代码给出完整版。最后面是通过初始化客户端类对象,然后进行运行,考虑到属于异步函数,需要再引入异步执行函数包装一下,最后输出的结果如下

在这里插入图片描述

通过输出结果,可以发现工具是在tools中,其是一个列表结构,里面有一些参数,后续会用到。

(6) 将工具封装为Function Calling能识别的对象

对于大模型来说,我们给与让其使用(选择)的工具,前提是其可以正常识别,因此,在让大模型调用工具之前,需要将工具转换为大模型可以识别的对象。

# 6. 将工具封装成Function Calling能识别的对象
# tools = [Tool(name='plus_tool',
#               description='\n    计算两个浮点数相加的结果\n    :param a: 第一个浮点数\n    :param b: 第二个浮点数\n    :return: 浮点数\n    ',
#               inputSchema={'properties': {'a': {'title': 'A', 'type': 'number'},
#                                           'b': {'title': 'B', 'type': 'number'}},
#                            'required': ['a', 'b'], 'title': 'plus_toolArguments', 'type': 'object'},
#               annotations=None)]
tools = []
for tool in response.tools:
    name = tool.name
    description = tool.description
    input_schema = tool.inputSchema
    tools.append(
        {
            "type":"function",
            "function":{
                "name":name,
                "description":description,
                "input_schema":input_schema,
            }
        }
    )
print(tools)

代码中,按照输出的格式来构建Function Calling能识别的对象,其中tools是一个列表套字典的形式,原来的是列表里面套一个Tool类对象。输出结果如下在这里插入图片描述

(7)发送消息给大模型,让大模型选择调用哪个工具(大模型不会自己调用)

关于首次接触大模型开发的新手,有必要梳理一下有关于大模型中角色role的作用和说明,如下

# 7. 发送消息给大模型,让大模型选择调用哪个工具(大模型不会自己调用)
# role:
# 1. user:用户发送给大模型的消息
# 2. assistant:大模型发给用户的消息
# 3. system:给大模型的系统提示词
# 4. tool:函数执行完后返回的信息
messages = [{
    "role":"user",
    "content":query
}]
deepseek_response = self.deepseek.chat.completions.create(
    messages=messages,
    model="deepseek-chat",
    tools=tools,
)

print(deepseek_response)

此时,运行后输出结果如下:(由输出结果可知,这个大模型返回的内容角色是assistant,代表内容是大模型返回给我们的,然后choice参数,代表大模型做的选择,其可以识别出我们问的问题:就是2+3为多少,例如a为3,b为2。大模型选择的工具是plus_tool

在这里插入图片描述

注意上面返回的结果中,finish_reason=tool_call,证明了大模型可以调用工具(选择了一个工具),为了让回复的结果更精准,可以考虑将大模型选择工具的message的信息添加到messages中

(8)重新把消息发给大模型,让大模型生成最终响应

上面的输出结果中,可以获取大模型的选择choice,获取具体的参数也很简单。需要判断一下大模型是不是选择了一个模型,也就是这里的finish_reason是不是等于tool_call。将信息添加到messages中,使用到model_dump()函数。tool_calls是一个列表,有时候会有多个工具,因此需要进行遍历循环。

注意遍历循环中,获取对应属性的值,可按照上面的输出结果进行理解,不要写错单词。主要是对于function信息的添加。信息收集完毕后,使用进程调用一下工具,得到第一次大模型调用服务端计算工具返回的结果。打印出来后,此时输出结果为5.0。

 choice = deepseek_response.choices[0]
 # 如果finish_reason=tool_calls,那么说明大模型选择了一个工具
 if choice.finish_reason == 'tool_calls':
     # 为了后期,大模型能够更加精准的回复,把大模型选择的工具的message,重新添加到messages
     messages.append(choice.message.model_dump())
     # 获取工具
     tool_calls = choice.message.tool_calls
     # 依次调用
     for tool_call in tool_calls:
         tool_call_id = tool_call.id
         function = tool_call.function
         function_name = function.name
         function_arguments = json.loads(function.arguments)
         result = await session.call_tool(name=function_name,arguments=function_arguments)
         content = result.content[0].text
         print(content)
         messages.append({
             "role":"tool",
             "content":content,
             "tool_call_id": tool_call_id
         })

     # 重新把数据发送给大模型,让大模型给出最终响应
     response = self.deepseek.chat.completions.create(
         model="deepseek-chat",
         messages=messages
     )
     print(response.choices[0].message.content)

程序输出结果如下:(大模型总共被调用2次,第一次返回的结果是5.0 ,第二次返回结果是把第一次返回的结果重新添加到信息中,再次调用返回的结果更加精确。注意:工具调用,此时的roletool,需要指定tool_call_id
在这里插入图片描述

至此,关于stdio进行通信的客户端执行服务端的工具的代码就梳理完毕了,最后全部的代码如下:

"""
-------------------------------------------------------------------------------
@Project : MCP projects
File    : client_stdio.py
Time    : 2025-06-03 9:40
author  : musen
Email   : xianl828@163.com
-------------------------------------------------------------------------------
"""
import asyncio
import json

from openai import OpenAI
from mcp.client.stdio import StdioServerParameters,stdio_client
from mcp import ClientSession


# stdio: 在客户端中,启动一个新的子进程来执行服务端的脚本代码
class MCPClient(OpenAI):
    def __init__(self,server_path):
        self.server_path = server_path
        self.deepseek = OpenAI(
            api_key="sk-5d307e0a45xxxxxce4575ff9",
            base_url="https://api.deepseek.com",
        )

    async def run(self,query:str):
        # 1. 创建连接服务器端的参数
        server_parameters = StdioServerParameters(
            command="python",
            args=[self.server_path],
        )
        # 2. 创建读写流
        async with stdio_client(server=server_parameters) as (read_stream,write_stream):
            # 3. 创建客户端与服务端的进程通话
            async with ClientSession(read_stream, write_stream) as session:
                # 4. 初始化请求
                await session.initialize()
                # 5. 获取服务端的所有的tools
                response = await session.list_tools()
                # print(response)
                # 6. 将工具封装成Function Calling能识别的对象
                # tools = [Tool(name='plus_tool',
                #               description='\n    计算两个浮点数相加的结果\n    :param a: 第一个浮点数\n    :param b: 第二个浮点数\n    :return: 浮点数\n    ',
                #               inputSchema={'properties': {'a': {'title': 'A', 'type': 'number'},
                #                                           'b': {'title': 'B', 'type': 'number'}},
                #                            'required': ['a', 'b'], 'title': 'plus_toolArguments', 'type': 'object'},
                #               annotations=None)]
                tools = []
                for tool in response.tools:
                    name = tool.name
                    description = tool.description
                    input_schema = tool.inputSchema
                    tools.append(
                        {
                            "type":"function",
                            "function":{
                                "name":name,
                                "description":description,
                                "input_schema":input_schema,
                            }
                        }
                    )
                # print(tools)

                # 7. 发送消息给大模型,让大模型选择调用哪个工具(大模型不会自己调用)
                # role:
                # 1. user:用户发送给大模型的消息
                # 2. assistant:大模型发给用户的消息
                # 3. system:给大模型的系统提示词
                # 4. tool:函数执行完后返回的信息
                messages = [{
                    "role":"user",
                    "content":query
                }]
                deepseek_response = self.deepseek.chat.completions.create(
                    messages=messages,
                    model="deepseek-chat",
                    tools=tools,
                )
                # print(deepseek_response)

                choice = deepseek_response.choices[0]
                # 如果finish_reason=tool_calls,那么说明大模型选择了一个工具
                if choice.finish_reason == 'tool_calls':
                    # 为了后期,大模型能够更加精准的回复,把大模型选择的工具的message,重新添加到messages
                    messages.append(choice.message.model_dump())
                    # 获取工具
                    tool_calls = choice.message.tool_calls
                    # 依次调用
                    for tool_call in tool_calls:
                        tool_call_id = tool_call.id
                        function = tool_call.function
                        function_name = function.name
                        function_arguments = json.loads(function.arguments)
                        result = await session.call_tool(name=function_name,arguments=function_arguments)
                        content = result.content[0].text
                        print(content)
                        messages.append({
                            "role":"tool",
                            "content":content,
                            "tool_call_id": tool_call_id
                        })

                    # 8. 重新把数据发送给大模型,让大模型给出最终响应
                    response = self.deepseek.chat.completions.create(
                        model="deepseek-chat",
                        messages=messages
                    )
                    print("AI回复:",response.choices[0].message.content)
                else:
                    print("执行出错")


if __name__ == "__main__":
    client = MCPClient('./server.py')
    asyncio.run(client.run("请帮计算3加2等于多少?"))

3.2.3 代码优化

关于async withAsyncExitStack的区别:(借助大模型回复的内容,都是属于资源管理的方式)
在这里插入图片描述

然后,

根据官网,提示的开发实例: For Client Developers。如下:

首先在类中添加AsyncExitStack函数,
在这里插入图片描述

然后再正文中处理时候,将async with替换成为了AsyncExitStack的使用方式。

在这里插入图片描述

最后,手动设置了清理函数,如下
在这里插入图片描述

最后优化后的全部代码如下:

from openai import OpenAI
from mcp.client.stdio import StdioServerParameters, stdio_client
from mcp import ClientSession
import asyncio
import json
from contextlib import AsyncExitStack

# stdio:在客户端中,启一个新的子进程来执行服务端的脚本代码

class MCPClient:
    def __init__(self, server_path: str):
        self.server_path = server_path
        self.deepseek = OpenAI(
            api_key="sk-e2bec0ab4xxxxxaad44b849",
            base_url="https://api.deepseek.com"
        )
        self.exit_stack = AsyncExitStack()

    async def run(self, query: str):
        # 1. 创建连接服务端的参数
        server_parameters = StdioServerParameters(
            command="python",
            args=[self.server_path]
        )
        # 改成用异步上下文堆栈来处理
        read_stream, write_stream = await self.exit_stack.enter_async_context(stdio_client(server=server_parameters))
        session = await self.exit_stack.enter_async_context(ClientSession(read_stream=read_stream, write_stream=write_stream))

        # 4. 初始化通信
        await session.initialize()

        # 5. 获取服务端有的tools
        response = await session.list_tools()
        # 6. 将工具封装成Function Calling格式的对象
        tools = []
        for tool in response.tools:
            name = tool.name
            description = tool.description
            input_schema = tool.inputSchema
            tools.append({
                "type": "function",
                "function": {
                    "name": name,
                    "description": description,
                    "input_schema": input_schema,
                }
            })
        # 7. 发送消息给大模型,让大模型自主选择哪个工具(大模型不会自己调用)
        # role:
        # 1. user:用户发送给大模型的消息
        # 2. assistant:大模型发送给用户的消息
        # 3. system:给大模型的系统提示词
        # 4. tool:函数执行完后返回的信息
        messages = [{
            "role": "user",
            "content": query
        }]
        deepseek_response = self.deepseek.chat.completions.create(
            messages=messages,
            model="deepseek-chat",
            tools=tools
        )
        choice = deepseek_response.choices[0]
        # 如果finish_reason=tool_calls,那么说明大模型选择了一个工具
        if choice.finish_reason == 'tool_calls':
            # 为了后期,大模型能够更加精准的回复。把大模型选择的工具的message,重新添加到messages中
            messages.append(choice.message.model_dump())

            # 获取工具
            tool_calls = choice.message.tool_calls
            # 依次调用工具
            for tool_call in tool_calls:
                tool_call_id = tool_call.id
                function = tool_call.function
                function_name = function.name
                function_arguments = json.loads(function.arguments)
                result = await session.call_tool(name=function_name, arguments=function_arguments)
                content = result.content[0].text
                messages.append({
                    "role": "tool",
                    "content": content,
                    "tool_call_id": tool_call_id
                })
            # 重新把消息发送给大模型,让大模型生成最终的回应
            response = self.deepseek.chat.completions.create(
                model="deepseek-chat",
                messages=messages
            )
            print(f"AI:{response.choices[0].message.content}")
        else:
            print('回复错误!')

    async def aclose(self):
        await self.exit_stack.aclose()


async def main():
    client = MCPClient(server_path="./server.py")
    try:
        await client.run('请帮我计算2加3等于多少')
    finally:
        # 不管上面代码是否出现异常,都要执行关闭操作
        await client.aclose()


if __name__ == '__main__':
    # client = MCPClient(server_path="./server.py")
    # asyncio.run(client.run("请帮我计算2加3等于多少"))
    asyncio.run(main())

4. 基于SSE协议通信

基于SSE协议,则服务端代码和客户端代码是分开来运行的。由于服务端代码是单独运行,因此可以非常方便的采用这种方式来调试服务端代码。

新建一个python文件,命名为:client_sse.py,全部代码如下

from openai import OpenAI
from mcp.client.sse import sse_client   # 修改1
from mcp import ClientSession
import asyncio
import json
from contextlib import AsyncExitStack

# stdio:在客户端中,启一个新的子进程来执行服务端的脚本代码

class MCPClient:
    def __init__(self, server_path: str):
        self.server_path = server_path
        self.deepseek = OpenAI(
            api_key="sk-e2bec0ab4cxxxxxx44b849",
            base_url="https://api.deepseek.com"
        )
        self.exit_stack = AsyncExitStack()

    async def run(self, query: str):
        # 改成用异步上下文堆栈来处理
        read_stream, write_stream = await 
        # 修改2
        self.exit_stack.enter_async_context(sse_client("http://127.0.0.1:8000/sse"))  
        session = await self.exit_stack.enter_async_context(ClientSession(read_stream=read_stream, write_stream=write_stream))

        # 4. 初始化通信
        await session.initialize()

        # 5. 获取服务端有的tools
        response = await session.list_tools()
        # 6. 将工具封装成Function Calling格式的对象
        tools = []
        for tool in response.tools:
            name = tool.name
            description = tool.description
            input_schema = tool.inputSchema
            tools.append({
                "type": "function",
                "function": {
                    "name": name,
                    "description": description,
                    "input_schema": input_schema,
                }
            })
        # 7. 发送消息给大模型,让大模型自主选择哪个工具(大模型不会自己调用)
        # role:
        # 1. user:用户发送给大模型的消息
        # 2. assistant:大模型发送给用户的消息
        # 3. system:给大模型的系统提示词
        # 4. tool:函数执行完后返回的信息
        messages = [{
            "role": "user",
            "content": query
        }]
        deepseek_response = self.deepseek.chat.completions.create(
            messages=messages,
            model="deepseek-chat",
            tools=tools
        )
        choice = deepseek_response.choices[0]
        # 如果finish_reason=tool_calls,那么说明大模型选择了一个工具
        if choice.finish_reason == 'tool_calls':
            # 为了后期,大模型能够更加精准的回复。把大模型选择的工具的message,重新添加到messages中
            messages.append(choice.message.model_dump())

            # 获取工具
            tool_calls = choice.message.tool_calls
            # 依次调用工具
            for tool_call in tool_calls:
                tool_call_id = tool_call.id
                function = tool_call.function
                function_name = function.name
                function_arguments = json.loads(function.arguments)
                result = await session.call_tool(name=function_name, arguments=function_arguments)
                content = result.content[0].text
                messages.append({
                    "role": "tool",
                    "content": content,
                    "tool_call_id": tool_call_id
                })
            # 重新把消息发送给大模型,让大模型生成最终的回应
            response = self.deepseek.chat.completions.create(
                model="deepseek-chat",
                messages=messages
            )
            print(f"AI:{response.choices[0].message.content}")
        else:
            print('回复错误!')

    async def aclose(self):
        await self.exit_stack.aclose()


async def main():
    client = MCPClient(server_path="./server.py")
    try:
        await client.run('请帮我计算2加3等于多少')
    finally:
        # 不管上面代码是否出现异常,都要执行关闭操作
        await client.aclose()


if __name__ == '__main__':
    # client = MCPClient(server_path="./server.py")
    # asyncio.run(client.run("请帮我计算2加3等于多少"))
    asyncio.run(main())

对于客户端的代码只需要修改2处,就是把原来导入stdio_cientr更换为sse_client。然后服务端中需要修改1处,如下

from mcp.server.fastmcp import FastMCP

app = FastMCP("start mcp")


@app.tool()
def plus_tool(a: float, b: float) -> float:
    """
    计算两数相加的工具
    :param a: 第一个相加的数
    :param b: 第二个相加的数
    :return: 返回两数相加后的结果
    """
    return a + b


if __name__ == '__main__':
    # app.run(transport="stdio")
    app.run(transport="sse")

将通信模型修改一下,然后再运行客户端之前,服务端需要先执行,根据输出的url进行配置客户端中的sse_client中的网址。

服务端运行输出结果如下:
在这里插入图片描述

客户端执行输出结果如下:
在这里插入图片描述

至此,第一个MCP项目的开发步骤详细梳理就完结,撒花✿✿ヽ(°▽°)ノ✿。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2400705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

测试面试题总结一

目录 列表、元组、字典的区别 nvicat连接出现问题如何排查 mysql性能调优 python连接mysql数据库方法 参数化 pytest.mark.parametrize 装饰器 list1 [1,7,4,5,5,6] for i in range(len(list1): assert list1[i] < list1[i1] 这段程序有问题嘛&#xff1f; pytest.i…

【深度学习】14. DL在CV中的应用章:目标检测: R-CNN, Fast R-CNN, Faster R-CNN, MASK R-CNN

深度学习在计算机视觉中的应用介绍 深度卷积神经网络&#xff08;Deep convolutional neural network&#xff0c; DCNN&#xff09;是将深度学习引入计算机视觉发展的关键概念。通过模仿生物神经系统&#xff0c;深度神经网络可以提供前所未有的能力来解释复杂的数据模式&…

UE 材质基础第三天

飘动的旗帜 错乱的贴图排序&#xff0c;创建一个材质函数 可以用在地面材质 体积云材质制作 通过网盘分享的文件&#xff1a;虚幻引擎材质宝典.rar 链接: https://pan.baidu.com/s/1AYRz2V5zQFaitNPA5_JbJw 提取码: cz1q --来自百度网盘超级会员v6的分享

【Github/Gitee Webhook触发自动部署-Jenkins】

Github/Gitee Webhook触发自动部署-Jenkins #mermaid-svg-hRyAcESlyk5R2rDn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hRyAcESlyk5R2rDn .error-icon{fill:#552222;}#mermaid-svg-hRyAcESlyk5R2rDn .error-tex…

新松机械臂 2001端口服务的客户端例程

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

电脑网络重置,找不到原先自家的WIFI,手机还能正常连接并上网

问题排查&#xff1a;1、电脑感觉网络太慢&#xff0c;因此打算点击了网络重置 2、点击提示会删除网络&#xff0c;在五分钟后关机重启 3、从设备管理器设备的无线wifi属性-事件中发现删除记录 4、选择更新驱动程序 5、从列表中选取 6、更改回老驱动版本 备选方案&#…

期末复习(学习)之机器学习入门基础

上课没听过报道。欢迎补充交流&#xff01; 前言&#xff1a;老师画的重点其实可以完全不用看&#xff0c;我这里只是看了一眼书顺着书本敲一遍。 比较干货的部分&#xff0c;直接看学习通的内容就好。最重要的是把学习通的内容记好。 目录 老师划的重点&#xff1a;P50 结构…

网络各类型(BMA,NBMA,P2P)

网络类型—基于二层&#xff08;数据链路层&#xff09;使用的协议不同从而导致数据包封装方式不同&#xff0c;工作方式也有所区别&#xff0c;从而对网络本身进行分类 一、网络类型分类 2. 关键差异对比 1. HDLC&#xff08;高级数据链路控制协议&#xff09; 协议特点&…

【计算机网络】第3章:传输层—概述、多路复用与解复用、UDP

目录 一、概述和传输层服务 二、多路复用与解复用 三、无连接传输&#xff1a;UDP 四、总结 &#xff08;一&#xff09;多路复用与解复用 &#xff08;二&#xff09;UDP 一、概述和传输层服务 二、多路复用与解复用 三、无连接传输&#xff1a;UDP 四、总结 &#xff08…

神经符号AI的企业应用:结合符号推理与深度学习的混合智能

&#x1f4a1; 技术前沿&#xff1a; 神经符号AI代表了人工智能发展的新阶段&#xff0c;它将深度学习的模式识别能力与符号推理的逻辑分析能力有机结合&#xff0c;创造出更加智能、可解释且可靠的AI系统。这种混合智能技术正在重塑企业的智能化应用&#xff0c;从自动化决策到…

VSCode 中 C/C++ 安装、配置、使用全攻略:小白入门指南

引言 本文为Windows系统下安装配置与使用VSCode编写C/C代码的完整攻略&#xff0c;示例机器为Windows11。 通过本文的指导&#xff0c;你可以成功在Windows 机器上上使用VSCode进行C/C开发。 在文章开始之前&#xff0c;你可以先阅读下面这段话&#xff0c;以便于对步骤有个大…

重温经典算法——希尔排序

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 基本原理 希尔排序是插入排序的改进版&#xff0c;通过按增量分组并逐步缩小增量实现排序。时间复杂度取决于增量序列&#xff0c;平均约为 O(n log n) 到 O(n^(3/2))&…

CortexON:开源的多代理AI系统无缝自动化和简化日常任务

简介 CortexON是一个开源的多代理AI系统&#xff0c;灵感来自Manus和OpenAI DeepResearch等高级代理平台。CortexON旨在无缝自动化和简化日常任务&#xff0c;擅长执行复杂的工作流程&#xff0c;包括全面的研究任务、技术操作和复杂的业务流程自动化。 技术架构 CortexON的技…

海信IP810N-海思MV320芯片-安卓9-2+16G-免拆优盘卡刷固件包

海信IP810N-海思MV320芯片-安卓9-216G-免拆优盘卡刷固件包 线刷方法&#xff1a;&#xff08;新手参考借鉴一下&#xff09; 1.准备一个优盘&#xff0c;最佳是4G&#xff0c;卡刷强刷刷机&#xff0c;用一个usb2.0的8G以下U盘&#xff0c;fat32&#xff0c;2048块单分区格式化…

2025年6月4日收获

Authorization Authorization是一种通用的、标准化的权限控制和认证的通用框架&#xff0c;它能够使跨系统和跨域的身份验证和授权管理更容易&#xff0c;使不同应用程序之间能够更轻松地实现单点登录&#xff08;SSO&#xff09;、用户身份验证和授权控制等。 在前端使用 axi…

leetcode hot100 链表(二)

书接上回&#xff1a; leetcode hot100 链表&#xff08;一&#xff09;-CSDN博客 8.删除链表的倒数第N个结点 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* currhead;int len0;while(curr){currcurr->next;len;}int poslen-n…

6. MySQL基本查询

1. 表的增删改查 Create(创建), Retrieve(读取), Update(更新), Delete(删除) 2. Create & Insert 语法: insert [info] table_name () values () 2.1. 案例: 创建一个学生表 指定列单行插入, 如果values前省略, 则默认是全属性插入多行指定列插入, 中间分隔符为, 3. 插入替…

CMS32M65xx/67xx系列CoreMark跑分测试

CMS32M65xx/67xx系列CoreMark跑分测试 1、参考资料准备 1.1、STM32官方跑分链接 1.2、官网链接 官方移植文档&#xff0c;如下所示&#xff0c;点击红框处-移植文档: A new whitepaper and video explain how to port CoreMark-Pro to bare-metal 1.3、测试软件git下载链接 …

中国区域30m/15天植被覆盖度数据集(2010-2022)

时间分辨率&#xff1a;日空间分辨率&#xff1b;&#xff1a;10m - 100m共享方&#xff1a;式开放获取数据大小&#xff1a;2.98 TB数据时间范围&#xff1a;2010-01-01 — 2022-12-31元数据更新时间&#xff1a;2024-12-23 数据集摘要 高时空分辨率的植被覆盖度产品存在着广…

力扣HOT100之二分查找:74. 搜索二维矩阵

这道题直接a了&#xff0c;我们可以参考上一道题&#xff1a;35.搜索插入位置的思路&#xff0c;详情见我的上一篇博客。将每一行的第一个元素当作一个数组中的元素&#xff0c;然后对这个数组进行二分查找&#xff0c;如果直接找到了target&#xff0c;则直接返回true&#xf…