使用SpringAI/SpringAlibaba接入MCP

Spring AI 提供了两种机制快速搭建 MCP Server,通过这两种方式开发者可以快速向 AI 应用开放自身的能力,这两种机制如下:

  • 基于 stdio 的进程间通信传输,以独立的进程运行在 AI 应用本地,适用于比较轻量级的工具。
  • 基于 SSE(Server-Sent Events) 进行远程服务访问,需要将服务单独部署,客户端通过服务端的 URL 进行远程访问,适用于比较重量级的工具。

本文涉及三类角色:

1. MCP Server
   - 对外暴露工具能力
   - 可以是 stdio 或 SSE 形式
   - 不直接调用大模型

2. MCP Client
   - 负责连接一个或多个 MCP Server
   - 将工具注册给 Agent 使用

3. Agent(基于 LLM)
   - 由 Spring AI / Spring AI Alibaba 构建
   - 通过 MCP Client 使用外部工具

参考文档:

https://java2ai.com/blog/spring-ai-alibaba-mcp#22-使用-spring-ai-mcp-快速搭建-mcp-server

https://docs.springframework.org.cn/spring-ai/reference/api/mcp/mcp-overview.html

1. 开发stdio类型的MCP服务

使用java语言开发stdio的服务,并打包成jar包, 使用方可以下载使用

首先需要引用依赖:

注意SpringAIMCP的依赖,已经基于SpringBoot 3.4.x之上

这里主要引入的依赖为: spring-ai-starter-mcp-server 主要用于实现stdio协议类型的MCP服务

<?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>

    <groupId>org.example</groupId>
    <artifactId>ai-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath/>
    </parent>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server</artifactId>
        </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>

    <build>
        <plugins>
            <!-- Spring Boot Maven Plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

application中配置信息如下:

spring:
  main:
    web-application-type: none
    banner-mode: off
  ai:
      mcp:
        server:
          stdio: true            # 启用stdio模式
          name: check-sensitive-word-server # 服务器名称
          version: 0.0.1         # 服务器版本

使用注解方式定义此服务对外提供的函数调用

@Service
public class CheckSensitiveWordService {

    @Tool(description = "检测文本中是否有敏感词汇,如果有,将替换敏感词为*,并返回新文本")
    public String checkSensitiveWord(
            @ToolParam(description = "需要校验的文本") String text) {
        // 简单模拟一下
        return text.replaceAll("我草", "**");
    }
}

这里模拟一个敏感词检测的功能服务, 需要在函数注解和方法参数注解上详细描述函数和参数的含义, 使用方才能根据描述调用

设置一个启动类, 并像容器内注入该函数的实现:

@SpringBootApplication
public class TestApplication {

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

    @Bean
    public ToolCallbackProvider weatherTools(CheckSensitiveWordService checkSensitiveWordService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(checkSensitiveWordService)
                .build();
    }

}

执行maven打包命令,将该工程打包使用: mvn clean package

接下来需要找一个Client端测试调用, 例如Claude. cursor, 我这里使用 cursor模拟. 在cursor设置中添加注册MCP, 配置信息如下:

{
  "mcpServers": {
    "check-sensitive-word": {
            "command": "java",
            "args": [
                "-Dspring.ai.mcp.server.stdio=true",
                "-Dspring.main.web-application-type=none",
                "-Dlogging.pattern.console=",
                "-jar",
                "/Users/hehe/dev/code/lazada/ai-server/ai-server/target/ai-server-1.0-SNAPSHOT.jar"
            ],
            "env": {}
        }
  }
}

该json中, 指定使用java方式启动, 并指定启动参数,和jar包的路径

【非常重要】stdio 模式下必须关闭所有控制台日志输出

MCP stdio 模式下,标准输出(stdout)是协议通信通道,任何非 JSON 的日志输出都会导致 MCP 客户端解析失败。

因此必须:

  • 关闭 banner
  • 清空 logging.pattern.console
  • 不允许 System.out.println 输出

否则 Cursor / Claude 会直接报错或无法识别 MCP 服务。

使用测试如下: 可以看到, cursor客户端成功识别了注册的jar包,并根据提示调用了此MCP

image-20251218205237502

2. 开发sse类型的MCP服务

基于 SSE 的 MCP 服务端通过 HTTP 协议与客户端通信,适用于作为独立服务部署的场景,可以被多个客户端远程调用,具体做法与 stdio 非常类似。

SpringAI 提供了两种方式实现对外Web端点, 一个是WebMVC, 一个是 WebFlux.
分别依赖: spring-ai-starter-mcp-server-webmvcspring-ai-starter-mcp-server-webflux

