SpringAI+MCP协议 实战

文章目录

  • 前言
  • 快速实战
    • [Spring AI](#Spring AI)
    • [Spring AI 集成 MCP 协议](#Spring AI 集成 MCP 协议)
      • [Spring Mcp Client 示例](#Spring Mcp Client 示例)
      • [Spring Mcp Server 示例](#Spring Mcp Server 示例)

前言

尽管Python最近成为了编程语言的首选,但是Java在人工智能领域的地位同样不可撼动,得益于强大的Spring框架。随着人工智能技术的快速发展,我们正处于一个创新不断涌现的时代。从智能语音助手到复杂的自然语言处理系统,人工智能已经成为了现代生活和工作中不可或缺的一部分。在这样的背景下,Spring AI 项目迎来了发展的机遇。尽管该项目汲取了Python项目如LangChain和LlamaIndex的灵感,但Spring AI并不是简单的移植。该项目的初衷在于推进生成式人工智能应用程序的发展,使其不再局限于Python开发者。

Spring AI 的核心理念是提供高度抽象化的组件,作为开发AI应用程序的基础。这些抽象化组件具备多种实现,使得开发者能够以最少的代码改动便捷地交换和优化功能模块。

具体而言,Spring AI 提供了支持多种主流模型提供商的功能,包括OpenAI、Microsoft、Amazon、Google和Hugging Face。支持的模型类型涵盖了从聊天机器人到文本生成、图像处理、语音识别等多个领域。而其跨模型提供商的可移植API设计,不仅支持同步和流式接口,还提供了针对特定模型功能的灵活选项。

此外,Spring AI 还支持将AI模型输出映射为POJO,以及与主流矢量数据库提供商(如Apache Cassandra、Azure Vector Search、MongoDB Atlas等)无缝集成的能力。其功能不仅局限于模型本身,还包括了数据工程中的ETL框架和各种便利的函数调用,使得开发AI应用程序变得更加高效和可靠。

Spring Ai官网:https://spring.io/projects/spring-ai

什么是Mcp?

Model Context Protocol 是Anthropic 于2024年11月重磅开源的「模型上下文协议」MCP。其是一种开放的通信协议,是人工智能领域的 "USB 接口",在大模型和其他数据源(数据、工具、开发环境等)之间建立了双向、并且更加安全的连接。

Mcp 将LLM的数据孤岛被彻底打破,LLM应用和外部数据源、工具都将无缝集成。目标是实现LLM应用程序与外部数据源和工具之间的无缝集成。

官方文档:https://modelcontextprotocol.io/introduction

快速实战

Spring AI

当我们开始时,首先需要创建一个项目结构。我们可以前往官方网站,快速生成Spring AI的依赖并创建项目。

IDEA配置方式不过多介绍,参考:https://blog.csdn.net/qq_15437629/article/details/131912201

Maven 仓库配置在pom.xml中添加以下内容:

bash 复制代码
<repositories>
  <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>

导入 Spring AI BOM

bash 复制代码
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

添加 OpenAI 聊天

bash 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

使用 OpenAI 创建 API 来访问 ChatGPT 模型。在OpenAI 注册页面创建账户并在API 密钥页面生成令牌。如果需要其他AI,则添加对应的starter即可,如Ollama:

bash 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>

Ollama Chat 示例:
https://docs.spring.io/spring-ai/reference/api/chat/ollama-chat.html

java 复制代码
package com.example.spring_ai;

import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.ollama.OllamaChatModel;
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;
import reactor.core.publisher.Flux;

import java.util.Map;

@RestController
public class ChatController {

    private final OllamaChatModel chatModel;

    @Autowired
    public ChatController(OllamaChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @GetMapping("/ai/generate")
    public Map<String,String> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        return Map.of("generation", this.chatModel.call(message));
    }

    @GetMapping("/ai/generateStream")
    public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        return this.chatModel.stream(prompt);
    }
}

application.properities:

java 复制代码
spring.application.name=spring-ai
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.options.model=deepseek-r1
server.port=8181
logging.level.org.springframework.ai=DEBUG

pom.xml:

csharp 复制代码
<?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 https://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/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>spring-ai</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-ai</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
		<spring-ai.version>1.0.0-M6</spring-ai.version> <!-- 或最新稳定版 -->
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</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>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

测试结果:

Spring AI 集成 MCP 协议

对于 Mcp Client,Spring AI 提供了如下两个 Starter 集成 MCP Client;

  • spring-ai-mcp-client-spring-boot-starter: 实现基于 STDIO 和 HTTP 的 SSE 传输协议的 Mcp Client
  • spring-ai-mcp-client-webflux-spring-boot-starter: 实现基于 WebFlux 的 SSE 传输协议的 Mcp Client

对于 Mcp Server, Spring AI 提供了如下三个 Starter 集成 Mcp Server:

  • spring-ai-mcp-server-spring-boot-starter: 实现支持 STDIO 传输协议的 Mcp Server
  • spring-ai-mcp-server-webmvc-spring-boot-starter: 实现基于 webmvc 的 SSE 传输协议的 Mcp Server
  • spring-ai-mcp-server-webflux-spring-boot-starter: 实现基于 webflux 的 SSE 传输协议的 Mcp Server

下面基于SpringAI 1.0.0-M6实现

Spring Mcp Client 示例

https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html

pom依赖:

bash 复制代码
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
		</dependency>

配置文件:

bash 复制代码
# MCP Client Configuration
spring.ai.mcp.client.enabled=true
spring.ai.mcp.client.name=mcp-client
spring.ai.mcp.client.version=1.0.0
spring.ai.mcp.client.type=SYNC
spring.ai.mcp.client.request-timeout=30s
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json

mcp-servers-config.json:

bash 复制代码
{
  "mcpServers": {
    "filesystem": {
      "command": "D:\\\\node_js\\\\npx.cmd",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "."
      ]
    }
  }
}

1,服务包说明:

@modelcontextprotocol/server-filesystem 是 MCP 协议中用于对接文件系统的标准化服务模块,其配置通过 Node.js 环境快速启动,使 LLM 能够安全、可控地操作本地文件资源‌。

MCP 生态中还有其他类型的服务包,例如:

‌@modelcontextprotocol/server-postgres‌:支持数据库查询‌;

‌@modelcontextprotocol/server-http‌:集成 RESTful API‌。

2,npx

本地需要提前安装配置npx和uvx。npx是nodeJs下的一个工具可以执行一些Ts或者JS脚本甚至应用程序,uvx的功能则和他很类似是python环境下执行脚本的工具。简单来说,使用 TypeScript 编写的 MCP server 可以通过 npx 命令来运行,使用 Python 编写的 MCP server 可以通过 uvx 命令来运行。npx 是 Node.js 自带的工具,无需单独安装,但需先安装 Node.js。

npm 全局目录配置:

bash 复制代码
mkdir D:\node_js\pm-cache
npm config set prefix "D:\node_js"
npm config set cache "D:\node_js\pm-cache"
npm config get prefix
npm config get cache

npm cache clean --force

执行以下命令检查是否成功:

bash 复制代码
npx -v          # 应输出 npx 版本(如 10.8.2)
npm ls -g --depth=0  # 检查全局依赖是否可正常列出

如果出现权限问题: 右键点击 D:\node_js → 属性 → 安全 → 高级 ,添加当前用户并勾选 完全控制

3,模型选择

出现报错:deepseek-r1 does not support tools,需要选择支持tools的模型:

测试结果:


Spring Mcp Server 示例

https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html

依赖:

bash 复制代码
<!--标准IO通信类型的MCP服务端,适合命令行形式的桌面工具,例如之前的案例中的文件助手  -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>

<!--基于Http协议通信类型的MCP服务端  -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency>

<!--基于SSE通信类型的MCP服务端  -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
</dependency>

一个标准的MCP服务端程序需要包含三个主要信息分别为Tools、Prompts、Resources

  • 资源(Resources):资源是AI可以读取的数据,比如文件内容、数据库查询结果或API的响应。 例如,AI可能通过资源获取你的日历事件列表。
  • 工具(Tools):工具是AI可以调用的函数,用于执行特定操作,比如添加新任务或发送邮件,使用工具时,通常需要用户先批准,以确保安全。
  • 提示词(Prompts):提示词是服务器提供给AI的预写消息或模板,帮助AI理解如何使用资源和工具,例如,服务器可能告诉AI:"你可以添加任务,试试说'添加任务:买牛奶'",从而帮助用户更轻松地完成任务。提示词虽然直接提供给AI,但实际上是通过AI间接帮助用户,比如AI会根据提示词告诉用户如何操作。

依赖版本:

java 复制代码
<properties>
	<java.version>17</java.version>
	<spring-ai.version>1.0.0-M6</spring-ai.version> 
</properties>		
<dependency>
	<groupId>io.modelcontextprotocol.sdk</groupId>
	<artifactId>mcp</artifactId>
	<version>0.8.1</version>
</dependency>

配置:

go 复制代码
spring.ai.mcp.server.enabled=true
spring.ai.mcp.server.name=custom-mcp-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=ASYNC   
spring.ai.mcp.server.sse-message-endpoint=/mcp/stream  # SSE????

1,定义外部工具类

实现两个示例工具:查询当前时间和计算数学表达式。

java 复制代码
package com.example.spring_ai;

import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.ai.tool.annotation.Tool;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CustomTools {

    @Tool(description = "获取当前系统时间")
    public String getCurrentTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }

    @Tool(description = "执行数学运算(支持加减乘除)")
    public double calculateExpression(
            @Parameter(name = "expression", description = "数学表达式,如 3+5*2") String expr
    ) {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");
        try {
            return (double) engine.eval(expr);
        } catch (ScriptException e) {
            throw new RuntimeException("表达式解析失败: " + e.getMessage());
        }
    }
}

ps:Java 15+ 移除了 Nashorn JavaScript 引擎,导致 getEngineByName("JavaScript") 返回 null:

go 复制代码
<!-- Nashorn 兼容依赖 -->
<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.4</version>
</dependency>

2,注册工具到MCP服务器

通过@Configuration类将工具暴露为MCP协议的可调用接口:

java 复制代码
package com.example.spring_ai;

import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.ToolCallbacks;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class McpServerConfig {

    @Bean
    @Primary // 添加此注解指定优先使用此Bean
    public ToolCallbackProvider toolProvider() {
        // 注册工具类实例
        ToolCallback[] callbacks = ToolCallbacks.from(new CustomTools());
        return ToolCallbackProvider.from(callbacks);
    }
}

4,客户端调用示例

java 复制代码
package com.example.spring_ai;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
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;

@RestController
@RequestMapping("/client")
public class McpClientController {

    private final ChatClient chatClient;

    public McpClientController(ChatClient.Builder builder, ToolCallbackProvider tools) {
        this.chatClient = builder.defaultTools(tools).build();
    }

    @GetMapping("/time")
    public String getTime() {
        return chatClient.prompt("调用getCurrentTime工具").call().content();
    }

    @GetMapping("/calc")
    public String calculate(@RequestParam String expr) {
        return chatClient.prompt("计算表达式:" + expr).call().content();
    }
}

当客户端发送请求时,Spring AI 的底层流程会遍历所有已注册工具,检查输入文本是否与工具描述(如:执行数学运算(支持加减乘除))匹配。若匹配成功,直接调用 对应方法,无需显式指定工具名称。

测试结果如下:

日志:

go 复制代码
[spring-ai] [nio-8181-exec-3] o.s.a.m.tool.DefaultToolCallingManager   : Executing tool call: getCurrentTime
[spring-ai] [nio-8181-exec-3] o.s.ai.tool.method.MethodToolCallback    : Starting execution of tool: getCurrentTime
[spring-ai] [nio-8181-exec-3] o.s.ai.tool.method.MethodToolCallback    : Successful execution of tool: getCurrentTime
[spring-ai] [nio-8181-exec-3] o.s.a.t.e.DefaultToolCallResultConverter : Converting tool result to JSON.

[spring-ai] [nio-8181-exec-7] o.s.a.m.tool.DefaultToolCallingManager   : Executing tool call: calculateExpression
[spring-ai] [nio-8181-exec-7] o.s.ai.tool.method.MethodToolCallback    : Starting execution of tool: calculateExpression
[spring-ai] [nio-8181-exec-7] o.s.ai.tool.method.MethodToolCallback    : Successful execution of tool: calculateExpression
[spring-ai] [nio-8181-exec-7] o.s.a.t.e.DefaultToolCallResultConverter : Converting tool result to JSON.
相关推荐
前端大卫1 分钟前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘17 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare18 分钟前
浅浅看一下设计模式
前端
Lee川22 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端