上文:Spring AI 与 Spring Alibaba AI 构建智能应用


背景

    随着AI全面化发展,spring对ai的支持也覆盖,以下为远程调用mcp服务以及自建mcp服务本地调用案例,仅供参考~

准备工作

名称

要求

备注

环境准备

创建docker环境

安装cursor

https://www.cursor.com/cn

概念了解

https://modelcontextprotocol.io/introduction#general-architecture?login=from_csdn

先了解

mcp介绍

https://mcp.programnotes.cn/zh/docs/ai-mcp-awsome-server

mcp服务平台

https://mcp.so/zh/servers

查找服务

远程mcp调用(客户端)

说明:通过接入高德地图的实现mcp远程调用,通过大模型+mcp快速实现应用

准备工作

名称

要求

备注

基础知识

什么是MCP Server?

高德地图mcp及key申请

申请Key

https://lbs.amap.com/api/mcp-server/gettingstarted

jdk

17

必须是17或更高

阿里云百炼AK

申请AK

用个人账号

配置好cursor

https://lbs.amap.com/api/mcp-server/gettingstarted

代码实现

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
    </parent>

    <artifactId>spring-api-mcp-map-client</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-ai.version>1.0.0-M6</spring-ai.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI 依赖 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-mcp</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

        <!-- Model Context Protocol SDK -->
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp-spring-webmvc</artifactId>
            <version>0.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp</artifactId>
            <version>0.9.0</version>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 添加 Spring 里程碑仓库 -->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

注意:api-key 要用自已的,可以根据上面的申请!

server:
port: 18080
spring:
application:
name: spring-ai-demo
#百炼平台
ai:
    openai:
      api-key: 你的百炼key
      chat:
        options:
          model: deepseek-r1
      base-url: https://dashscope.aliyuncs.com/compatible-mode/
    mcp:
      client:
        toolcallback:
          enabled: true
        version: true
        name: spring-ai-mcp-map-client
        type: async
        connection-timeout: 60s

# 调试日志_
logging:
level:
    io:
      modelcontextprotocol:
        client: DEBUG
        spec: DEBUG
        server: DEBUG
package com.springai.demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import org.springframework.ai.mcp.client.autoconfigure.NamedClientMcpTransport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;
import java.util.List;

@Configuration
publicclass McpConfig {

    @Bean
    public List<NamedClientMcpTransport> mcpClientTransport() {
        McpClientTransport transport = HttpClientSseClientTransport
                .builder("https://mcp.amap.com")
                .sseEndpoint("/sse?key=你的key")
                .objectMapper(new ObjectMapper())
                .build();

        return Collections.singletonList(new NamedClientMcpTransport("amap", transport));
    }
    
}
package com.springai.demo.controller;

import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.spec.McpSchema;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/*** 获取 高德天气预报
 **/
@RestController
@RequestMapping("/ai/chat")
@Slf4j
publicclass ChatController {

    @Autowired
    private OpenAiChatModel openAiChatModel;

    @Autowired
    private AsyncMcpToolCallbackProvider asyncMcpToolCallbackProvider;

    @Autowired
    List<McpAsyncClient> mcpAsyncClients;

    @RequestMapping("/test")
    public Mono<McpSchema.CallToolResult> test() {
        var mcpClient = mcpAsyncClients.get(0);

        return mcpClient.listTools()
                .flatMap(tools -> {
                    log.info("tools: {}", tools);

                    return mcpClient.callTool(
                            new McpSchema.CallToolRequest(
                                    "maps_weather",
                                    Map.of("city", "深圳")
                            )
                    );
                });
    }

    @GetMapping(value = "/getWeather", produces = "text/octet-stream;charset=UTF-8")
    public Flux<String> getWeather(@RequestParam (value = "message", defaultValue = "深圳天气") String message) {
        log.info("接收到请求,消息内容: {}", message);

        ToolCallback[] toolCallbacks = asyncMcpToolCallbackProvider.getToolCallbacks();
        log.info("获取到 {} 个 MCP 工具回调", toolCallbacks.length);

        // 打印所有可用工具
        for (ToolCallback tool : toolCallbacks) {
            log.info("可用工具: {} - 描述: {}", tool.getName(), tool.getDescription());
        }

        ChatOptions chatOptions = ToolCallingChatOptions.builder()
                .toolCallbacks(toolCallbacks)
                .build();
        Prompt prompt = new Prompt(message, chatOptions);
        log.info("创建提示: {}", prompt);

        // deepseek模型支持块式调用
        // 流式响应
        Flux<String> responseFlux = openAiChatModel.stream(prompt)
                .map(response -> {
                    String text = (response.getResult() == null || response.getResult().getOutput() == null
                            || response.getResult().getOutput().getText() == null)
                            ? ""
                            : response.getResult().getOutput().getText();

                    log.info("收到响应片段: {}", text);
                    return text;
                });

        return responseFlux;
    }
}

测试地址:http://localhost:18080/ai/chat/getWeather?message=%E6%B7%B1%E5%9C%B3%E5%A4%A9%E6%B0%94

