AI项目博客
记录 AI 工程实践与大模型应用

Spring AI Alibaba 17|Tools 工具 · 在 ReactAgent 中使用工具:React Agent 远程 MCP 工具调用示例

在实际的 React Agent 应用中,我们多数情况需要接入三方平台或其他应用所提供的 MCP 工具,且目前已经有众多的 MCP 工具平台,大多数工具不需要自己手动定义,你可以通过构建 MCP Client 来使用远端 MCP Server 平台(ModelScope、百炼等)提供的工具。-

1. 基于 Spring Boot 的自动发现构建 MCP Client 为 React Agent 提供工具

在使用这种方式前,你必须引入以下依赖:

<dependencies>
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-starter-mcp-client</artifactId>
		</dependency>
    </dependencies>

并且注入相关 MCP 的配置项:

spring:
  ai:
    mcp: # 结合 Spring AI 使用 MCP 的必须配置
      client: 
        enabled: true # 启用 Spring AI MCP Client
        name: saa-mcp-client
        toolcallback:
          enabled: true
        type: async
        streamable-http: # 定义 streamableHttp 的 MCP 工具
          connections:
            amap-maps:
              url: ${MODEL_SCOPE_AMAP_BASE_URL} # 从魔搭社区获取调用 url,移除尾部 endpoint,例如: https://mcp.api-inference.modelscope.net/?????????/
              endpoint: mcp
        sse: # 定义基于 sse 的 MCP 工具
          connections:
            12306-mcp:
              url: ${MODEL_SCOPE_12306_BASE_URL}  # 从魔搭社区获取调用 url,移除尾部 endpoint,例如: https://mcp.api-inference.modelscope.net/?????????/
              sse-endpoint: sse

在配置完成后,你可以很容易地在 React Agent 中使用这些远程的 MCP 工具。

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.graph.NodeOutput;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.Builder;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import com.alibaba.fastjson2.JSON;
import org.jspecify.annotations.NonNull;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

@Service
public class RemoteMcpToolsExample {

  @Autowired
  private final ToolCallbackProvider toolCallbackProvider;

  public RemoteMcpToolsExample(ToolCallbackProvider toolCallbackProvider) {this.toolCallbackProvider = toolCallbackProvider;}

