MCP client支持同时连接多个MCP server-使用Python开发

该文章已生成可运行项目,

1. 相关总结

总结内容链接
MCP之大模型Function Calling开发与原理https://blog.youkuaiyun.com/a82514921/article/details/147860120
MCP概念与server开发及调试-使用Python+SSE传输机制https://blog.youkuaiyun.com/a82514921/article/details/147860221
MCP client开发与日志分析-使用Python+SSE传输机制https://blog.youkuaiyun.com/a82514921/article/details/147860518
MCP细节与原理分析-使用Python+SSE传输机制https://blog.youkuaiyun.com/a82514921/article/details/147860541
MCP server支持在不同Python脚本中实现工具方法https://blog.youkuaiyun.com/a82514921/article/details/147860559
MCP client支持同时连接多个MCP server-使用Python开发https://blog.youkuaiyun.com/a82514921/article/details/147860587

2. 前言

假如使用 Python 开发 MCP client,需要支持同时连接到多个 MCP server,可以参考以下方式

3. 实现方式

3.1. 代码示例

以下示例代码可参考 https://github.com/Adrninistrator/MCP-DEMO

async def query_mcp_server(question: str, parallel: bool):
    available_tools = []
    tool_session_map: Dict[str, ClientSession] = {}
    async with AsyncExitStack() as stack:
        for server_url in server_url_array:
            read, write = await stack.enter_async_context(sse_client(url=server_url))
            session: ClientSession = await stack.enter_async_context(ClientSession(read, write))
            # Initialize the connection
            await session.initialize()
            # List available tools
            tools = await session.list_tools()
            logger.info(f"MCP Server Tools {server_url} {tools}")
            for tool in tools.tools:
                if tool.name in tool_session_map:
                    logger.error(f"重复名称的工具 {tool.name}")
                    raise ValueError(f"重复名称的工具 {tool.name}")
                tool_session_map[tool.name] = session
                tool_param: ChatCompletionToolParam = {
                    "type": "function",
                    "function": {
                        "name": tool.name,
                        "description": tool.description,
                        "parameters": tool.inputSchema
                    }
                }
                available_tools.append(tool_param)

        messages = [{
            "role": "system",
            "content": "仅使用 role=tool 相关数据进行分析,不使用模型中的数据"
        }, {
            "role": "user",
            "content": f"要求:每次返回选择工具时,在 choices.message.content 返回选择工具的原因、n 任务:{question}"
        }]

        seq = 0
        while True:
            seq += 1
            response = MCPClientBase.client.chat.completions.create(
                model=MCPClientBase.model,
                messages=messages,
                tools=available_tools,
                parallel_tool_calls=parallel
            )

            assistant_output = response.choices[0].message
            if assistant_output.content is None:
                assistant_output.content = ""

            if response.choices[0].finish_reason != "tool_calls" \
                    or assistant_output.tool_calls is None \
                    or len(assistant_output.tool_calls) == 0:
                logger.info(f"获得结果:{assistant_output.content}")
                return assistant_output.content

            messages.append(assistant_output)

            tool_seq = 0
            for tool_call in assistant_output.tool_calls:
                tool_seq += 1
                function = tool_call.function
                tool_name = function.name
                session = tool_session_map[tool_name]
                if session is None:
                    logger.error(f"模型返回的工具不存在 {tool_name}")
                    raise ValueError(f"模型返回的工具不存在 {tool_name}")
                mcp_server_result: CallToolResult = await session.call_tool(tool_name, json.loads(
                    function.arguments))
                if mcp_server_result.isError:
                    raise ValueError(f"调用 MCP server tool {tool_name} 返回失败")

                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": mcp_server_result.content
                })
                if not parallel:
                    break

3.2. 实现说明

使用数组 available_tools 保存可用的 MCP server 工具

使用类似 Map 结构的 tool_session_map 保存会话对象,key 为 MCP 工具名称,value 为 mcp.client.session.ClientSession 类型的会话对象

遍历需要处理的 MCP server SSE url,对应“for server_url in server_url_array:”

创建 contextlib.AsyncExitStack 类型的对象 stack,用于记录和集中清理需要关闭的异步资源

调用 mcp.client.sse.sse_client() 方法创建的数量为 2 的元组,需要调用 stack 对象对应的 contextlib.AsyncExitStack.enter_async_context() 方法进行记录,以在后续关闭

创建的 mcp.client.session.ClientSession 类型的会话对象,也需要调用以上方法进行记录

在与每个 MCP server 建立连接后,获取到的当前 MCP server 支持的工具信息,记录工具名称及对应的会话对象到 tool_session_map,并记录工具到 available_tools 中

在调用 OpenAI 格式的 Chat Completions API 时,tools 使用以上 available_tools;获取到大模型返回后,遍历大模型选择的需要执行的工具,对于每个工具,根据工具名称从 tool_session_map 中获取对应的会话对象,使用会话对象执行 MCP server 对应的工具

本文章已经生成可运行项目
虽然给定引用中未直接提供Python实现MCP客户端和服务器的代码,但可以基于MCP基本概念给出一个简单示例代码。 ### MCP服务器代码示例 ```python import socket # 创建一个TCP/IP套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定地址和端口 server_address = ('localhost', 8888) server_socket.bind(server_address) # 监听连接 server_socket.listen(1) print('MCP服务器正在监听 {}:{}'.format(*server_address)) while True: # 等待连接 print('等待客户端连接...') connection, client_address = server_socket.accept() try: print('客户端连接来自:', client_address) # 接收数据 data = connection.recv(1024) print('收到数据:', data.decode()) # 发送响应 response = '服务器已收到数据' connection.sendall(response.encode()) finally: # 关闭连接 connection.close() ``` ### MCP客户端代码示例 ```python import socket # 创建一个TCP/IP套接字 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 服务器地址和端口 server_address = ('localhost', 8888) # 连接服务器 client_socket.connect(server_address) try: # 发送数据 message = '这是来自客户端的消息' client_socket.sendall(message.encode()) # 接收响应 data = client_socket.recv(1024) print('收到服务器响应:', data.decode()) finally: # 关闭连接 client_socket.close() ``` 上述代码是一个非常基础的示例,仅实现了简单的客户端和服务器通信。在实际的MCP应用中,需要根据MCP协议的具体要求来实现更多功能,比如标准化模型上下文协议等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值