引言

在上一篇文章中,我们解析了一个关于天气实时预报的MCP server,相信大家对这一块应该有不错的体感了。那么我们这一篇就会来讲解一下MCP 客户端的一些问题和配置,如果篇幅合适的话还会讲MCP的一些高级特性

如果你对前面的内容感兴趣,可以点击这里跳转

MCP (Model Context Protocol) 技术理解 - 第一篇

MCP (Model Context Protocol) 技术理解 - 第二篇

MCP (Model Context Protocol) 技术理解 - 第三篇

MCP (Model Context Protocol) 技术理解 - 第四篇

MCP (Model Context Protocol) 技术理解 - 第五篇

如何调用MCP server

很多人可能有这个误区,应该是LLM直接发出指令来调用对应的MCP server的吧,但是实际上这个想法是错误的,根本原因在于LLM没有外调的能力,即不能直接调用接口。

那么一般调用MCP server会有两种情况,但是无论是什么情况,我们都是AI应用通过MCP client来调用MCP server,LLM只负责推理输入和输出,下面我们来讲讲这两种情况,然后我们再仔细地讲讲MCP client配置。

  • 安全隔离: 这意味着你可以在 Client 层前做拦截。如果 LLM 发疯了,想执行 rm -rf /,你的 Java/Go 代码可以在发送给 Server 之前检测并拦截这个请求。

  • 协议转换: LLM 输出的是文本/Token,MCP Server 需要的是 JSON-RPC 消息。你的 Client 就是这个 Adapter (适配器)

场景一:作为用户在现有host中调用

如果你只是想让 Claude code 或 Cursor 等AI应用调用一个现有的 MCP Server或者你自己编写的MCP server,你不需要写MCP client代码,只需要修改配置文件。

在这种情况,host就是AI应用,已经内置写好的MCP client代码了,你配置好MCP server即可调用,会自动维护连接。

场景二:作为开发者通过代码调用

如果你想在自己的 Java 或 Go 程序中调用一个 MCP Server(例如,构建一个能调用本地工具的 AI Agent),你需要实现一个 MCP Client。

核心交互流程 (以 Stdio 为例)

  1. 启动进程: 你的程序(Client)启动 MCP Server(如 npx -y @modelcontextprotocol/server-filesystem)作为子进程。
  2. 握手 (Initialization) : 发送 initialize 请求,协商协议版本和能力。
  3. 发送指令: 发送 tools/list, tools/call, resources/read 等 JSON-RPC 消息

所以在这种场景下,你需要自己编写MCP client进行初始化链接,维护相关协议和连接,发送消息和处理响应等等。

一次MCP调用示例

那么大家在看完上面的示例可能还有疑惑,那么LLM到底是干嘛的?别急,我们上面说了,AI应用通过MCP client来调用MCP server,LLM只负责推理输入和输出,下面我来给大家解析一遍完整的调用链,大家一看便知!

我们先强调各个角色吧:

AI应用:负责管控全局,协调各个请求和负载均衡

MCP client:负责连接 Server,管理进程,维护对话上下文

MCP server:它是无状态的 Worker。它不知道上下文,不知道是谁在问,只负责接收指令 -> 干活 -> 返回结果

LLM:它没有手脚(不能联网、不能读文件)。它负责分析用户的自然语言,对比 AI应用 给它的“Tools list”,然后生成一个结构化的 JSON 文本,告诉 AI应用:“请帮我调用这个函数,参数是 X 和 Y”

以下是一次完整的交互流程:

  • 初始化 (Handshake) :

    • Client 启动 Server
    • Client 询问 Server: “你有什么本事?(Tools List)”
    • Server 回答: “我会读文件 (fs.read),我会查数据库 (db.query)。”
  • 用户提问:

    • 用户对 Client 说: “帮我查一下 user_id=101 的订单。”
  • Prompt 组装 (AI应用 内部逻辑) :

    • AI应用 将用户的 System Prompt + Server 提供的工具定义 (Schema) + 用户问题,打包发给 LLM
  • LLM 决策:

    • LLM 经过推理,发现 db.query 工具能解决这个问题。

    • LLM 返回给 AI应用 (而不是直接给 Server):

      { 
        "tool_use": "db.query", 
        "params": { 
              "sql": "SELECT * FROM orders WHERE user_id=101" 
                  } 
      }
      
  • 执行 (真正的 MCP 调用发生在这里) :

    • AI应用 解析 LLM 的响应,发现它想调用工具,控制MCP client发送相关请求和数据。
    • Client 通过 JSON-RPC (Stdio/SSE) 向 MCP Server 发送 tools/call 请求。
  • Server 响应:

    • Server 执行 SQL,返回 JSON 数据给 Client
  • 回环 (Loop Back) :

    • AI应用 接收到client返回的数据,将 Server 的执行结果再次喂给 LLM
    • AI应用 说: “刚才你让我调用的工具,结果是 [Order A, Order B],请生成给用户的最终回复。”
  • 最终输出:

    • LLM 生成自然语言: “用户你好,ID 为 101 的用户有两笔订单…”

