TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

让大模型通过 MCP 客户端调用工具

让 MCP 客户端接上大语言模型:把服务器的工具转换成模型能理解的格式,用户用自然语言提问,由模型决定调用哪个工具。

这篇适合谁、读完能做到什么

你已经能写一个 MCP 客户端,会列出服务器的工具并手动调用。接下来想做的是让用户直接用一句话提问,由模型判断该调用哪个工具。读完这篇,你应该能把服务器的工具转换成模型可用的格式,并写出一个从用户问题到工具调用的完整流程。[1]

为什么要给客户端接上大语言模型

出发点是:让用户自己去调用 MCP 的工具并不实用。用户期待的是用自然语言交流,并不关心能力是不是通过 MCP 存放的。解决办法就是在客户端里加一个大语言模型。[1] 学习目标有三个:用大模型创建客户端,通过模型与 MCP 服务器交互,并让客户端的最终用户体验更好。[1]

整体流程

把流程分成四步:

  1. 连接服务器。
  2. 列出服务器的能力,包括提示词、资源和工具,并保存它们的结构(schema)。
  3. 加入大语言模型,把保存下来的能力和结构转换成模型能理解的格式交给它。
  4. 处理用户的提问:把提问和客户端列出的工具一起交给模型。[1]

前两步在连接服务器时做一次,后两步则在每次用户提问时进行,所以列出和转换工具的代码应该放在连接之后、处理提问之前。[1]

准备:模型服务和环境变量

练习使用 Microsoft Foundry。步骤是创建一个 Foundry 资源,部署一个可用的模型,例如 gpt-5.1,然后设置三个环境变量:AZURE_OPENAI_ENDPOINT、AZURE_OPENAI_API_KEY 和 AZURE_OPENAI_DEPLOYMENT。[1]

特别提醒两点。第一,GitHub Models 已在 2026 年 7 月 30 日退役,所以不能再沿用它。第二,API 调用里使用的是部署名称,它可能和底层模型名称不同。选模型之前,还要查看 Microsoft Foundry 的模型退役时间表。[1]

第一步:连接服务器

TypeScript 版在类的构造函数里创建 OpenAI 客户端,baseURL 由端点加上 /openai/v1/ 组成;如果 AZURE_OPENAI_ENDPOINT 或 AZURE_OPENAI_API_KEY 没有设置,构造函数会直接抛出错误。同一个类里还有一个 MCP Client,名称是 example-client。[1]

Python 版的导入和连接方式与前面的客户端相同:从 mcp 导入 ClientSession、StdioServerParameters 和 types,从 mcp.client.stdio 导入 stdio_client,服务器的启动命令是 mcp run server.py。[1]

第二步:列出能力并保存结构

Python 版列出资源和工具,对每个工具打印名称和 inputSchema 中的 properties。工具的结构要保存下来,后面才能交给模型:

# List available resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
    print("Resource: ", resource)

# List available tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
    print("Tool: ", tool.name)
    print("Tool", tool.inputSchema["properties"])

TypeScript 版用 this.client.listTools() 拿到工具列表,每个工具的 inputSchema 作为 input_schema 传给转换函数。[1]

第三步:把工具转换成模型能理解的格式

大模型需要的是 OpenAI 风格的函数工具定义。TypeScript 版的 openAiToolAdapter 返回一个 type 为 function 的对象,参数的 properties 和 required 取自工具的 input_schema:

    return {
        type: "function" as const, // Explicitly set type to "function"
        function: {
        name: tool.name,
        description: tool.description,
        parameters: {
        type: "object",
        properties: tool.input_schema.properties,
        required: tool.input_schema.required,
        },
        },
    };

Python 版用 convert_to_llm_tool 完成同样的事,它把 MCP 工具转换成一个字典,parameters 的 properties 同样来自 inputSchema:

def convert_to_llm_tool(tool):
    tool_schema = {
        "type": "function",
        "function": {
            "name": tool.name,
            "description": tool.description,
            "type": "function",
            "parameters": {
                "type": "object",
                "properties": tool.inputSchema["properties"]
            }
        }
    }

    return tool_schema

转换完成后,Python 版把所有工具收集到 functions 列表里:

functions = []
for tool in tools.tools:
    print("Tool: ", tool.name)
    print("Tool", tool.inputSchema["properties"])
    functions.append(convert_to_llm_tool(tool))

第四步:处理用户的提问

TypeScript 版的 run 方法把用户消息 What is the sum of 2 and 3? 交给模型。模型名取自环境变量 AZURE_OPENAI_DEPLOYMENT,没有设置时用 gpt-5.1,max_completion_tokens 设为 1000,转换好的工具通过 tools 传入:

        let response = this.openai.chat.completions.create({
            model: process.env.AZURE_OPENAI_DEPLOYMENT ?? "gpt-5.1",
            max_completion_tokens: 1000,
            messages,
            tools: tools,
        });

模型返回的工具调用由 callTools 处理。它读出每个调用的名称和参数,再通过 MCP 客户端执行:

        // 2. Call the server's tool 
        const toolResult = await this.client.callTool({
            name: toolName,
            arguments: JSON.parse(args),
        });

