MCP - AI智能体调用 MCP Serverr - Stdio(六)

之前展示的都是手写一个 client,然后手工调用 MCP tools。没有使用到任何大模型,下文将展示如何通过大模型调用 MCP tools。

同样调用 MCP Server 有两种方式:第一,stdio。第二,Streamable HTTP。后文展示的是通过 stdio 模式。

一、同样需要将 server.py 代码改为 stdio 模式

将 mcp.run(transport="http", port=3002, host="0.0.0.0", path="/mcp_atlas") 改为 mcp.run()

修改后如下图

二、在 test 目录中添加 agent_stdio.py 文件

并添加如下代码:

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

# 加载 .env 文件
load_dotenv()


class MCPClient:
    def __init__(self):
        """初始化 MCP 客户端"""
        self.exit_stack = AsyncExitStack()
        self.api_key = os.getenv("API_KEY")  # 读取 OpenAI API Key
        self.base_url = os.getenv("BASE_URL")  # 读取 BASE URL
        self.model = os.getenv("MODEL")  # 读取 model

        if not self.api_key:
            raise ValueError("未找到 API KEY. 请在 .env 文件中配置 API_KEY")

        self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)

    async def process_query(self, query: str) -> str:
        """
        调用大模型处理用户查询并根据返回的 tools 列表调用对应工具
        """
        messages = [{"role": "user", "content": query}]

        # 获取工具列表
        response = await self.session.list_tools()
        available_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema
            }
        } for tool in response.tools]
        print('服务端工具列表', available_tools)

        # 请求 OpenAI 模型处理
        response = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            tools=available_tools
        )

        # 处理返回的内容
        content = response.choices[0]
        if content.finish_reason == "tool_calls":
            # 执行工具调用
            tool_call = content.message.tool_calls[0]
            tool_name = tool_call.function.name
            tool_args = json.loads(tool_call.function.arguments)

            # 执行工具
            result = await self.session.call_tool(tool_name, tool_args)
            print(f"\n\n[Calling tool {tool_name} with args {tool_args}]\n\n")

            # 将模型返回的原始消息和工具执行的结果都添加到 messages 中
            messages.append(content.message.model_dump())
            messages.append({
                "role": "tool",
                "content": result.content[0].text,
                "tool_call_id": tool_call.id,
            })

            # 将上面的结果再返回给大模型生产最终的结果
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
            )
            return response.choices[0].message.content

        return content.message.content

    async def chat_loop(self):
        """运行交互式聊天循环"""
        print("MCP 客户端已启动!输入 'exit' 退出")

        while True:
            try:
                query = input("问: ").strip()
                if query.lower() == 'exit':
                    break

                response = await self.process_query(query)
                print(f"AI回复: {response}")

            except Exception as e:
                print(f"发生错误: {str(e)}")

    async def clean(self):
        """清理资源"""
        await self.exit_stack.aclose()

    async def connect_to_server(self, server_script_path: str):
        """
        连接到 MCP 服务器
        """
        is_python = server_script_path.endswith('.py')
        is_js = server_script_path.endswith('.js')
        if not (is_python or is_js):
            raise ValueError("不支持的文件类型")

        command = "python" if is_python else "node"
        server_params = StdioServerParameters(command=command,
                                              args=[server_script_path],
                                              env=None)

        # 启动 MCP 服务器并建立通信
        stdio_transport = await self.exit_stack.enter_async_context(
            stdio_client(server_params))
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(
            ClientSession(self.stdio, self.write))

        await self.session.initialize()

    async def list_tools(self):
        """列出所有工具"""
        # 列出 MCP 服务器上的工具
        response = await self.session.list_tools()
        tools = response.tools
        print("已连接到服务器,server 支持以下工具:", [tool.name for tool in tools])


async def main():
    # 启动并初始化 MCP 客户端
    client = MCPClient()
    try:
        # 连接到 MCP 服务器
        await client.connect_to_server('../server.py')
        # 列出 MCP 服务器上的工具
        await client.list_tools()
        # 运行交互式聊天循环,处理用户对话
        await client.chat_loop()
    finally:
        # 清理资源
        await client.clean()


if __name__ == "__main__":
    asyncio.run(main())

注意如果你的 MCP server 文件位置不同,需要修改。

三、tools 修改

找到 math_tools.py 文件,增加参数说明,修改后代码如下:

# -*- coding: utf-8 -*-
import random
from base.tool_registry import mcp_tool

@mcp_tool()
def add_numbers(a: int, b: int) -> int:
    """
    两个数字相加
    
    Args:
        a (int): 参数1
        b (int): 参数2

    Returns:
        a、b两数之和
    """
    return a + b

由于之前代码没写参数说明,所以有些模型返回 tools 调用时没带 参数

四、添加 .env

在根目录添加 .env 文件(注意文件名 env 前要有 .),内容如下:

API_KEY= xxx # 根据你的配置修改
BASE_URL=xxx # 根据你的配置修改
MODEL=xxx # 根据你的配置修改

五、效果展示

执行如下命令启动 Agent(同样需要先运行 pytho3.1 的虚拟环境,运行方法参见之前文章)

python agent_stdio.py

运行后如下图

用户输入 3+2= 后可以看到如下图结果:

 

 

源代码:  提取码: rsak

posted @ 2025-10-28 13:49  rslai  阅读(32)  评论(0)    收藏  举报