在AI应用里面配置MCP server

ok,我相信通过上面的解析,大家已经对整个调用过程是非常清晰的了,接下来我再按场景逐渐解析MCP client的问题。

在上面的分析中,我们已经分析了在Claude code里面,我们只需要进行MCP server的配置即可,而不需要编写MCP client的相关代码,下面我们以Claude code为例子,讲讲如何配置MCP server。

我们可以在claude_desktop_config.json的文件中进行server的配置

// ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/weather",
        "run",
        "weather-server"
      ]
    },
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Documents"
      ]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token_here"
      }
    }
  }
}

你可以把每一项配置看作是在终端执行一个长连接的子进程。以下是详细的参数拆解:

1.mcpServers:根节点,是一个 Map。Key(如 weather, github)是该 Server 的唯一标识符,用于在 AI 界面显示和内部隔离。

2.command (可执行文件)

这是 Client 尝试启动的二进制文件或脚本解释器

  • 作用: 指定入口程序。
  • 常见值: npx (Node.js), python, uv (Python 包管理器), 或者你编译好的 Go/Java 二进制文件(如 /usr/local/bin/my-go-server)。
  1. args (启动参数)

这是一个字符串数组,会被追加在 command 后面,形成完整的 shell 启动命令。

  • weather 示例分析:

    • uv --directory /path/to/weather run weather-server
    • 这告诉 uv 切换到特定目录并运行该项目。
  • filesystem 示例分析:

    • npx -y @modelcontextprotocol/server-filesystem /Users/username/Documents
    • -y: 自动确认安装。
    • 最后一个参数 /Users/...: 这是传给 Server 程序内部的参数,告知它允许访问哪个文件夹(白名单机制)。
  1. env (环境变量)

这是子进程启动时注入的 Environment Variables

  • 作用: 用于传递敏感信息(API Keys)或配置参数,而不需要将其硬编码在代码或命令行参数中。
  • 后端视角: 就像你在 Docker 中配置 ENV 或在本地 .env 文件中定义变量一样。
  • GitHub 示例: GITHUB_PERSONAL_ACCESS_TOKEN 会被注入到该进程的上下文中,Server 代码中通过 os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN") (Go) 或 System.getenv(...) (Java) 获取。

通过代码来调用MCP server

这就是我们上面说的场景二,在自己构建的AI应用中,我们需要使用官方提供的SDK来编写MCP client的相关代码。

这里多说一句,官方提供了许多SDK,其中后端开发常用的Go是由Google和Go官方来维护,Java则是Spring AI官方维护,而Python则是MCP官方进行维护

在这里插入图片描述

下面是使用Python SDK来编写MCP client相关代码

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

# 配置服务器参数
server_params = StdioServerParameters(
    command="uv",
    args=["--directory", "/path/to/weather", "run", "weather-server"],
)

async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化连接
            await session.initialize()
            
            # 列出可用工具
            tools = await session.list_tools()
            print("Available tools:", tools)
            
            # 调用工具
            result = await session.call_tool(
                "get_forecast",
                arguments={"latitude": 37.7749, "longitude": -122.4194}
            )
            print("Forecast:", result)
            
            # 列出资源
            resources = await session.list_resources()
            print("Available resources:", resources)
            
            # 读取资源
            config = await session.read_resource("weather://config")
            print("Config:", config)

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

总结

这一篇我们解答了MCP client的一些问题,包括client如何调用server,还有不同场景下我们该如何调用server等等,我相信通过这一篇和前面的文章,大家也已经对MCP比较了解了,下一篇我们再讲讲MCP的高级特性和一些需要注意的点

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