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客戶端對話就比較簡單了,咱們多看看源碼接口提供的方法,多看幾遍在看網上的事例代碼就容易多了。也可以自己嘗試修改一些參數。