Spring-Ai-McpSever从外到内
MCP是什么
Model Context Protocol (MCP) 是一个开放协议,它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能。无论你是构建 AI 驱动的 IDE、改善 chat 交互,还是构建自定义的 AI 工作流,MCP 提供了一种标准化的方式,将 LLM 与它们所需的上下文连接起来
目前,MCP 已经积累了足够的临界规模和动能,因此它被视为 2023-2025 年“代理开放标准”之争的潜在赢家。有人预计,按照当前的速度,MCP 将在 7 月超OpenAPI
JAVA实现MCP
找了一个github的事例代码仅供参考
事例代码:
spring-ai-mcp-deepseek
代码模块比较清晰
mcp-server
基于springBoot启动配置
application.properties
# MCP服务端开启
spring.ai.mcp.server.enabled=true# MCP服务端配置
spring.ai.mcp.server.name=book-management-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.sse-message-endpoint=/mcp/message
@Configuration
public class McpServerConfig {/*** 注册工具回调提供者,将BookQueryService中的@Tool方法暴露为MCP工具** @param bookService 图书服务* @return 工具回调提供者*/@Beanpublic ToolCallbackProvider bookToolCallbackProvider(BookService bookService) {return MethodToolCallbackProvider.builder().toolObjects(bookService).build();}}
打开 ToolCallbackProvider
public interface ToolCallbackProvider {//回调函数接口需要实现类去实现的FunctionCallback[] getToolCallbacks();//静态方法接口默认实现 ,不需要实现类实现public static ToolCallbackProvider from(List<? extends FunctionCallback> toolCallbacks) {return new StaticToolCallbackProvider(toolCallbacks);}public static ToolCallbackProvider from(FunctionCallback... toolCallbacks) {return new StaticToolCallbackProvider(toolCallbacks);}}
ToolCallbackProvider 得实现类图
打开源码 StaticToolCallbackProvider
public class StaticToolCallbackProvider implements ToolCallbackProvider {private final FunctionCallback[] toolCallbacks;// 可以使用构造器去实例化类,入参不能null 可以是空的数组public StaticToolCallbackProvider(FunctionCallback... toolCallbacks) {Assert.notNull(toolCallbacks, "ToolCallbacks must not be null");this.toolCallbacks = toolCallbacks;}public StaticToolCallbackProvider(List<? extends FunctionCallback> toolCallbacks) {Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");this.toolCallbacks = toolCallbacks.toArray(new FunctionCallback[0]);}// 必须实现父类的接口方法@Overridepublic FunctionCallback[] getToolCallbacks() {return this.toolCallbacks;}}
打开源码(MethodToolCallbackProvider)
package org.springframework.ai.tool.method;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.ai.tool.metadata.ToolMetadata;
import org.springframework.ai.tool.util.ToolUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class MethodToolCallbackProvider implements ToolCallbackProvider {private static final Logger logger = LoggerFactory.getLogger(MethodToolCallbackProvider.class);// 我们要实例化对象的属性private final List<Object> toolObjects;//构造器私有化了 不能new去实例化对象private MethodToolCallbackProvider(List<Object> toolObjects) {Assert.notNull(toolObjects, "toolObjects cannot be null");Assert.noNullElements(toolObjects, "toolObjects cannot contain null elements");this.toolObjects = toolObjects;}//实现接口默认方法@Overridepublic ToolCallback[] getToolCallbacks() {var toolCallbacks = toolObjects.stream().map(toolObject -> Stream.of(ReflectionUtils.getDeclaredMethods(toolObject.getClass())).filter(toolMethod -> toolMethod.isAnnotationPresent(Tool.class)).filter(toolMethod -> !isFunctionalType(toolMethod)).map(toolMethod -> MethodToolCallback.builder().toolDefinition(ToolDefinition.from(toolMethod)).toolMetadata(ToolMetadata.from(toolMethod)).toolMethod(toolMethod).toolObject(toolObject).toolCallResultConverter(ToolUtils.getToolCallResultConverter(toolMethod)).build()).toArray(ToolCallback[]::new)).flatMap(Stream::of).toArray(ToolCallback[]::new);validateToolCallbacks(toolCallbacks);return toolCallbacks;}//实现类自己的私有方法private boolean isFunctionalType(Method toolMethod) {var isFunction = ClassUtils.isAssignable(toolMethod.getReturnType(), Function.class)|| ClassUtils.isAssignable(toolMethod.getReturnType(), Supplier.class)|| ClassUtils.isAssignable(toolMethod.getReturnType(), Consumer.class);if (isFunction) {logger.warn("Method {} is annotated with @Tool but returns a functional type. "+ "This is not supported and the method will be ignored.", toolMethod.getName());}return isFunction;}//实现类自己的校验方法private void validateToolCallbacks(ToolCallback[] toolCallbacks) {List<String> duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);if (!duplicateToolNames.isEmpty()) {throw new IllegalStateException("Multiple tools with the same name (%s) found in sources: %s".formatted(String.join(", ", duplicateToolNames),toolObjects.stream().map(o -> o.getClass().getName()).collect(Collectors.joining(", "))));}}
//静态方法 构建器 直接用类.builder调用(创建型模式)public static Builder builder() {//使用 new 关键字 内部实例化对象return new Builder();}
//静态内部类,私有化构造器,只能在内部被实例化public static class Builder {// 接toolObjects方法的入参private List<Object> toolObjects;private Builder() {}public Builder toolObjects(Object... toolObjects) {Assert.notNull(toolObjects, "toolObjects cannot be null");//这个是Builder 的toolObjects 对象赋值this.toolObjects = Arrays.asList(toolObjects);//返回Builder对象return this;}public MethodToolCallbackProvider build() {//返回实例化并且初始化后的 MethodToolCallbackProvider 对象return new MethodToolCallbackProvider(toolObjects);}}}
mcp-client
先看一下项目的application.properties
我用硅基流的第三方模型,这个模型deepseek-ai/DeepSeek-R1-Distill-Qwen-7B是免费试用的。你们自己看情况去选择
server.port=8082spring.ai.openai.api-key=你自己的key
spring.ai.openai.base-url=https://api.siliconflow.cn
spring.ai.openai.chat.options.model=deepseek-ai/DeepSeek-R1-Distill-Qwen-7Bspring.ai.mcp.client.sse.connections.server1.url=http://localhost:8081
spring.ai.mcp.client.sse.toolcallback.enabled=true
客户端配置启动连接服务端
package com.example.client.config;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ChatClientConfig {// 自动注入回调服务工具类@Autowiredprivate ToolCallbackProvider toolCallbackProvider;/*** 配置ChatClient,注册系统指令和工具函数*/@Beanpublic ChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("你是一个智能AI管理员。")// 注册工具方法.defaultTools(toolCallbackProvider).build();}
}
使用对话
package com.example.client.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** 聊天控制器,处理AI聊天请求*/
@RestController
@RequestMapping("/c3")
public class ChatController {// 依赖注入bean初始化完的实例对象@Resourceprivate ChatClient chatClient;@RequestMapping("/api/chat")public String chat(@RequestParam String message) {// 使用API调用聊天String content = chatClient.prompt("你要问什么?").user(message).call().content();System.out.println(">>> 问题: " + message);System.out.println(">>> 回答:" + content);return content;}}
打开 ChatClient
ChatClient.Builder 有个默认实现类DefaultChatClientBuilder
ChatClient 本身是接口无法实现所以有一个DefaultChatClient 默认的实现类
public ChatClient build() {return new DefaultChatClient(this.defaultRequest);}
结语
chatClient客户端对话就比较简单了,咱们多看看源码接口提供的方法,多看几遍在看网上的事例代码就容易多了。也可以自己尝试修改一些参数。