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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78494.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78494.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78494.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ubuntu22.04 命令行修改靜態ip

傳統interfaces文件配置&#xff08;適用于舊版&#xff09;即便我們已經在桌面上配置了固定ip 這里也可以修改 ?編輯配置文件? 修改/etc/network/interfaces&#xff08;需安裝net-tools&#xff09;&#xff1a; # interfaces(5) file used by ifup(8) and ifdown(8) # In…

計算機網絡學習筆記 4-6章

第 4 章 網絡層 【考綱內容】 &#xff08;一&#xff09;網絡層的功能 異構網絡互連&#xff1b;路由與轉發&#xff1b;SDN 基本概念&#xff1b;擁塞控制 &#xff08;二&#xff09;路由算法 靜態路由與動態路由&#xff1b;距離 - 向量路由算法&#xff1…

力扣hot100_子串_python版本

一、560. 和為 K 的子數組 思路&#xff1a;這就是一道典型的前綴和的題代碼: class Solution:def subarraySum(self, nums: List[int], k: int) -> int:presum [0] * (len(nums) 1)for i, x in enumerate(nums):presum[i 1] presum[i] x # 前綴和序列需要n1個ans 0…

猿人學web端爬蟲攻防大賽賽題第15題——備周則意怠-常見則不疑

解題步驟 1、觀察抓的包 2、有個m參數&#xff0c;一看就是經過處理的&#xff0c;我們得知道m是如何組成的。看Initiator模塊。 3、還是看request函數&#xff0c;往上一看就看到了m的賦值操作。 打斷點&#xff0c;觸發。 4、看下window.m()的定義 5、比較好理解的&#x…

rag增強檢索-基于關鍵詞檢索的混合檢索模式

1. 為什么在 RAG 里要用關鍵詞檢索? 向量檢索(embedding-based retrieval)是找語義相近的內容,但有時候不夠準確。比如用戶問了具體人名、產品型號、年份,這類關鍵詞強指向性的信息,用向量檢索可能匹配不到最相關內容。**關鍵詞檢索(keyword-based retrieval)**可以直接…

純真社區IP庫離線版發布更新

純真社區IP庫離線版發布更新 發布者&#xff1a;技術分享 2005年&#xff0c;隨著中國互聯網的蓬勃發展&#xff0c;純真IP庫誕生了。作為全球網絡空間地理測繪技術的領先者&#xff0c;純真開源項目為中國互聯網行業提供了高質量的網絡空間IP庫數據。純真IP庫目前已經覆蓋超…

GitOps進化:深入探討 Argo CD 及其對持續部署的影響

什么是 GitOps&#xff1f; 雖然軟件開發生命周期的大部分已經實現自動化&#xff0c;但基礎設施仍然在很大程度上依賴于人工&#xff0c;需要專業團隊的參與。隨著當今基礎設施需求的不斷增長&#xff0c;實施基礎設施自動化變得越來越重要。現代基礎設施需要具備彈性&#x…

通過示例學習:連續 XOR

通過示例學習&#xff1a;連續 XOR 如果我們想在 PyTorch 中構建神經網絡&#xff0c;可以使用 &#xff08;with&#xff09; 指定所有參數&#xff08;權重矩陣、偏差向量&#xff09;&#xff0c;讓 PyTorch 計算梯度&#xff0c;然后調整參數。但是&#xff0c;如果我們有很…

百度Create大會深度解讀:AI Agent與多模態模型如何重塑未來?

目錄 百度Create大會亮點全解析&#xff1a;從數字人到Agent生態布局 數字人商業化&#xff1a;從"擬人"到"高說服力"的進化 Agent生態&#xff1a;從"心響"App看百度的Agent戰略布局 "心響"App的技術架構與創新點 多模態大模型&a…

django filter 日期大于當前日期的

在Django中&#xff0c;如果你想要過濾出日期大于當前日期的記錄&#xff0c;你可以使用Django的QuerySet API中的__gt&#xff08;大于&#xff09;操作符。這里是如何做到這一點的步驟&#xff1a; 確定你的模型&#xff1a;首先&#xff0c;確保你有一個模型&#xff08;Mo…