Python 版把模型调用封装在 call_llm 里。它用 AZURE_OPENAI_ENDPOINT 和 AZURE_OPENAI_API_KEY 创建 OpenAI 客户端,把转换好的函数作为 tools 传入:

def call_llm(prompt, functions):
    client = OpenAI(
        base_url=(
            f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/"
        ),
        api_key=os.environ["AZURE_OPENAI_API_KEY"],
    )

    print("CALLING LLM")
    response = client.chat.completions.create(
        messages=[
            {
            "role": "system",
            "content": "You are a helpful assistant.",
            },
            {
            "role": "user",
            "content": prompt,
            },
        ],
        model=os.getenv("AZURE_OPENAI_DEPLOYMENT", "gpt-5.1"),
        tools=functions,
        max_completion_tokens=1000,
    )

函数的后半部分读取模型给出的工具调用,把参数用 json.loads 解析,返回 name 和 args 组成的列表:

    response_message = response.choices[0].message

    functions_to_call = []

    if response_message.tool_calls:
        for tool_call in response_message.tool_calls:
            print("TOOL: ", tool_call)
            name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)
            functions_to_call.append({ "name": name, "args": args })

    return functions_to_call

主程序用一个提问调用模型,然后逐个执行建议的函数:

prompt = "Add 2 to 20"

# ask LLM what tools to all, if any
functions_to_call = call_llm(prompt, functions)

# call suggested functions
for f in functions_to_call:
    result = await session.call_tool(f["name"], arguments=f["args"])
    print("TOOLS result: ", result.content)

这段代码的作用是:用 session.call_tool 调用模型选出的函数,并打印结果。[1]

常见问题和限制

  • 工具结果还没有交回模型。 TypeScript 片段在处理工具结果的地方只留了 TODO 注释,Python 片段打印结果后就结束了。要得到模型最终的自然语言回答,需要把工具结果再作为消息交给模型,这几段代码没有展示这一步。[1]
  • 模型返回的参数要解析。 工具调用的参数是字符串,TypeScript 用 JSON.parse,Python 用 json.loads 解析后才能传给工具。[1]
  • 自定义模型名要看端点。 Java 示例里,显式设置的 MINIMAX_MODEL_ID 会原样传下去,包括旧版或自定义的模型名,能不能用取决于所连的端点。不设置 OPENAI_BASE_URL 时,由 MINIMAX_REGION 决定地址:cn_zh 对应 https://api.minimaxi.com/v1,global_en 对应 https://api.minimax.io/v1。[1]
  • 注意 SDK 和传输方式的版本。 提醒,Java 示例用的是旧的 HTTP+SSE 传输,针对 2025-11-25 版的 SDK;新的远程客户端应使用兼容 2026-07-28 的 SDK 和 Streamable HTTP。[1]

其他语言怎么做

.NET 版把 MCP 工具的 properties 取出来,用 ChatTool.CreateFunctionTool 生成 OpenAI 的工具定义。模型返回 ToolCalls 后,循环读取每个调用的 FunctionName 和 FunctionArguments,再交给 mcpClient.CallToolAsync 执行。[1]

Rust 版没有官方的 OpenAI 库,使用的是社区维护的 async-openai 库。[1]

Java 版使用 LangChain4j。McpToolProvider 自动发现 MCP 客户端的工具,AiServices 把聊天模型和工具绑在一起,工具结构的转换和函数调用由框架处理。示例中的模型服务是 MiniMax,默认模型是 MiniMax-M3.1-Flash,另一个当前模型 MiniMax-M3。环境变量 MINIMAX_REGION 可以选择 global_en 或 cn_zh,后者对应 https://api.minimaxi.com/v1。想显式指定端点和模型时,可以这样设置:

export OPENAI_API_KEY=your_minimax_api_key_here
export OPENAI_BASE_URL=https://api.minimax.io/v1
export MINIMAX_MODEL_ID=MiniMax-M3.1-Flash

这段命令把端点设为海外地址。如果想按地区选择,做法是不设置 OPENAI_BASE_URL,改设 MINIMAX_REGION。[1]

下一步

这个客户端能把自然语言变成工具调用,接下来可以补上工具结果的回传,让模型给出最终回答,也可以把 Python 版的主程序改成交互式的循环。 Java 示例还演示了用一个 Bot 接口处理多轮自然语言请求,适合做成更完整的应用。[1]

如果想让普通用户直接使用,可以在 Python 版的主程序外面加一层循环:读入用户的一句话,交给 call_llm 选出函数,执行后把结果显示出来。这是在代码之上的延伸,没有给出这段代码,本文也不展示。[1]

本文改编自 Microsoft 开源课程 *mcp-for-beginners* 入门部分第 3 课《Creating a client with LLM》[1],原文以 MIT 许可证发布。

来源

  1. 1
    Creating a client with LLMMicrosoft mcp-for-beginners (MIT)

引用本页: 让大模型通过 MCP 客户端调用工具. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-llm-client. 更新于 2026-11-14

相关动态