我这里使用WebFlux作为演示, 实际使用方式几乎一样

pom依赖:

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

application中配置信息如下:

spring:
  main:
#    web-application-type: none
    banner-mode: off
  ai:
    mcp:
      server:
        stdio: false            # 启用stdio模式
        name: check-sensitive-word-server # 服务器名称
        version: 0.0.1         # 服务器版本

server:
  port: 8080  # MCP Server 端口配置

需要指定HTTP服务端口

工具定义方式一样,

@Service
public class CheckSensitiveWordService {

    @Tool(description = "检测文本中是否有敏感词汇,如果有,将替换敏感词为*,并返回新文本")
    public String checkSensitiveWord(
            @ToolParam(description = "需要校验的文本") String text) {
        // 简单模拟一下
        System.out.println("正在检测文本中敏感词:" + text);
        return text.replaceAll("我草", "**");
    }

}

但是需要额外注入Web启动监听的Client类: 作为MCPServer的Client

@SpringBootApplication
public class TestApplication {

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

    @Bean
    public ToolCallbackProvider weatherTools(CheckSensitiveWordService checkSensitiveWordService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(checkSensitiveWordService)
                .build();
    }

    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }

}

这里我们就不用打包,直接本地启动即可, 默认的监听端点为: http://127.0.0.1:8080/sse

cursor中配置信息如下: 指定端点路径和协议类型

    "check-sensitive-word": {
          "type": "sse",
          "url": "http://127.0.0.1:8080/sse",
          "headers": {
          }
        }

再次测试敏感词校验,成功调用启动的web服务,web服务打印日志: 正在检测文本中敏感词:我草,太吓人了

image-20251219112228760

3. 使用Spring AI Alibaba 开发Agent接入MCP服务

在之前使用MCP服务的测试案例中,我们都使用了目前现有的客户端, 例如 ClaudeCursor 等等.

那我们自己开发Agent也可以接入MCP吗? 使Agent具有更强大的功能, SpringAI 框架实现了这个功能, 我们这里使用Spring AI Alibaba 接入LLM进行测试

我们可以引入spring-ai-starter-mcp-client 依赖, 通过STDIO进程内和/或SSE远程传输方式同时连接到一个或多个 MCP 服务器. SSE 连接使用基于 HttpClient 的传输实现。每个与 MCP 服务器的连接都会创建一个新的 MCP 客户端实例

也可以使用 基于 WebFlux的Http调用端来实现SSE远程调用(官方建议), 可以使用 这个依赖: spring-ai-starter-mcp-client-webflux

下面我们通过一个示例,快速了解使用方式:

首先需要引入SpringAIAlibaba dashscope的依赖, 用于LLM的调用. 和SpringAI对于MCP支持的依赖: spring-ai-starter-mcp-client-webflux,具体如下

<?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.4</version>
        <relativePath/>
    </parent>

    <groupId>org.example</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-ai.version>1.1.0-M4</spring-ai.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
      
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.3.0</version>
        </dependency>

        <!-- Spring AI Alibaba Agent Framework -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-agent-framework</artifactId>
            <version>1.1.0.0-M5</version>
        </dependency>

        <!-- DashScope ChatModel 支持(如果使用其他模型,请参考文档选择对应的 starter) -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.1.0.0-M5</version>
        </dependency>


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

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
        </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>


    <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>
            <url>https://repo.spring.io/milestone</url>
        </pluginRepository>
    </pluginRepositories>
</project>

版本兼容性说明(非常重要)

本文示例基于以下版本组合验证通过:

  • Spring Boot:3.4.4
  • Spring AI:1.1.0-M4
  • Spring AI Alibaba:1.1.0.0-M5

这三个版本在当前时间点是【已验证可用】的组合。
由于均为 Milestone 版本,未来升级时可能存在不兼容风险,
强烈建议严格对齐版本或参考官方 Release Notes。

在该案例中,我们一共引入了三个MCP服务,分别为:

  • 基于stdio调用的mysql-mcp-server
  • 基于sse调用的自实现敏感词服务调用
  • 基于sse调用的阿里百炼平台高德地图服务

application配置文件:

server:
  port: 8089
spring:
  application:
    name: ai-test
  main:
    allow-bean-definition-overriding: true
  ai:
    mcp:
      client:
        enabled: true
        name: spring-ai-mcp-client
        # stdio的方式可以使用json文件的方式
        stdio:
          servers-configuration: classpath:mcp-servers.json
        # sse接入的方式只能在这里配置
        sse:
          connections:
            checkSensitiveWord:
              url: http://127.0.0.1:8080
              sseEndpoint: /sse
            amap:
              url: https://dashscope.aliyuncs.com
              sseEndpoint: /api/v1/mcps/amap-maps/sse

    dashscope:
      api-key: sk-*****
      base-url: https://dashscope.aliyuncs.com
      chat:
        options:
          model: qwen3-max