C#本地使用離線ocr庫識別圖片中文本,工具包PaddleOCRSharp

C#本地使用離線ocr庫識別圖片文本&#xff0c;工具包PaddleOCRSharp PaddleOCRSharp介紹 項目地址&#xff1a;https://github.com/raoyutian/PaddleOCRSharp PaddleOCRSharp 是一個.NET版本OCR可離線使用類庫。項目核心組件PaddleOCR.dll目前已經支持C\C、.NET、Python、Go…

缺省處理、容錯處理

布爾判定 假&#xff1a;false 0 null undefined NaN 可選符.?和&#xff1f;&#xff1f; let obj {name: jim,data: {money: 0,age: 18,fn(a){return a}} }1、如果左側的值為null或者undefined&#xff0c;則使用右側值。需要使用"??" obj?.data?.a…

【Java面試題系列02】Java 集合常見面試題

文章目錄 一、前言&#x1f680;&#x1f680;&#x1f680;二、Java 基礎面試題&#xff1a;??????1、說說 Java 中 HashMap 的原理&#xff1f;2、HashMap 的擴容機制&#xff1f;3、為什么 Java 中 HashMap 的默認負載因子是 0.75?4、JDK 1.8 對 HashMap 除了紅黑樹還…

如何創建并使用極狐GitLab 部署令牌?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 部署令牌 (BASIC ALL) 您可以使用部署令牌來啟用部署任務的身份驗證&#xff0c;而與用戶賬戶無關。在大多數情況下&#xf…

OpenGl ES 3.0 筆記一:初步概念

&#x1f3af; 整體比喻&#xff1a;開一場 3D 打印畫展&#xff01; 想象你在做一件事情&#xff1a;「拿設計圖&#xff0c;把它畫在一張紙上」。 這整個流程就好像 GPU 在渲染一幅畫。 而下面這幾個階段&#xff0c;就是這場「畫展」里每個具體的人或機器要做的事情&#x…

人類社會的第四階段

本書的主旨是探討一場新的權力革命&#xff0c;它將以20世紀民族國家的毀滅為代價&#xff0c; 解放出個體。創新&#xff0c;以前所未有的方式改變了暴力的邏輯&#xff0c;并且正在革新未來的 邊界。如果我們的推論是正確的&#xff0c;你們正站在一場有史以來最宏大的革命的…

位置差在坐標系間的相互轉換

1 NED轉經緯高 &#xff08;n 系下的北向、東向和垂向位置差異&#xff08;單位 m&#xff09;轉化為緯度、經度和高程分量的差異&#xff09; 2 基站坐標轉換 緯度、經度、高程 到 ECEF %緯度、經度、高程 到 ECEF clc; clear; glvs; addpath(genpath(E:\GNSSINS\ACES)…

WPF定義擴展屬性和依賴屬性

WPF擴展屬性與依賴屬性詳解 一、依賴屬性(Dependency Property)詳解 1. 什么是依賴屬性? 依賴屬性是WPF框架的核心特性之一,它允許屬性值依賴于: 父元素的屬性值(繼承)樣式和模板動畫數據綁定資源查找2. 依賴屬性的特點 ??屬性值繼承??:子元素可以繼承父元素的屬性…

邊緣函數:全棧開發的最后1毫秒性能革命

一、邊緣計算的時空折疊術 1. 傳統CDN vs. 智能邊緣網絡 全球電商平臺實測數據&#xff1a; 場景云端處理延遲邊緣處理延遲轉化率提升搜索建議320ms8ms18%個性化推薦450ms12ms27%實時庫存檢查680ms9ms42%欺詐檢測920ms15ms63% 二、邊緣全棧架構的量子糾纏 1. 代碼的時空分布…

Codeforces Round 1021 (Div. 2) D. Baggage Claim(建圖)

每周五篇博客&#xff1a;&#xff08;4/5&#xff09; https://codeforces.com/contest/2098/problem/D 題意 每個機場都有一個行李索賠區&#xff0c;巴爾貝索沃機場也不例外。在某個時候&#xff0c;Sheremetyevo的一位管理員提出了一個不尋常的想法&#xff1a;將行李索…