效果不错,不过还是取决于数据源的精准度。

本地MCP调用

        说明:通过service提供mcp服务,clinet调用mcp服务(跟rpc没啥太大区别~)

服务端(service)
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
    </parent>

    <artifactId>spring-ai-mcp-local-server</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M7</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

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

    <repositories>
        <repository>
            <name>Central Portal Snapshots</name>
            <id>central-portal-snapshots</id>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
port: 18080
spring:
application:
name: spring-ai-mcp-local-server
ai:
    mcp:
      connect-timeout: 60000
      response-timeout: 60000
      server:
        name: spring-ai-mcp-local-server
        connection-timeout: 60s
        type: sync
        sse-message-endpoint: /mcp/gzh

# 调试日志_
logging:
level:
    io:
      modelcontextprotocol:
        client: DEBUG
        spec: DEBUG
        server: DEBUG
package com.springai.demo.config;

importcom.springai.demo.service.MyTools;
importorg.springframework.ai.tool.ToolCallbackProvider;
importorg.springframework.ai.tool.method.MethodToolCallbackProvider;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;

/**
 * @version v1.0
 * @ClassName ToolCallConfig
 * @Description: 工具类配置
 */
@Configuration
public class ToolCallConfig {
    @Bean
    public ToolCallbackProvider gzhRecommendTools(MyTools myTools) {
        returnMethodToolCallbackProvider.builder().toolObjects(myTools).build();
    }
}
package com.springai.demo.service;

importorg.springframework.ai.tool.annotation.Tool;
importorg.springframework.context.i18n.LocaleContextHolder;
importorg.springframework.stereotype.Service;

importjava.time.LocalDateTime;

@Service("myTools")
public class MyTools {

    @Tool(name = "我的工具", description = "使用我的工具")
    public String myTool() {
        return "返回: 暂无~";
    }

    @Tool(description = "获取当前用户日期和时间")
    public String getCurrentDateTime() {
        returnLocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }
}
package com.springai.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class McpLocalServiceApplication {

  public static void main(String[] args) {
    SpringApplication.run(McpLocalServiceApplication.class, args);
  }

}

启动服务可以看到注册的服务信息~

客户端(clinet)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
    </parent>

    <artifactId>spring-ai-mcp-local-client</artifactId>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.ai</groupId>-->
<!-- <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
        <!-- 使用的大模型依赖 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-ollama</artifactId>
            <version>1.0.0-M7</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0-M7</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

</dependencyManagement>

    <repositories>
        <repository>
            <name>Central Portal Snapshots</name>
            <id>central-portal-snapshots</id>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
package com.springai.demo.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName ToolController
 * @Description: 入口
 */
@RestController
publicclass ToolController {
    @Autowired
    private OllamaChatModel openAiChatModel;



    @Autowired
    private ToolCallbackProvider toolCallbackProvider;




    @GetMapping("/ai/generate")
    public String generate(@RequestParam(value = "message", defaultValue = "当前时间") String message) {
        ChatClient chatClient = ChatClient.builder(openAiChatModel)
                .defaultTools(toolCallbackProvider.getToolCallbacks())
                .build();
        ChatClient.CallResponseSpec call = chatClient.prompt(message).call();
        return call.content();
    }


}
package com.springai.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class McpLocalClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(McpLocalClientApplication.class, args);
  }

}

注意:不要使用deepseek,不支持工具类,暂不清楚工具类原因或模型原因;

server:
port: 18081
spring:
application:
name: spring-ai-mcp-local-client
ai:
    ollama:
      client:
        connect-timeout: 30s
        read-timeout: 60s
#这里可以改为百炼自已去试吧
      base-url: http://127.0.0.1:11434/
      chat:
        options:
#注意deepseek不支持工具类(大坑)
          #model: deepseek-r1:7b
          model: qwen3:8b
    mcp:
      connect-timeout: 60000
      response-timeout: 60000
      client:
        connection-timeout: 60s
        name: spring-ai-mcp-local-client
# type: sync
        toolcallback:
          enabled: true
#你的本地mcp服务(是不是跟rpc很像)
        sse:
          connections:
            server1:
              url: http://localhost:18080


# 调试日志_
logging:
level:
    io:
      modelcontextprotocol:
        client: DEBUG
        spec: DEBUG
        server: DEBUG

启动服务~

方问后缀为:/ai/generate?message=现在几点?

这里说下,这里调到接口后,会结合我们的工具及大远程(本地)将我们的工具给到大模型去调用,然后大模型根据意图及相关配置进行rag调用,最终总结输出。所以现在来看来,在spring则做应用层来集成非常easy~

最后

    不管是spring ai还是spring alibaba ai对ai应用层的支持已经都比较全面,很容易就上手了,只是可惜目前国内大部分都还是jdk1.8而要支持spring ai得升级到jdk17暂时未发现有相应的兼容框架或哪些组织提供支持,所以在传统项目中去支持spring ai的调用还是比较困难而且基础架构都得配套升级,这块还是有一定挑战~不过随着spring ai崛起应该很快会全面普及应用开发

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