  /**
   * 示例 17:基于 Spring Boot 使用远端 MCP 工具 -- React Agent
   */
  public void remoteMcpToolsReactWithSpringBootExample() throws GraphRunnerException {
      DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(System.getenv("AI_DASHSCOPE_API_KEY")).build();
      DashScopeChatModel chatModel = DashScopeChatModel.builder().dashScopeApi(dashScopeApi).build();

      //Get Tools From Spring AI ToolCallbackProvider which the tools config in application.yml
      ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
      System.out.printf("""
                      ==============================Find the tools from spring ToolCallbackProvider==============================
                      %s
                      """,
              JSON.toJSONString(toolCallbacks));
      //Run React Agent With MCP Tools
      Builder builder = ReactAgent.builder()
              .name("travel_planning_assistant")
              .model(chatModel)
              .description("Your Travel Assistant")
              .instruction("You are a helpful assistant with travel route planning and train ticket search.")
              .saver(new MemorySaver());

      //==============================Add Tools==============================
      //set the ToolCallbackProvider
      builder.toolCallbackProviders(toolCallbackProvider);
      //you can also get the ToolCallback[],do some filter or choose that which you want to use in this React Agent Session
      //builder.tools(toolCallbacks);
      //==============================Add Tools==============================

      ReactAgent agent = builder.build();

      RunnableConfig config = RunnableConfig.builder()
              .threadId("travel_planning_session")
              .build();

      //stream
      Flux<NodeOutput> stream = agent.stream("""
              I plan to travel from Shanghai to Beijing tomorrow.
              1. Please check at which stations I can alight (i.e., the available arrival/drop-off stations) for my journey.
              2. Please check the available train numbers and their departure times.
              3. Please also check Beijing’s weather forecast for tomorrow.
              """, config);
      StringBuffer answerString = new StringBuffer();
      stream.doOnNext(output -> {
                  if (output.node().equals("_AGENT_MODEL_")) {
                      answerString.append(((StreamingOutput<?>) output).message().getText());
                  }
                  else if (output.node().equals("_AGENT_TOOL_")) {
                      answerString.append("
Tool Call:").append(((ToolResponseMessage) ((StreamingOutput<?>) output).message()).getResponses().get(0)).append("
");
                  }
              })
              .doOnComplete(() -> System.out.println(answerString))
              .doOnError(e -> System.err.println("Stream Processing Error: " + e.getMessage()))
              .blockLast();
  }
}

2. 使用 MCP SDK 为 React Agent 提供工具

如果项目不依赖于 Spring Boot 脚手架,你也可以通过如下方式自行构建可供 React Agent 使用的工具。

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.graph.NodeOutput;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.Builder;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapper;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import reactor.core.publisher.Flux;

import java.net.http.HttpClient;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class RemoteMcpToolsExample {
  private static final Logger log = LoggerFactory.getLogger(RemoteMcpToolsExample.class);

  /**
   * 示例 18:解耦 Spring Boot 使用远端 MCP 工具 -- React Agent
   * <p>
   * 不使用 Spring 依赖注入的情况下,直接使用 MCP 客户端
   * 获取远程工具,并将其转换为 Spring AI 的 ToolCallback,最后在 ReactAgent 中使用。</p>
   *
   * <p>关键步骤:</p>
   * <ol>
   *   <li>创建 MCP 客户端传输层 (HttpClientSseClientTransport)</li>
   *   <li>构建并初始化 MCP 同步客户端</li>
   *   <li>调用 listTools() 获取远程服务器的工具列表</li>
   *   <li>将 MCP 工具转换为 Spring AI 的 ToolCallback</li>
   * </ol>
   */
  public void remoteMcpToolsReactWithoutSpringBootExample() throws GraphRunnerException {
      DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(System.getenv("AI_DASHSCOPE_API_KEY")).build();

      DashScopeChatModel chatModel = DashScopeChatModel.builder().dashScopeApi(dashScopeApi).build();

      //  Get Remote MCP Server Endpoint Configuration
      String modelScope12306BaseUrlSse = System.getenv("MODEL_SCOPE_12306_BASE_URL");
      String modelScopeAmapBaseUrlSse = System.getenv("MODEL_SCOPE_AMAP_BASE_URL");

      //  Create HTTP Client Builder (Reusable)
      HttpClient.Builder httpBuilder = HttpClient.newBuilder()
              .version(HttpClient.Version.HTTP_1_1)
              .connectTimeout(Duration.ofSeconds(60));

      List<ToolCallback> toolCallbacks = new ArrayList<>();
      List<McpSyncClient> clientsToClose = new ArrayList<>();

      try {
          // get MCP Tools
          List<ToolCallback> tools12306 = fetchMcpTools(modelScope12306BaseUrlSse, "sse", httpBuilder, clientsToClose, "12306", false);
          toolCallbacks.addAll(tools12306);

          List<ToolCallback> toolsAmap = fetchMcpTools(modelScopeAmapBaseUrlSse, "mcp", httpBuilder, clientsToClose, "amap", true);
          toolCallbacks.addAll(toolsAmap);

          //...... also you can build these toolCallback into a ToolCallbackProvider ......
          System.out.printf("""
                          ==============================Find the tools from MCP Servers==============================
                          Found %d Tools From MCP Servers
                          """,
                  toolCallbacks.size());

          //Run React Agent With MCP Tools
          Builder builder = ReactAgent.builder().name("travel_planning_assistant").model(chatModel).description("Your Travel Assistant").instruction("You are a helpful assistant with travel route planning, train ticket search, and map services.").saver(new MemorySaver());
          builder.tools(toolCallbacks);

          ReactAgent agent = builder.build();

          RunnableConfig config = RunnableConfig.builder()
                  .threadId("travel_planning_session_no_spring")
                  .build();

          //stream
          Flux<NodeOutput> stream = agent.stream("""
                  I plan to travel from Shanghai to Beijing tomorrow.
                  1. Please check at which stations I can alight (i.e., the available arrival/drop-off stations) for my journey.
                  2. Please check the available train numbers and their departure times.
                  3. Please also check Beijing's weather forecast for tomorrow.
                  4. Please help me find the route from Beijing South Station to Tiananmen Square.
                  """, config);
          StringBuffer answerString = new StringBuffer();
          stream.doOnNext(output -> {
                      if (output.node().equals("_AGENT_MODEL_")) {
                          answerString.append(((StreamingOutput<?>) output).message().getText());
                      }
                      else if (output.node().equals("_AGENT_TOOL_")) {
                          answerString.append("
Tool Call:").append(((ToolResponseMessage) ((StreamingOutput<?>) output).message()).getResponses().get(0)).append("
");
                      }
                  })
                  .doOnComplete(() -> System.out.println(answerString))
                  .doOnError(e -> System.err.println("Stream Processing Error: " + e.getMessage()))
                  .blockLast();
      } catch (Exception e) {
          log.error("execute MCP Agent error", e);
      } finally {
          //close all MCP client
          for (McpSyncClient client : clientsToClose) {
              try {
                  if (client != null) {
                      client.close();
                      log.info("MCP Client Is Closed");
                  }
              } catch (Exception e) {
                  log.warn("Close MCP Client Error", e);
              }
          }
      }
  }

  /**
   * 从指定的 MCP 服务器获取工具并转换为 ToolCallback 列表
   *
   * @param baseUrl        MCP 服务器的基础 URL
   * @param endpoint       SSE 端点路径(如 "sse")
   * @param httpBuilder    HTTP 客户端构建器
   * @param clientsToClose 需要关闭的客户端列表(用于资源管理)
   * @param serverName     服务器名称(用于日志和工具名称前缀)
   *
   * @return ToolCallback 列表
   */
  private List<ToolCallback> fetchMcpTools(String baseUrl,String endpoint,HttpClient.Builder httpBuilder,
          List<McpSyncClient> clientsToClose,String serverName,boolean isStreamable) {

      List<ToolCallback> toolCallbacks = new ArrayList<>();
      McpSyncClient mcpClient;

      try {
          //...... build mcp client ......

          //register to ToolCallback
          for (McpSchema.Tool mcpTool : mcpTools) {
              log.info("[{}] Register MCP Tool: name={}, description={}",
                      serverName, mcpTool.name(), mcpTool.description());
              ToolCallback toolCallback = createToolCallback(mcpTool, mcpClient, serverName);
              toolCallbacks.add(toolCallback);
          }
      } catch (Exception e) {
          log.error("[{}] Fetch MCP Tools Error", serverName, e);
      }
      return toolCallbacks;
  }

  /** Convert MCP Tools to ToolCallback */
  private ToolCallback createToolCallback(McpSchema.Tool mcpTool, McpSyncClient mcpClient, String serverName) {
      return FunctionToolCallback.builder(
                      mcpTool.name(),
                      (Map<String, Object> functionInput) -> {
                          //...... the true mcp call ......
                      })
              .description(mcpTool.description())
              .inputType(Map.class)
              .build();
  }
}

3. 为 ChatClient 提供 MCP 工具

结合示例 1 和示例 2,你可以通过 Spring Boot 或 任意一种可以构建 ToolCallbackProvider 的方式来为 ChatClient 提供 MCP 工具。

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.fastjson2.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RemoteMcpToolsExample {

  private static final Logger log = LoggerFactory.getLogger(RemoteMcpToolsExample.class);
  @Autowired
  private final ToolCallbackProvider toolCallbackProvider;

  public RemoteMcpToolsExample(ToolCallbackProvider toolCallbackProvider) {this.toolCallbackProvider = toolCallbackProvider;}

  /**
   * 示例 19:基于 Spring Boot 使用远端 MCP 工具 -- Only ChatClient
   */
  public void remoteMcpToolsWithChatCliAndSpringBootExample() {
      DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(System.getenv("AI_DASHSCOPE_API_KEY")).build();
      DashScopeChatModel chatModel = DashScopeChatModel.builder().dashScopeApi(dashScopeApi).build();
      ChatClient chatClient = ChatClient.builder(chatModel).build();
      //Get Tools From Spring AI ToolCallbackProvider which the tools config in application.yml
      ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
      System.out.printf("""
                      ==============================Find the tools from spring ToolCallbackProvider==============================
                      %s
                      """,
              JSON.toJSONString(toolCallbacks));
      ChatClient.ChatClientRequestSpec doChat =
              chatClient.prompt("You are a helpful assistant with travel route planning and train ticket search.")
                      .user("""
                              I plan to travel from Shanghai to Beijing tomorrow.
                              1. Please check at which stations I can alight (i.e., the available arrival/drop-off stations) for my journey.
                              2. Please check the available train numbers and their departure times.
                              3. Please also check Beijing’s weather forecast for tomorrow.
                              """)
                      .toolCallbacks(toolCallbackProvider);
      //check the logs from DefaultToolCallingManager
      String text = doChat.call().chatResponse().getResult().getOutput().getText();
      System.out.println(text);
  }
}
  • Agents 文档 – 了解如何在 Agent 中使用工具
  • Messages 文档 – 了解工具消息类型
  • Models 文档 – 了解模型如何调用工具

本文整理自 Spring AI Alibaba 官方文档《Tools 工具》:https://java2ai.com/docs/frameworks/agent-framework/tutorials/tools

赞(0)
未经允许不得转载:AI项目博客 » Spring AI Alibaba 17|Tools 工具 · 在 ReactAgent 中使用工具:React Agent 远程 MCP 工具调用示例

评论 抢沙发

AI项目博客

专注 AI 工程化落地 —— 覆盖智能体、大模型、Java AI 与 Python 的学习笔记,以及 AI 平台的项目实战记录。

关于本站文章归档

登录

找回密码

注册