利用 Spring AI 進行 MCP 引出
1. 概述
在使用模型上下文協定 (MCP) 時,有時 MCP 伺服器需要在工具執行期間從使用者獲取一些原始請求中未包含的額外資訊。如果沒有標準化的方法來要求這些訊息,工具就無法將這些訊息回饋給客戶端,從而導致執行失敗。
MCP 資訊取得機制透過允許 MCP 伺服器暫停並明確地向使用者請求缺失資訊來解決這個問題。這使我們能夠建立能夠動態收集額外上下文資訊的互動式工具。
在本教程中,我們將探討如何使用 Spring AI 實作 MCP 請求。
2. 建立 MCP 伺服器
我們首先建立一個 MCP 伺服器,該伺服器提供一個用於獲取作者詳細資訊的工具。
我們將設計此工具,以便在原始請求中缺少詳細資訊時,有條件地觸發獲取詳細資訊的請求。
2.1. 依賴關係和配置
首先,讓我們將必要的依賴項新增到專案的pom.xml檔案中:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.1.2</version>
</dependency>
我們導入Spring AI 的 MCP 伺服器依賴項,它提供了建立自訂 HTTP MCP 伺服器所需的類別。
接下來,我們編輯application.properties文件,將我們的應用程式配置為 MCP 伺服器:
spring.ai.mcp.server.name=author-server
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.protocol=streamable
在這裡,我們為 MCP 伺服器配置一個名稱,將其設定為同步,並將傳輸類型指定為可串流 HTTP 。
2.2. 定義工具
接下來,我們來定義一個 MCP 伺服器將公開的工具。
我們將創建一個AuthorRepository類,該類提供一個方法,用於根據文章標題獲取作者詳細資訊。如果要求的文章是付費文章,我們會在返回作者詳細資訊之前向用戶索取其他資訊:
`private static final Logger log = LoggerFactory.getLogger(AuthorRepository.class);
@McpTool(description = "Get Baeldung author details using an article title")
Author getAuthorByArticleTitle(
@McpToolParam(description = "Title/name of the article") String articleTitle,
@McpToolParam(required = false, description = "Name of user requesting author information") String username,
@McpToolParam(required = false, description = "Reason for requesting author information") String reason,
McpSyncRequestContext requestContext
) {
log.info("Author requested for article: {}", articleTitle);
if (isPremiumArticle(articleTitle)) {
log.info("Article is premium, further information required");
if ((isBlank(username) || isBlank(reason)) && requestContext.elicitEnabled()) {
log.info("Required details missing, initiating elicitation");
StructuredElicitResult
e -> e.message("Baeldung username and reason required."),
PremiumArticleAccessRequest.class
);
if (McpSchema.ElicitResult.Action.ACCEPT.equals(elicitResult.action())) {
username = elicitResult.structuredContent().username();
reason = elicitResult.structuredContent().reason();
log.info("Elicitation accepted - username: {}, reason: {}", username, reason);
}
}
if (isSubscriber(username) && isValidReason(reason)) {
log.info("Access granted, returning author details");
return new Author("John Doe", "[email protected]");
}
}
return null;
}
record Author(String name, String email) {
}
record PremiumArticleAccessRequest(String username, String reason) {
}`
我們使用@McpTool註解對getAuthorByArticleTitle()方法進行標註,使其作為 MCP 工具公開。此方法接受articleTitle作為必需參數,以及可選的username和reason參數。
此外,我們將McpSyncRequestContext作為方法參數注入,從而可以存取當前請求的元數據,並使我們能夠向 MCP 客戶端發起請求。
如果要求的文章是付費文章,並且缺少任何可選參數,我們會使用elicit()方法觸發一個資訊獲取請求。我們會傳遞一則訊息,說明需要哪些訊息,以及定義預期回應結構的模式。
我們也應該注意到,在發起此請求之前,我們會呼叫elicitEnabled()方法來檢查連線的 MCP 用戶端是否支援請求,因為嘗試從不支援的用戶端請求請求會導致錯誤。
如果使用者接受請求並提供有效的詳細信息,我們將從結果中提取username和reason ,並進行授權檢查,然後再返回硬編碼的作者詳細資訊。
在我們的示範中,私有方法isPremiumArticle() 、 isSubscriber()和isValidReason()總是傳回true 。
3. 建立 MCP 主機
現在我們的 MCP 伺服器已經準備就緒,我們需要一個應用程式來使用它。
我們將使用 Anthropic 的 Claude 模型來建立一個聊天機器人,它將作為我們的 MCP 主機。或者,我們也可以使用 Hugging Face 或 Ollama 等本地 LLM,因為對於本次演示而言,具體的 AI 模型並不重要。
本節我們將建立一個新的 Spring Boot 應用程式。
3.1. 依賴關係和 LLM 配置
首先,讓我們在pom.xml檔案中加入必要的依賴項:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
<version>1.1.2</version>
</dependency>
Anthropic starter 依賴項是Anthropic Message API 的包裝器,我們將使用它在我們的應用程式中與 Claude 模型進行互動。
此外,我們匯入MCP 用戶端啟動器依賴項,這將允許我們在 Spring Boot 應用程式內配置客戶端,以與 MCP 伺服器保持 1:1 連線。
接下來,讓我們在application.properties檔案中設定Anthropic API 金鑰和聊天模型:
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
spring.ai.anthropic.chat.options.model=claude-opus-4-5-20251101
我們使用${}屬性佔位符從環境變數載入 API 金鑰的值。
此外,我們指定使用 Anthropic 公司的Claude Opus 4.5 ,型號 ID 為claude-opus-4-5-20251101 。您可以根據實際需求探索並使用其他型號。
設定了這兩個屬性後, Spring AI 會自動建立一個ChatModel類型的 bean,讓我們可以與指定的模型進行互動。
3.2. 設定 MCP 用戶端並啟用 MCP 請求
最後,為了在我們的聊天機器人應用程式中使用我們自訂的 MCP 伺服器,我們需要針對它配置一個 MCP 用戶端:
spring.ai.mcp.client.capabilities.elicitation={}
spring.ai.mcp.client.streamable-http.connections.author-server.url=http://localhost:8081/mcp
在我們的application.properties文件中,我們首先啟用 MCP 請求獲取,這樣,如果工具需要其他信息,MCP 伺服器就可以發送請求獲取回复。
接下來,我們使用可串流 HTTP 傳輸類型,為自訂 MCP 伺服器設定一個新的客戶端。我們的設定假設 MCP 伺服器運行在http://localhost:8081/mcp 。如果伺服器運行在不同的主機或連接埠上,我們需要更新url屬性。
應用程式啟動時,Spring AI 會掃描我們的配置,建立 MCP 用戶端,並與對應的 MCP 伺服器建立連線。此外,它還會建立一個類型為SyncMcpToolCallbackProvider的 bean,該 bean 提供已配置的 MCP 伺服器公開的所有工具的清單。
3.3 建構一個基本的聊天機器人
LLM 和 MCP 用戶端配置完成後,讓我們建立一個簡單的聊天機器人。
我們將首先使用自動配置的ChatModel和SyncMcpToolCallbackProvider bean 來建立一個ChatClient類型的 bean:
@Bean
ChatClient chatClient(ChatModel chatModel, SyncMcpToolCallbackProvider toolCallbackProvider) {
return ChatClient
.builder(chatModel)
.defaultToolCallbacks(toolCallbackProvider.getToolCallbacks())
.build();
}
ChatClient類別將作為我們與聊天完成模型(即 Claude Opus 4.5)互動的主要入口點。
接下來,讓我們在控制器類別中註入ChatClient bean 並公開 REST API:
@PostMapping("/chat")
ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest chatRequest) {
String answer = chatClient
.prompt()
.user(chatRequest.question())
.call()
.content();
return ResponseEntity.ok(new ChatResponse(answer));
}
record ChatRequest(String question) {
}
record ChatResponse(String answer) {
}
在這裡,我們只需將使用者的question傳遞給chatClient bean,並傳回 LLM 的回應。稍後在本教學中,我們將使用此 API 端點與聊天機器人互動。
3.4 處理詢價請求
當 MCP 伺服器發起請求時,MCP 用戶端必須有機制來攔截此請求並提供必要的資料。
讓我們在配置類別中定義一個方法來處理這些請求:
private static final Logger log = LoggerFactory.getLogger(ChatbotConfiguration.class);
@McpElicitation(clients = "author-server")
ElicitResult handleElicitation(ElicitRequest elicitRequest) {
log.info("Elicitation requested: {}", elicitRequest.message());
log.info("Requested schema: {}", elicitRequest.requestedSchema());
return new ElicitResult(
ElicitResult.Action.ACCEPT,
Map.of(
"username", "john.smith",
"reason", "Contacting author for article feedback"
)
);
}
我們使用@McpElicitation註解handleElicitation()方法,並將clients屬性指定為author-server以便將其連結到我們先前在application.properties檔案中配置的客戶端。
當 MCP 伺服器觸發請求時,此處理程序會收到包含訊息和請求模式的ElicitRequest 。
在我們的處理程序中,我們會記錄請求詳情,並傳回一個包含ACCEPT作業和請求詳情的ElicitResult物件。為了演示,我們傳回的是硬編碼值。在生產應用程式中,MCP 用戶端通常會提示使用者輸入這些資訊。
值得注意的是,MCP 請求取得也支援 URL 模式,伺服器會將使用者重新導向至外部 URL 以請求敏感資訊或執行受保護的操作。這確保了敏感資料永遠不會經過 MCP 客戶端。但是,截至撰寫本文時,Spring AI 尚不支援 URL 模式請求取得。
4. 與我們的聊天機器人互動
現在我們已經建立了 MCP 伺服器和主機應用程序,讓我們與聊天機器人進行互動並測試請求流程。
我們將使用 HTTPie CLI 來呼叫聊天機器人的 API 端點:
http POST :8080/chat question="Who wrote the article 'Testing CORS in Spring Boot?' on Baeldung, and how can I contact them?"
在這裡,我們發送一個簡單的問題,詢問有關特定文章作者的詳細資訊。我們刻意不在查詢中指定使用者名稱或原因,以觸發 MCP 伺服器上的資訊收集流程。
讓我們看看會得到怎樣的回覆:
`{
"answer": "The article 'Testing CORS in Spring Boot' on Baeldung was written by John Doe. You can contact him via email at [[email protected]](mailto:[email protected])."
}`
正如我們所看到的,聊天機器人成功地返回了作者的詳細資料。
讓我們查看聊天機器人的日誌,以確認是否已收到請求:
`[2026-01-28 13:16:25] [INFO] [cbsmcChatbotConfiguration] - Elicitation requested: Baeldung username and reason required.
[2026-01-28 13:16:25] [INFO] [cbsmcChatbotConfiguration] - Requested schema: {type=object, properties={reason={type=string}, username={type=string}}, required=[reason, username]}`
日誌證實,我們的請求處理程序已收到來自 MCP 伺服器的請求,包括訊息和預期的回應模式。
現在,讓我們再檢查一下 MCP 伺服器的日誌,以確認整個流程:
`[2026-01-28 15:28:00] [INFO] [cbsmsAuthorRepository] - Author requested for article: Testing CORS in Spring Boot
[2026-01-28 15:28:00] [INFO] [cbsmsAuthorRepository] - Article is premium, further information required
[2026-01-28 15:28:00] [INFO] [cbsmsAuthorRepository] - Required details missing, initiating elicitation
[2026-01-28 15:28:00] [INFO] [cbsmsAuthorRepository] - Elicitation accepted - username: john.smith, reason: Contacting author for article feedback
[2026-01-28 15:28:00] [INFO] [cbsmsAuthorRepository] - Access granted, returning author details`
在這裡,該工具檢測到文章是付費文章,發起了一個請求來收集缺失的參數,從我們的請求處理程序接收了硬編碼的值,最後執行了工具邏輯來返回作者詳細資訊。
5. 結論
在本文中,我們探討如何使用 Spring AI 實作 MCP 請求。
我們建立了一個 MCP 伺服器,該伺服器提供了一個工具,在存取高級內容時請求額外資訊。然後,我們建立了一個 MCP 主機應用程序,其中包含一個請求處理程序,用於回應這些請求。
最後,我們測試了我們的實現,並透過應用程式日誌驗證了資訊獲取流程。這種模式支援更具互動性的 AI 應用,工具可以在需要時從使用者收集更多上下文資訊。
與往常一樣,本文中使用的所有程式碼範例都可以在 GitHub 上找到。