文章目錄
- 概述
- MCP協議:為AI應用連接外部世界的橋梁
- MCP Server:上下文與能力的提供者
- 基于Spring AI 1.0.0的開發之路
- 1. 使用Spring AI構建MCP客戶端
- 2. 使用Spring AI構建MCP服務器
- Mcp Client 實戰
- 整體架構概覽
- 技術棧
- Code
- pom
- 配置mcp servers(sse&stdio)
- amap-maps 服務配置
- filesystem 服務配置
- 工作原理
- ChatClient核心模塊
- 工具回調機制
- 典型應用場景代碼示例
- 基礎對話場景
- 工具增強對話場景
- 流式響應場景
- MCP Server調用
- 調用高德 MCP Server (stdio)
- 調用server-filesystem MCP Server (stdio)
- 調用自己寫的服務(sse)
- 性能優化建議
- 緩存策略
- 異步處理優化
- 最佳實踐建議
- 架構設計最佳實踐
- 性能優化最佳實踐
- 安全性最佳實踐
概述
自2024年11月由Anthropic公司發布以來,模型上下文協議(Model Context Protocol, MCP)作為一項開放標準,正迅速成為人工智能領域,特別是大型語言模型(LLM)應用開發中的一個重要基石。它旨在為LLM應用與外部數據源和工具之間提供一個標準化的“通用接口”,從而打破模型僅能依賴其內部訓練數據的局?pad?,使其能夠獲取和利用實時、動態的外部世界信息。
接下來我們將深入介紹MCP協議的核心概念,重點解析其服務器(MCP Server)的角色與功能,并進一步探討如何利用日趨成熟的Java AI開發框架——Spring AI 1.0.0,來構建支持MCP的應用。
MCP協議:為AI應用連接外部世界的橋梁
MCP協議的核心思想是,在LLM應用(客戶端)和提供上下文信息、工具或數據的服務(服務器)之間建立一個統一的通信規范。這極大地簡化了集成過程,開發者不再需要為每一個數據源或API編寫定制化的連接代碼,而是可以遵循一個通用的協議標準。
MCP協議的主要構成與特點:
- 開放標準與協作: 由Anthropic發起,并得到了包括OpenAI、Google DeepMind和微軟在內的行業巨頭支持,保證了其廣泛的適用性和未來的發展潛力。
- 基于成熟技術: MCP在設計上借鑒了語言服務器協議(Language Server Protocol, LSP),并采用JSON-RPC 2.0作為其消息傳輸格式,可通過stdio、HTTP(支持Server-Sent Events)等多種方式進行傳輸。
- 核心概念:
- 主機(Host): 指的是LLM應用程序本身,它負責發起與MCP服務器的連接。
- 客戶端(Client): 位于主機應用內部,是實現MCP通信的具體連接器。
- 服務器(Server): 負責提供上下文信息,是外部數據和能力的提供者。
- 安全與隱私: 協議在設計上強調了用戶授權和控制,要求在訪問用戶數據或執行操作前必須獲得明確的用戶同意,并提供了相應的安全指導原則。
MCP Server:上下文與能力的提供者
在MCP生態中,MCP Server扮演著至關重要的角色。它是一個遵循MCP規范的服務,其主要職責是向MCP客戶端(即LLM應用)暴露其擁有的資源和能力。
MCP Server提供的核心能力:
-
資源(Resources): 服務器可以提供類似文件的只讀數據。這可以是本地文件系統的文件、數據庫查詢結果、API響應內容等。LLM應用可以通過標準化的請求來讀取這些資源,從而獲得回答問題或執行任務所需的背景信息。
-
工具(Tools): 這是MCP Server最強大的功能之一。服務器可以定義一系列可供LLM調用的函數或方法。例如,一個MCP Server可以提供“發送郵件”、“查詢訂單狀態”、“執行代碼”等工具。當LLM在其推理過程中判斷需要使用某個工具時,它會向服務器發起一個工具調用請求,服務器執行相應的功能后將結果返回給LLM。這極大地擴展了LLM的應用場景,使其從一個“對話者”轉變為一個“行動者”。
-
提示(Prompts): 服務器還可以提供預定義的、可復用的提示模板。這些模板可以包含參數,允許LLM應用動態地生成更復雜、更具針對性的指令。這有助于提高與模型交互的效率和一致性。
MCP Server的實現與部署:
MCP Server的實現是多樣化的。開發者可以使用官方或社區提供的SDK(目前已有Go, C#, Rust, TypeScript等版本)來構建自己的服務器。根據部署需求,服務器可以:
- 以子進程(stdio)形式運行: 適用于本地開發或桌面應用集成,服務器與主應用在同一臺機器上運行。
- 以遠程服務(HTTP/SSE)形式運行: 這是更常見的部署方式,服務器作為一個獨立的Web服務運行,可通過URL被多個客戶端訪問。
微軟的Copilot Studio等產品已經集成了對MCP的支持,允許用戶直接連接到外部的MCP服務器,并將其提供的工具無縫地集成到自己的AI助手中。
基于Spring AI 1.0.0的開發之路
Spring AI作為Spring生態系統的一部分,旨在簡化Java開發者構建AI應用的復雜性。它提供了一套高級API,用于與各種大型語言模型進行交互,并集成了向量數據庫、ETL框架等常用工具。
盡管在當前時間點(2025年8月),Spring AI與MCP協議之間尚未出現官方的、開箱即用的直接集成,但這并不妨礙我們利用Spring AI的強大功能來開發一個符合MCP規范的應用。開發者可以扮演MCP客戶端或MCP服務器的角色。
1. 使用Spring AI構建MCP客戶端
當你的Java應用需要連接一個已有的MCP服務器時,你可以利用Spring AI的核心能力來消費MCP服務。
開發步驟概覽:
-
理解MCP通信: 首先需要熟悉MCP的JSON-RPC 2.0消息格式和通信流程。你需要知道如何構建初始化請求、工具調用請求等。
-
HTTP客戶端實現: 使用Spring框架中強大的
WebClient
或RestClient
來與遠程的MCP Server(通常是HTTP服務)進行通信。你需要手動構建符合MCP規范的JSON請求體,并解析服務器返回的JSON響應。 -
集成Spring AI
ChatClient
: 你的應用核心部分可能仍在使用Spring AI的ChatClient
與LLM進行交互。當ChatClient
返回的AssistantMessage
表明需要調用一個外部工具時,你的代碼需要:- 解析出需要調用的工具名稱和參數。
- 判斷該工具是否由某個已連接的MCP Server提供。
- 通過前述的HTTP客戶端,向對應的MCP Server發起工具調用請求。
- 將MCP Server返回的結果包裝成Spring AI的
ToolResponseMessage
,再次提交給ChatClient
,讓LLM根據工具執行結果繼續生成回應。
2. 使用Spring AI構建MCP服務器
當你想將你的Java應用所擁有的數據或能力通過MCP協議暴露出去時,你可以構建一個MCP Server。
開發步驟概覽:
-
創建Spring Boot應用: 使用Spring Boot快速搭建一個Web服務,這將是你的MCP Server的基礎。
-
定義MCP的API端點: 根據MCP規范,創建處理JSON-RPC請求的Controller。你需要實現如
initialize
、tool/run
等核心端點。 -
利用Spring AI實現工具功能: 這是將Spring AI與MCP Server結合的關鍵。你的MCP Server所暴露的“工具”,其內部邏輯可以完全由Spring AI來驅動。
- 示例:創建一個“智能文檔問答”工具
- 工具定義: 在你的MCP Server中,你會向客戶端聲明一個名為
askDocument
的工具,它接受一個question
作為參數。 - 內部實現: 當收到
tool/run
請求調用askDocument
時,你的Spring Boot應用會:- 使用Spring AI的
VectorStore
接口,將用戶的question
進行向量化,并在你預先加載好的文檔向量庫中進行相似性搜索,檢索出最相關的文檔片段。 - 將這些相關的文檔片段作為上下文,連同用戶的原始問題,一起構建成一個更豐富的提示(Prompt)。
- 調用Spring AI的
ChatClient
,將這個豐富的提示發送給LLM(如GPT-4, Claude 3等)。 - 將LLM返回的答案作為
askDocument
工具的執行結果,通過MCP的響應格式返回給客戶端。
- 使用Spring AI的
- 工具定義: 在你的MCP Server中,你會向客戶端聲明一個名為
- 示例:創建一個“智能文檔問答”工具
通過這種方式,你將Spring AI在文檔檢索、與LLM交互等方面的能力,封裝成了一個符合MCP標準的、可被任何MCP客戶端調用的強大工具。
Mcp Client 實戰
整體架構概覽
MCP-Client框架采用分層架構設計,主要包含以下核心組件:
┌─────────────────────────────────────────────────────────────┐
│ Web Layer (控制層) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ChatController │ │ 其他Controller │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘│
┌─────────────────────────────────────────────────────────────┐
│ Service Layer (服務層) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ChatClient │ │ ToolCallbacks │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘│
┌─────────────────────────────────────────────────────────────┐
│ Integration Layer (集成層) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Spring AI │ │ MCP Protocol │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘│
┌─────────────────────────────────────────────────────────────┐
│ Utility Layer (工具層) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ MarkdownUtil │ │ 其他工具類 │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
技術棧
- Spring Boot 3.x:提供基礎框架支持和自動配置
- Spring AI:集成AI模型和工具調用能力
- MCP Protocol:實現模型上下文協議標準
Code
pom
父工程bom
<?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>com.example</groupId><artifactId>spring-ai-mcp-deepseek</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.3</version><relativePath/> <!-- lookup parent from repository --></parent><modules><module>mcp-server</module><module>mcp-client</module><module>mcp-tool-client</module></modules><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><spring.ai.version>1.0.0</spring.ai.version><spring.boot.version>3.4.3</spring.boot.version></properties><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><dependencies><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies>
</project>
mcp-client 子模塊pom
<?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"><parent><artifactId>spring-ai-mcp-deepseek</artifactId><groupId>com.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>mcp-client</artifactId><dependencies><!-- spring-web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- model openai --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><!-- 添加 Spring AI MCP client 依賴 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client</artifactId></dependency><dependency><groupId>org.commonmark</groupId><artifactId>commonmark</artifactId><version>0.21.0</version></dependency><dependency><groupId>org.commonmark</groupId><artifactId>commonmark-ext-gfm-tables</artifactId><version>0.21.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
配置mcp servers(sse&stdio)
application.properties
server.port=8082spring.ai.openai.api-key=sk-xxxxxxx
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.chat.options.model=deepseek-chatspring.ai.mcp.client.enabled=truespring.ai.mcp.client.sse.connections.server1.url=http://localhost:8085spring.ai.mcp.client.stdio.servers-configuration=classpath:mcp-servers-config.jsonlogging.level.org.springframework.ai.model.tool=DEBUG
logging.level.io.modelcontextprotocol=DEBUG
配置項詳解
spring.ai.openai.api-key=sk-xxxxxx
這是Spring AI OpenAI模塊的配置項,用于設置訪問DeepSeek API所需的API密鑰。這個密鑰用于身份驗證,允許應用調用DeepSeek的AI模型服務。
spring.ai.openai.base-url=https://api.deepseek.com
這個配置項指定了OpenAI兼容API的基礎URL。由于項目使用的是DeepSeek API而不是真正的OpenAI API,所以需要將基礎URL設置為DeepSeek的API端點。
spring.ai.openai.chat.options.model=deepseek-chat
此配置項指定要使用的具體AI模型。在這里,使用的是DeepSeek的"deepseek-chat"模型,這是DeepSeek的主要對話模型。
# 啟用 MCP client
spring.ai.mcp.client.enabled=true
這個配置項用于啟用或禁用MCP(Model Context Protocol)客戶端功能。設置為true表示啟用MCP客戶端,允許應用與MCP服務器進行交互。
# 配置 MCP 服務端連接地址
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8085
這個配置項指定了通過SSE(Server-Sent Events)方式連接的MCP服務器的URL。在這里,MCP客戶端將連接到本地8085端口的MCP服務器。
# 配置 stdio 方式啟動的 MCP 服務
spring.ai.mcp.client.stdio.servers-configuration=classpath:mcp-servers-config.json
這個配置項指定了通過標準輸入輸出(stdio)方式啟動的MCP服務的配置文件位置。該配置文件定義了需要啟動的MCP服務及其參數,例如文件系統服務或高德地圖服務。
# 設置日志級別以便觀察工具調用
logging.level.org.springframework.ai.model.tool=DEBUG
logging.level.io.modelcontextprotocol=DEBUG
這兩個配置項設置了特定包的日志級別為DEBUG,以便更詳細地觀察工具調用過程:
logging.level.org.springframework.ai.model.tool=DEBUG
:啟用Spring AI工具調用的調試日志logging.level.io.modelcontextprotocol=DEBUG
:啟用MCP協議相關組件的調試日志
這些配置項共同構成了MCP客戶端應用的完整配置,使應用能夠連接到DeepSeek AI服務和MCP服務器,并提供詳細的調試信息以觀察工具調用過程。
mcp-servers-config.json
{"mcpServers": {"amap-maps": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@amap/amap-maps-mcp-server"],"env": {"AMAP_MAPS_API_KEY": "xxxxxx"}},"filesystem": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@modelcontextprotocol/server-filesystem","D:\\tmp"]}}
}
配置項詳解
amap-maps 服務配置
"amap-maps": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@amap/amap-maps-mcp-server"],"env": {"AMAP_MAPS_API_KEY": "eb2e8e2d3e2d24188cc28ec46903b331"}
}
高德地圖(AMAP)MCP 服務的配置:
"amap-maps"
: 服務的標識符名稱,用于在系統中唯一標識這個服務- command: 指定啟動服務的命令,這里是使用 Node.js 的
npx
命令 - args: 命令行參數數組,用于傳遞給 command 命令
-y
: 表示自動確認安裝依賴@amap/amap-maps-mcp-server
: 指定要運行的 npm 包
- env: 環境變量配置
AMAP_MAPS_API_KEY
: 高德地圖 API 密鑰,用于訪問高德地圖服務
當系統需要使用高德地圖功能時,會通過這個配置啟動相應的 MCP 服務。
KEY申請,請參考: https://lbs.amap.com/api/mcp-server/create-project-and-key
filesystem 服務配置
"filesystem": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@modelcontextprotocol/server-filesystem","D:\\tmp"]
}
這是一個文件系統 MCP 服務的配置:
"filesystem"
: 服務的標識符名稱- command : 同樣使用 Node.js 的
npx
命令啟動服務 - args : 命令行參數數組
-y
: 自動確認安裝依賴@modelcontextprotocol/server-filesystem
: 指定要運行的文件系統服務 npm 包D:\tmp
: 指定文件系統服務可以訪問的目錄路徑
這個服務允許 AI 模型訪問本地文件系統,例如讀取、寫入或瀏覽指定目錄中的文件。
工作原理
這些配置定義了可以通過標準輸入輸出(stdio)方式啟動的 MCP 服務。當 AI 模型需要使用特定功能(如訪問地圖信息或文件系統)時,系統會根據這些配置啟動相應的服務進程,并通過標準輸入輸出與之通信。
每個服務都是獨立的進程,通過 MCP 協議與主應用通信,這樣可以實現功能的模塊化和擴展性。當不再需要這些服務時,系統會自動終止相關進程以節省資源。
ChatClient核心模塊
ChatClient是框架的核心組件,負責AI對話的處理和工具調用的協調。
@RestController
public class ChatController {private ChatClient chatClient;public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider toolCallbackProvider) {this.chatClient = chatClientBuilder.defaultSystem("作為MCP小助手,請根據具體需求智能調用最合適的MCP工具組合來優化回答效果。" +"要求能夠自動識別任務類型,精準匹配工具鏈,并在響應中保持專業性與實用性的平衡。" +"請確保輸出結果既符合技術規范又具備良好的用戶體驗,同時支持多輪交互中的上下文連貫處理。")// 注冊工具方法.defaultToolCallbacks(toolCallbackProvider).build();}}
實現原理分析:
- 構建器模式:使用ChatClient.Builder實現靈活的客戶端配置
- 系統提示注入:通過defaultSystem()方法設置AI助手的角色定位
- 工具回調注冊:defaultToolCallbacks()注冊MCP工具回調處理器
- 鏈式調用:支持流暢的API調用鏈
工具回調機制
工具回調機制是MCP-Client的核心特性,實現AI與外部工具的無縫集成:
// 工具調用流程
String content = chatClient.prompt().user(message) // 用戶輸入.call() // 執行AI推理.content(); // 獲取響應內容
執行流程:
用戶輸入 → AI模型分析 → 識別工具需求 → 調用MCP工具 → 整合結果 → 返回響應↓ ↓ ↓ ↓ ↓ ↓message → ChatClient → ToolCallback → MCP Server → Result → content
典型應用場景代碼示例
基礎對話場景
package com.example.client.controller;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;@RestController
public class BasicChatExample {private final ChatClient chatClient;public BasicChatExample(ChatClient.Builder chatClientBuilder) {this.chatClient = chatClientBuilder.build();}@GetMapping("/api/simple-chat")public ResponseEntity<String> simpleChat(@RequestParam String question) {try {String response = chatClient.prompt().user(question).call().content();return ResponseEntity.ok(response);} catch (Exception e) {return ResponseEntity.status(500).body("處理請求時發生錯誤: " + e.getMessage());}}
}
工具增強對話場景
package com.example.client.controller;import com.example.client.domain.ChatRequest;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestController
public class ToolEnhancedChatExample {private final ChatClient toolEnabledClient;public ToolEnhancedChatExample(ChatClient.Builder builder,ToolCallbackProvider toolProvider) {this.toolEnabledClient = builder.defaultSystem("作為MCP小助手,請根據具體需求智能調用最合適的MCP工具組合來優化回答效果。" +"要求能夠自動識別任務類型,精準匹配工具鏈,并在響應中保持專業性與實用性的平衡。" +"請確保輸出結果既符合技術規范又具備良好的用戶體驗,同時支持多輪交互中的上下文連貫處理。").defaultToolCallbacks(toolProvider).build();}@PostMapping("/api/tool-chat")public Map<String, Object> toolChat(@RequestBody ChatRequest request) {long startTime = System.currentTimeMillis();String response = toolEnabledClient.prompt().user(request.getMessage()).call().content();long duration = System.currentTimeMillis() - startTime;return Map.of("response", response,"duration", duration,"timestamp", System.currentTimeMillis());}
}
日志輸出
2025-08-12T19:43:55.811+08:00 DEBUG 12292 --- [nio-8082-exec-4] o.s.a.m.tool.DefaultToolCallingManager : Executing tool call: spring_ai_mcp_client_amap_maps_maps_weather
2025-08-12T19:43:56.030+08:00 DEBUG 12292 --- [pool-7-thread-1] io.modelcontextprotocol.spec.McpSchema : Received JSON message: {"result":{"content":[{"type":"text","text":"{\n \"city\": \"北京市\",\n \"forecasts\": [\n {\n \"date\": \"2025-08-12\",\n \"week\": \"2\",\n \"dayweather\": \"中雨\",\n \"nightweather\": \"陰\",\n \"daytemp\": \"30\",\n \"nighttemp\": \"22\",\n \"daywind\": \"西南\",\n \"nightwind\": \"西南\",\n \"daypower\": \"1-3\",\n \"nightpower\": \"1-3\",\n \"daytemp_float\": \"30.0\",\n \"nighttemp_float\": \"22.0\"\n },\n {\n \"date\": \"2025-08-13\",\n \"week\": \"3\",\n \"dayweather\": \"雷陣雨\",\n \"nightweather\": \"雷陣雨\",\n \"daytemp\": \"29\",\n \"nighttemp\": \"22\",\n \"daywind\": \"東\",\n \"nightwind\": \"東\",\n \"daypower\": \"1-3\",\n \"nightpower\": \"1-3\",\n \"daytemp_float\": \"29.0\",\n \"nighttemp_float\": \"22.0\"\n },\n {\n \"date\": \"2025-08-14\",\n \"week\": \"4\",\n \"dayweather\": \"陰\",\n \"nightweather\": \"雷陣雨\",\n \"daytemp\": \"31\",\n \"nighttemp\": \"23\",\n \"daywind\": \"南\",\n \"nightwind\": \"南\",\n \"daypower\": \"1-3\",\n \"nightpower\": \"1-3\",\n \"daytemp_float\": \"31.0\",\n \"nighttemp_float\": \"23.0\"\n },\n {\n \"date\": \"2025-08-15\",\n \"week\": \"5\",\n \"dayweather\": \"雷陣雨\",\n \"nightweather\": \"雷陣雨\",\n \"daytemp\": \"31\",\n \"nighttemp\": \"24\",\n \"daywind\": \"南\",\n \"nightwind\": \"南\",\n \"daypower\": \"1-3\",\n \"nightpower\": \"1-3\",\n \"daytemp_float\": \"31.0\",\n \"nighttemp_float\": \"24.0\"\n }\n ]\n}"}],"isError":false},"jsonrpc":"2.0","id":"2b682735-4"}
2025-08-12T19:43:56.031+08:00 DEBUG 12292 --- [pool-7-thread-1] i.m.spec.McpClientSession : Received Response: JSONRPCResponse[jsonrpc=2.0, id=2b682735-4, result={content=[{type=text, text={"city": "北京市","forecasts": [{"date": "2025-08-12","week": "2","dayweather": "中雨","nightweather": "陰","daytemp": "30","nighttemp": "22","daywind": "西南","nightwind": "西南","daypower": "1-3","nightpower": "1-3","daytemp_float": "30.0","nighttemp_float": "22.0"},{"date": "2025-08-13","week": "3","dayweather": "雷陣雨","nightweather": "雷陣雨","daytemp": "29","nighttemp": "22","daywind": "東","nightwind": "東","daypower": "1-3","nightpower": "1-3","daytemp_float": "29.0","nighttemp_float": "22.0"},{"date": "2025-08-14","week": "4","dayweather": "陰","nightweather": "雷陣雨","daytemp": "31","nighttemp": "23","daywind": "南","nightwind": "南","daypower": "1-3","nightpower": "1-3","daytemp_float": "31.0","nighttemp_float": "23.0"},{"date": "2025-08-15","week": "5","dayweather": "雷陣雨","nightweather": "雷陣雨","daytemp": "31","nighttemp": "24","daywind": "南","nightwind": "南","daypower": "1-3","nightpower": "1-3","daytemp_float": "31.0","nighttemp_float": "24.0"}]
}}], isError=false}, error=null]
流式響應場景
package com.example.client.controller;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
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;@RestController
public class StreamingChatExample {private final ChatClient chatClient;public StreamingChatExample(ChatClient.Builder chatClientBuilder) {this.chatClient = chatClientBuilder.build();}@GetMapping(value = "/api/stream-chat", produces = "text/html;charset=UTF-8")public Flux<String> streamChat(@RequestParam String message) {return chatClient.prompt().user(message).stream().content().map(chunk -> "data: " + chunk + "\n\n");}@GetMapping(value = "/api/stream-chat-2", produces = "text/html;charset=UTF-8")public Flux<String> streamChat() {return chatClient.prompt().user("你是誰").stream().content();}
}
MCP Server調用
package com.example.client.controller;import com.example.client.domain.ChatRequest;
import com.example.client.util.MarkdownUtil;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;import java.util.Map;/*** 聊天控制器,處理AI聊天請求*/
@RestController
public class ChatController {private ChatClient chatClient;public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider toolCallbackProvider) {this.chatClient = chatClientBuilder.defaultSystem("作為MCP小助手,請根據具體需求智能調用最合適的MCP工具組合來優化回答效果。" +"要求能夠自動識別任務類型,精準匹配工具鏈,并在響應中保持專業性與實用性的平衡。" +"請確保輸出結果既符合技術規范又具備良好的用戶體驗,同時支持多輪交互中的上下文連貫處理。")// 注冊工具方法.defaultToolCallbacks(toolCallbackProvider).build();}@RequestMapping(value = "/chat", produces = MediaType.TEXT_HTML_VALUE)public String chatPage(@RequestParam String message) {System.out.println(">>> Q: " + message);// 記錄開始時間long startTime = System.currentTimeMillis();// 使用API調用聊天String content = chatClient.prompt().user(message).call().content();// 計算AI響應耗時long aiResponseTime = System.currentTimeMillis();long aiDuration = aiResponseTime - startTime;System.out.println(">>> A:" + content);// Markdown轉換耗時統計long markdownStartTime = System.currentTimeMillis();String htmlPage = MarkdownUtil.toHtmlPage(content);long markdownEndTime = System.currentTimeMillis();long markdownDuration = markdownEndTime - markdownStartTime;long totalDuration = markdownEndTime - startTime;System.out.println(">>> 耗時統計 - AI響應: " + aiDuration + "ms, Markdown轉換: " + markdownDuration + "ms, 總計: " + totalDuration + "ms");return htmlPage;}}
調用高德 MCP Server (stdio)
調用server-filesystem MCP Server (stdio)
調用自己寫的服務(sse)
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8085
2025-08-12T21:05:32.615+08:00 WARN 56008 --- [nio-8082-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'message' for method parameter type String is not present]
>>> Q: 有多少種書
2025-08-12T21:06:15.405+08:00 DEBUG 56008 --- [nio-8082-exec-1] o.s.a.m.tool.DefaultToolCallingManager : Executing tool call: spring_ai_mcp_client_server1_getBookCategories
2025-08-12T21:06:15.623+08:00 DEBUG 56008 --- [ient-1-Worker-8] io.modelcontextprotocol.spec.McpSchema : Received JSON message: {"jsonrpc":"2.0","id":"b016e092-6","result":{"content":[{"type":"text","text":"[\"Web服務器\",\"容器技術\",\"數據庫\",\"架構設計\",\"編程\",\"計算機科學\"]"}],"isError":false}}
2025-08-12T21:06:15.630+08:00 DEBUG 56008 --- [ient-1-Worker-8] i.m.spec.McpClientSession : Received Response: JSONRPCResponse[jsonrpc=2.0, id=b016e092-6, result={content=[{type=text, text=["Web服務器","容器技術","數據庫","架構設計","編程","計算機科學"]}], isError=false}, error=null]
>>> A:目前系統中有以下6種圖書分類:1. Web服務器
2. 容器技術
3. 數據庫
4. 架構設計
5. 編程
6. 計算機科學如果需要查詢某一分類下的圖書,可以告訴我具體分類名稱。
>>> 耗時統計 - AI響應: 12955ms, Markdown轉換: 5ms, 總計: 12960ms
性能優化建議
緩存策略
@Service
@EnableCaching
public class CachedChatService {@Cacheable(value = "chatResponses", key = "#message.hashCode()")public String getCachedResponse(String message) {return chatClient.prompt().user(message).call().content();}@CacheEvict(value = "chatResponses", allEntries = true)@Scheduled(fixedRate = 3600000) // 1小時清理一次public void clearCache() {// 定期清理緩存}
}
異步處理優化
@Service
public class AsyncChatService {@Async("chatExecutor")public CompletableFuture<String> processAsync(String message) {String response = chatClient.prompt().user(message).call().content();return CompletableFuture.completedFuture(response);}@Bean("chatExecutor")public TaskExecutor chatExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(4);executor.setMaxPoolSize(8);executor.setQueueCapacity(100);executor.setThreadNamePrefix("chat-");executor.initialize();return executor;}
}
最佳實踐建議
架構設計最佳實踐
- 分層清晰:嚴格按照控制層、服務層、集成層的分層架構設計
- 接口抽象:為核心組件定義清晰的接口,便于測試和擴展
- 配置外部化:所有可變參數通過配置文件管理
- 異常處理:建立統一的異常處理機制
性能優化最佳實踐
- 連接復用:使用連接池管理HTTP連接
- 緩存策略:對頻繁訪問的數據實施緩存
- 異步處理:對耗時操作采用異步處理
- 資源管理:及時釋放不再使用的資源
安全性最佳實踐
- 輸入驗證:對所有用戶輸入進行嚴格驗證
- 輸出轉義:防止XSS攻擊
- API密鑰管理:使用環境變量管理敏感信息
- 訪問控制:實施適當的訪問控制機制