配置文件中主要配置了LLM相关秘钥,链接, 和mcp配置信息.

sse配置信息配置在application文件中,指定端点即可.

stdio类型配置在外部json文件中, json配置文件信息如下:

{
  "mcpServers": {
    "mysql": {
      "command": "uvx",
      "args": [
        "--from",
        "mysql-mcp-server",
        "mysql_mcp_server"
      ],
      "env": {
        "MYSQL_HOST": "121.36.***.***",
        "MYSQL_PORT": "3307",
        "MYSQL_USER": "root",
        "MYSQL_PASSWORD": "****",
        "MYSQL_DATABASE": "***"
      }
    }
  }
}

该json配置文件,描述了mysql-mcp-server启动的一些配置信息.

在application这个配置信息中, 有一个小问题, 阿里百炼平台的高德地图MCP服务, 需要在header中携带鉴权信息:{"headers":{"Authorization":"Bearer ${DASHSCOPE_API_KEY}"}}.

但是框架中提供的配置参数,目前只有如上配置的: urlsseEndpoint,并没有设置SSE调用时的header传参.我这里用了一个临时的解决办法,

注意:当前 Spring AI MCP Client 尚未原生支持为不同 SSE MCP Server 配置独立 Header。

此处通过 WebClientCustomizer 注入全局 Header,属于临时解决方案(Workaround),后续版本可能提供官方配置方式。

在框架初始化SSE调用使用的HttpClient时, 设置默认的全局header传参:

@Configuration
public class McpSseHeaderAutoConfiguration {

    @Bean
    public WebClientCustomizer tokenWebClientCustomizer() {
        return builder -> builder.defaultHeader("Authorization", "Bearer " + "your-token");
    }
}

Springboot初始化WebClient.Builder 时,会使用容器中所有的WebClientCustomizer进行构建: org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration#webClientBuilder

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public WebClient.Builder webClientBuilder(ObjectProvider<WebClientCustomizer> customizerProvider) {
        WebClient.Builder builder = WebClient.builder();
        customizerProvider.orderedStream().forEach((customizer) -> {
            customizer.customize(builder);
        });
        return builder;
    }

org.springframework.ai.mcp.client.webflux.autoconfigure.SseWebFluxTransportAutoConfiguration#sseWebFluxClientTransports 方法中, 使用了该Builder类进行构建SSE客户端:

@Bean
	public List<NamedClientMcpTransport> sseWebFluxClientTransports(McpSseClientConnectionDetails connectionDetails,
			ObjectProvider<WebClient.Builder> webClientBuilderProvider,
			ObjectProvider<ObjectMapper> objectMapperProvider) {

		List<NamedClientMcpTransport> sseTransports = new ArrayList<>();

		var webClientBuilderTemplate = webClientBuilderProvider.getIfAvailable(WebClient::builder);
		var objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);

		for (Map.Entry<String, SseParameters> serverParameters : connectionDetails.getConnections().entrySet()) {
			var webClientBuilder = webClientBuilderTemplate.clone().baseUrl(serverParameters.getValue().url());
			String sseEndpoint = serverParameters.getValue().sseEndpoint() != null
					? serverParameters.getValue().sseEndpoint() : "/sse";
			var transport = WebFluxSseClientTransport.builder(webClientBuilder)
				.sseEndpoint(sseEndpoint)
				.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
				.build();
			sseTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
		}

		return sseTransports;
	}

启动时,SpringBoot会自动根据配置文件构建MCP服务类, 只需要在构建Agent时设置进Tool即可:

@Configuration
public class McpAgentConfiguration {

    private final DashScopeChatModel chatModel;

    public McpAgentConfiguration(DashScopeChatModel chatModel) {
        this.chatModel = chatModel;
    }


    @Bean
    public ReactAgent mcpReactAgent(ToolCallbackProvider tools) {
        String SYSTEM_PROMPT = """
                你是一个公司内部智能助手
                你可以根据已知的工具帮助回答用户的问题
                """;

        return ReactAgent.builder()
                .name("工作助手")
                .instruction(SYSTEM_PROMPT)
                .tools(tools.getToolCallbacks())
                // 基于内存的存储
                .saver(new MemorySaver())
                .model(chatModel)
                .build();
    }

}

