- Spring AI 開發(fā) MCP Server(Streamable HTTP)完整開發(fā)與測試:把 endpoint 改到 TaoToken)
1. 從 stdio 到 Streamable HTTP為什么要把 MCP Server 的 endpoint 改到 TaoToken如果你已經(jīng)用 Spring AI 寫過 stdio 版本的 MCP Server大概率會遇到一個尷尬本地 IDE 能跑但一旦想讓遠程客戶端、團隊其他成員或者 CI 環(huán)境調(diào)用stdio 的進程間通信模型就完全不夠用了。stdio 的本質(zhì)是 MCP Client 和 MCP Server 跑在同一臺主機上靠標(biāo)準(zhǔn)輸入輸出做 JSON-RPC 2.0 的消息交換適合本地工具接入但沒法跨網(wǎng)絡(luò)。Streamable HTTP 就是來解決這個問題的。它基于 HTTP 協(xié)議MCP Client 可以遠程調(diào)用 MCP Server 暴露的 endpoint服務(wù)端通過流式響應(yīng)把工具調(diào)用結(jié)果推回來。相比早期的 SSE 方案Streamable HTTP 的通信模型更統(tǒng)一新版本的客戶端和服務(wù)端都更偏向它。Spring AI 從 1.1.x 開始對 Streamable HTTP 的支持已經(jīng)比較完整依賴是spring-ai-starter-mcp-server-webmvc底層走 Servlet 棧。那為什么要把 endpoint 改到 TaoToken因為 MCP Server 本身只負責(zé)暴露工具真正要驗證工具調(diào)用鏈路是否正常需要一個能發(fā)起模型請求的客戶端。TaoToken 提供統(tǒng)一的 API endpoint兼容主流模型調(diào)用格式你可以把 MCP Client 的模型請求指向 TaoToken讓模型來決定調(diào)用哪個工具、傳什么參數(shù)然后觀察 Streamable HTTP 的流式返回是否正常。這樣整條鏈路——模型決策、工具調(diào)用、流式響應(yīng)——就都能在真實請求下驗證。這篇面向的是已經(jīng)了解 MCP 基本概念、想動手把 Spring AI MCP Server 跑起來并完成端到端測試的 Java 開發(fā)者。我會從依賴引入開始給出可復(fù)制的application.yml、工具注冊代碼、MCP 客戶端配置片段然后演示把 endpoint 改到 TaoToken 后用 curl 和單元測試驗證工具調(diào)用與流式返回。整個過程我試過幾輪踩過的坑會放在排障章節(jié)里。2. TaoToken 前置準(zhǔn)備API Key、Base URL 與模型 ID 三件套在寫代碼之前先把 TaoToken 這邊的接入信息準(zhǔn)備好。不管你是用 Cline、Claude Code 還是自己寫的 MCP Client核心就是三件套Base URL、API Key、Model ID。這三個東西缺一個請求就會在鑒權(quán)或路由階段掛掉。Base URL 用https://taotoken.net/api注意這個地址不帶任何查詢參數(shù)直接作為 API 根路徑。API Key 需要到控制臺創(chuàng)建路徑是 console 頁面下的 api-keys 管理。創(chuàng)建的時候建議按項目命名比如spring-ai-mcp-demo方便后面排查是哪個 key 在調(diào)用。Model ID 根據(jù)你要用的模型填比如claude-sonnet-4-20250514這類標(biāo)識具體以模型列表頁為準(zhǔn)。如果你用的是 Claude Code 這類工具配置方式會略有不同。Claude Code 的接入文檔在 doc 頁面有詳細說明核心是把 Base URL 指向 TaoToken 的 API 地址然后填入 Key。對于 Spring AI 的 MCP Client我們是在application.yml里配置spring.ai.openai.base-url和spring.ai.openai.api-key模型名通過spring.ai.openai.chat.options.model指定。這里有個容易混淆的點MCP Server 的 endpoint 和模型 API 的 endpoint 是兩個不同的東西。MCP Server 暴露的是/mcp這個路徑供 MCP Client 調(diào)用工具而 TaoToken 的/api是模型推理接口供 MCP Client 內(nèi)部的 ChatClient 調(diào)用。兩者不要搞混。我們在application-http.yml里配置的mcp-endpoint: /mcp是前者TaoToken 的 Base URL 是后者。另外如果你打算長期跑編碼類 Agent 任務(wù)可以了解下 Coding Plan它針對高頻編碼場景做了額度優(yōu)化。單純驗證工具調(diào)用的話按量調(diào)用模型對話接口就夠了。需要先拿到 Key 的話直接去 API Keys 頁面創(chuàng)建然后對照接入文檔確認參數(shù)格式。3. 可復(fù)制配置application.yml、工具注冊與 MCP 客戶端片段這一節(jié)給出完整的可復(fù)制配置。工程結(jié)構(gòu)是標(biāo)準(zhǔn)的 Spring Boot Maven 項目Java 17Spring Boot 3.4.xSpring AI 1.1.4。pom 里需要引入spring-ai-starter-mcp-server-webmvc來啟用 Streamable HTTP 模式同時引入spring-ai-starter-model-openai作為 MCP Client 側(cè)的模型調(diào)用依賴。先看application.yml的主配置spring: application: name: java-mcp-server-demo profiles: active: http ai: mcp: server: name: java-mcp-server-demo version: 0.0.1 type: SYNC instructions: | This server provides demo tools for Java MCP development. Use getWeather for city weather lookup and sumNumbers for calculation.然后是application-http.yml這是 Streamable HTTP 模式的核心配置spring: config: activate: on-profile: http main: web-application-type: servlet ai: mcp: server: protocol: STREAMABLE streamable-http: mcp-endpoint: /mcp server: port: 8080注意protocol: STREAMABLE和mcp-endpoint: /mcp這兩行前者指定傳輸協(xié)議后者定義 MCP Client 要訪問的路徑。啟動后完整的 endpoint 是http://localhost:8080/mcp。工具類用Tool和ToolParam注解標(biāo)記Service public class WeatherService { Tool(name getWeather, description 根據(jù)城市名稱獲取天氣信息) public String getWeather( ToolParam(description 城市名稱, required true) String city) { return 城市: city , 溫度: 25°C, 天氣: 晴; } Tool(name sumNumbers, description 計算兩個數(shù)字之和) public String sumNumbers( ToolParam(description 第一個數(shù)字) double a, ToolParam(description 第二個數(shù)字) double b) { return String.valueOf(a b); } }工具注冊配置類把WeatherService里的Tool方法注冊為 MCP 工具Configuration public class ToolRegistrationConfiguration { Autowired private WeatherService weatherService; Bean public ToolCallbackProvider toolCallbackProvider() { return MethodToolCallbackProvider.builder() .toolObjects(new Object[]{weatherService}) .build(); } }接下來是 MCP Client 側(cè)的配置把模型請求指向 TaoToken。在application-client.yml里spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-20250514 temperature: 0.7 mcp: client: streamable-http: connections: demo-server: url: http://localhost:8080/mcp這里base-url指向 TaoToken 的 API 地址api-key從環(huán)境變量讀取避免硬編碼。mcp.client.streamable-http.connections下面配置 MCP Server 的地址demo-server是連接名可以自定義。這樣 MCP Client 啟動時會自動連接本地 MCP Server拉取工具列表然后在模型請求中把工具定義一起發(fā)給 TaoToken。如果你用的是 Cline 或 Claude Code 這類外部客戶端配置方式類似核心還是 Base URL、Key、Model ID 三件套加上 MCP Server 的 URL。Claude Code 的配置在 doc 里有說明Cline 的 MCP 配置在 settings 里填http://localhost:8080/mcp即可。4. 驗證請求curl 與單元測試檢查工具調(diào)用和流式返回配置寫完之后先啟動 MCP Server。用mvn spring-boot:run -Dspring-boot.run.profileshttp啟動看到 start輸出說明服務(wù)起來了。然后分兩步驗證先用 curl 直接打 MCP endpoint確認 JSON-RPC 握手和工具列表正常再用單元測試走完整的模型調(diào)用鏈路。curl 驗證 MCP 初始化請求curl -X POST http://localhost:8080/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d { jsonrpc: 2.0, id: 1, method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: {name: curl-test, version: 1.0} } }正常返回會包含serverInfo和capabilities說明 Streamable HTTP 鏈路通了。注意Accept頭必須同時包含application/json和text/event-stream否則服務(wù)端可能拒絕流式響應(yīng)。接著查工具列表curl -X POST http://localhost:8080/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d { jsonrpc: 2.0, id: 2, method: tools/list, params: {} }返回里應(yīng)該能看到getWeather和sumNumbers兩個工具的定義包括參數(shù) schema。如果這里返回空列表說明工具注冊配置沒生效檢查ToolCallbackProvider是否被 Spring 掃描到。單元測試用 Spring AI 的ChatClient發(fā)起帶工具的請求驗證模型是否會調(diào)用工具SpringBootTest ActiveProfiles(client) class McpClientIntegrationTest { Autowired private ChatClient.Builder chatClientBuilder; Test void shouldCallWeatherTool() { ChatClient chatClient chatClientBuilder.build(); String response chatClient.prompt() .user(北京今天天氣怎么樣) .call() .content(); System.out.println(模型返回: response); assertThat(response).isNotBlank(); } }跑這個測試之前確保TAOTOKEN_API_KEY環(huán)境變量已設(shè)置。測試通過的標(biāo)準(zhǔn)是模型返回了包含天氣信息的文本并且日志里能看到工具調(diào)用的記錄。如果模型直接回答而沒有調(diào)用工具可能是工具描述不夠清晰或者模型不支持 function calling。流式返回的驗證用stream()方法Test void shouldStreamResponse() { ChatClient chatClient chatClientBuilder.build(); FluxString stream chatClient.prompt() .user(幫我算一下 12 加 30 等于多少) .stream() .content(); ListString chunks stream.collectList().block(); System.out.println(流式片段數(shù): chunks.size()); assertThat(chunks).isNotEmpty(); }流式返回正常的話chunks會有多個片段而不是一次性返回完整文本。這一步驗證的是 Streamable HTTP 的流式推送能力如果只收到一個片段說明流式配置有問題。5. 常見報錯排查401、local proxy failed、reading choices 與 OAuth這一節(jié)對照幾個真實報錯給出排查路徑。這些錯誤我在調(diào)試過程中都遇到過按順序檢查基本能定位。401 Unauthorized最常見的原因是 API Key 沒設(shè)置或設(shè)置錯了。檢查TAOTOKEN_API_KEY環(huán)境變量是否在當(dāng)前 shell 會話里生效echo $TAOTOKEN_API_KEY確認一下。如果 Key 是對的檢查base-url是否寫成了https://taotoken.net/api少寫/api或者多寫斜杠都會導(dǎo)致鑒權(quán)失敗。另外注意 Key 有沒有多余空格從控制臺復(fù)制的時候容易帶上換行。local proxy failed / connection refused這個報錯通常出現(xiàn)在 MCP Client 連接 MCP Server 的階段。檢查 MCP Server 是否真的在 8080 端口監(jiān)聽curl http://localhost:8080/mcp能不能通。如果 Server 沒起來檢查application-http.yml里的web-application-type: servlet有沒有配配成none的話 Web 容器不會啟動。還有一種情況是端口被占用換個端口或者殺掉占用進程。reading choices 相關(guān)報錯這個一般出現(xiàn)在模型返回解析階段說明請求發(fā)出去了但響應(yīng)格式不對。檢查model參數(shù)是否填了 TaoToken 支持的模型 ID填錯模型名會導(dǎo)致返回體里沒有choices字段。另外檢查spring.ai.openai.chat.options下的配置有沒有拼寫錯誤比如temperature寫成temprature這種。OAuth / 鑒權(quán)頭缺失如果你用的是 Claude Code 或 Cline 這類客戶端報 OAuth 相關(guān)錯誤通常是客戶端配置里沒填 Key或者填到了錯誤的字段。Claude Code 的配置參考 doc 頁面Cline 的 MCP 配置里 Base URL 和 Key 要分開填。對于 Spring AI 的 MCP Client鑒權(quán)是通過spring.ai.openai.api-key走的不需要額外配 OAuth。工具列表為空MCP Server 起來了但tools/list返回空。檢查ToolRegistrationConfiguration類是否在SpringBootApplication的掃描路徑下WeatherService是否被Service標(biāo)記。還有一個坑是Tool注解的name屬性如果和已有工具重名后面的會覆蓋前面的導(dǎo)致看起來少了工具。流式返回中斷stream()調(diào)用收到部分片段后斷開。檢查Accept頭是否包含text/event-stream以及服務(wù)端有沒有設(shè)置響應(yīng)超時。Spring MVC 的異步請求超時默認是 30 秒長任務(wù)需要調(diào)spring.mvc.async.request-timeout。6. 把 endpoint 改到 TaoToken 后的完整鏈路與后續(xù)方向整條鏈路跑通之后回顧一下數(shù)據(jù)流MCP Client 啟動時連接本地 MCP Server 的/mcpendpoint拉取工具列表用戶發(fā)起請求時ChatClient 把用戶消息和工具定義一起發(fā)給 TaoToken 的/api接口TaoToken 返回模型決策如果決定調(diào)用工具MCP Client 通過 Streamable HTTP 把工具調(diào)用請求發(fā)給 MCP ServerMCP Server 執(zhí)行工具方法把結(jié)果流式返回給 MCP ClientMCP Client 再把工具結(jié)果發(fā)給模型生成最終回答。這個鏈路里TaoToken 承擔(dān)的是模型推理角色MCP Server 承擔(dān)的是工具執(zhí)行角色兩者通過 MCP Client 串聯(lián)。把 endpoint 改到 TaoToken 的意義在于你可以用統(tǒng)一的 API 接口驗證不同模型對工具調(diào)用的支持情況而不需要為每個模型單獨適配。后續(xù)如果要擴展幾個方向可以考慮一是增加更多工具比如數(shù)據(jù)庫查詢、文件操作注意 MCP 的安全邊界不要讓工具直接訪問生產(chǎn)庫二是把 MCP Server 部署到遠程用真實的 HTTP 地址替代 localhost驗證跨網(wǎng)絡(luò)場景下的流式穩(wěn)定性三是接入多個 MCP Server在 MCP Client 配置里加多個 connection讓模型自己選擇調(diào)用哪個服務(wù)的工具。需要創(chuàng)建 API Key 的話去 API Keys 頁面配置細節(jié)對照接入文檔想先試試模型對話效果可以直接在模型對話頁面發(fā)幾條帶工具調(diào)用的請求觀察返回結(jié)構(gòu)。長期跑編碼 Agent 任務(wù)的話Coding Plan 的額度模型更適合高頻調(diào)用場景。