访问Agent:

@RestController
@RequestMapping("/mcp/ai")
public class McpAiController {

    @Resource
    @Qualifier("mcpReactAgent")
    private ReactAgent reactAgent;

    @GetMapping
    public String ai(@RequestParam String question) throws Exception {
        RunnableConfig runnableConfig = RunnableConfig.builder().threadId("threadId").build();
        return reactAgent.call(question, runnableConfig).getText();
    }


}

向数据库中插入一条记录:

INSERT INTO `game_center`.`test_user_bases` (`id`, `tenant_id`, `address`, `open_id`, `type`, `nickname`, `avatar`, `gender`, `birthday`, `mobile`, `email`, `country`, `province`, `city`, `created_at`, `updated_at`) VALUES (240415216, NULL, '浙江省杭州市余杭区良渚街道永旺梦乐城', NULL, 1, '我草尼玛', 'https://yaodu.obs.cn-north-4.myhuaweicloud.com/avatar/1_2.png', 1, NULL, '18256081124', NULL, NULL, NULL, NULL, '2025-09-01 10:28:40', '2025-12-19 15:51:55');

测试敏感词校验服务:http://127.0.0.1:8089/mcp/ai?question=从test_user_bases中id为240415216的用户,昵称有没有敏感词?

回答如下: 是的,该用户(id为240415216)的昵称"我草尼玛"包含敏感词。经过敏感词检测,昵称中的"草尼玛"被识别为敏感内容,已被替换为"**尼玛"。

测试高德地图: http://127.0.0.1:8089/mcp/ai?question=从test_user_bases中id为240415216的用户,居住地址附近一日游规划

回答如下: 成功调用数据库,并访问了高德地图

# 🌸 良渚一日游行程规划(轻松版)

**出发地**:浙江省杭州市余杭区  
**参考起点**:良渚街道 · 永旺梦乐城

---

## 🕘 上午|文化探索(9:00 – 11:30)

### 🏛 施昕更纪念馆
- **地址**:良港路 76 号  
- **开放时间**:  
  周二至周日  
  09:00–12:00|13:30–16:30(周一闭馆)
- **推荐游览时间**:1.5–2 小时  
- **亮点**:  
  - 了解良渚文化的发现历程  
  - 认识良渚文化发现者施昕更先生  
  - 人少安静,文化氛围浓厚  

---

## 🍽 中午|午餐休息(12:00 – 13:30)

### 🍵 绿茶餐厅(良渚永旺店)
- **位置**:永旺梦乐城 2F · 271 号  
- **菜系**:杭帮菜  
- **推荐菜品**:  
  - 面包诱惑  
  - 绿茶烤鸡  
  - 石锅豆腐  
- **优势**:吃完可在商场内休息、逛街  

---

## 🌿 下午|自然 & 休闲(14:00 – 17:00)

### 🌼 第一站:兰里景区 · 花溪公园
- **开放时间**:全天 24 小时  
- **游览建议**:1–1.5 小时  
- **特色**:  
  - 田园风光、溪流景观  
  - 适合散步、拍照、放松  
  - 节奏慢,适合慢生活  

---

### 🎡 第二站:平高摩天轮广场
- **地址**:古墩路 1892 号  
- **开放时间**:10:00 – 22:00  
- **游览建议**:1 小时左右  
- **亮点**:  
  - 摩天轮俯瞰良渚全景  
  - 傍晚及夜景灯光更好看  
  - 广场适合慢走休息  

---

## ☕ 傍晚|放松收尾(17:30 – 18:30)

### ☕ 星巴克(良渚永旺梦乐城店)
- **位置**:永旺梦乐城 1F · 171 号  
- **建议**:  
  - 喝杯咖啡,放松休息  
  - 回顾一天行程,轻松返程  

---

## ⭐ 行程亮点总结
- ✅ 文化 + 自然 + 休闲结合  
- ✅ 所有地点基本在 5 公里范围内  
- ✅ 行程节奏舒缓,不赶时间  
- ✅ 多数景点免费或低消费  

---

## 💡 温馨小提示
- 施昕更纪念馆周一闭馆  
- 建议穿舒适的步行鞋  
- 摩天轮夜景更佳,可适当延长停留  
- 建议携带充电宝与相机  

---

📍 **高德地图行程链接**  
[点击查看完整行程](amapuri://workInAmap/createWithToken?polymericId=mcp_a43d01ea21e5426d93e29363fa93986d&from=MCP)
posted @ 2025-12-29 17:18  哈哈丶丶  阅读(109)  评论(0)    收藏  举报