☰
Spring AI + MCP Client 配置与使用详解:从 Base URL 改到 TaoToken 的完整实践
2026/10/12 12:26:34 网站建设 项目流程

1. 为什么 Spring AI 接 MCP Client 总在 Base URL 上翻车

很多 Java 后端第一次把 Spring AI 和 MCP Client 拼到一起时,都会遇到一个很典型的场景:本地 MCP Server 明明已经跑起来了,curl也能通,但 Spring Boot 应用一启动就报连接失败,或者请求发出去了却拿不到choices字段。问题往往不在代码逻辑,而在配置链路——Base URL 写成了localhost:8080,鉴权头没带,工具注册没生效,三者任意一个断掉,整条通道就废了。

MCP(Model Coordination Protocol)在 Spring AI 里的定位,是让本地应用通过统一协议去协调远程模型服务。它负责的事情包括:把请求发给模型服务端、拿回生成结果、管理会话上下文、支持流式响应和多模型切换。Spring AI 通过spring-ai-mcp-client模块把这套能力封装成自动装配的 Bean,你只要把application.yml写对,理论上就能直接注入McpClient用。

但“理论上”和“跑通”之间,差的就是这篇要讲的东西。我试过在本地联调时把 Base URL 从默认的http://localhost:8080改成 TaoToken 的 API 地址,中间踩过的坑包括:401鉴权失败、local proxy failed连接被拒、reading choices解析报错、OAuth 相关配置缺失。这些报错看起来吓人,其实每一个都对应配置里的一行。

这篇文章面向的是 Java 后端在本地联调 MCP 服务的场景,目标是跑通一次可复现的调用。我会给出可复制的application.yml片段、MCP Client 初始化代码,以及用curl验证通道是否生效的动作。你跟着做,应该能在半小时内看到模型返回的第一段文本。

核心检索词先明确:Spring AI 集成 MCP Client 的配置与使用,重点在 Base URL、鉴权与工具注册这条链路上。适合谁?适合已经会用 Spring Boot 3.x、想在本机把 MCP 通道打通、但被配置细节卡住的开发者。不需要你懂模型训练,只需要你会改 YAML 和跑 Maven。

下面从环境准备开始,一步步把这条链路搭起来。每一步都有可复制的片段,遇到报错就对照第 5 节的排查表。

2. TaoToken 前置准备:Base URL、Key 与模型 ID 三件套

在改 Spring AI 配置之前,先把 TaoToken 这边的三件套准备好:Base URL、API Key、Model ID。这三样东西是后面所有配置的基础,缺一个都跑不通。

Base URL 用https://taotoken.net/api,注意这里不加任何查询参数,就是干净的 API 根路径。API Key 需要你去控制台生成,路径是 TaoToken API Keys。生成之后复制出来,形如sk-开头的一串字符,后面配置里会用到。Model ID 根据你要调的模型填,比如claude-sonnet-4-20250514这类标识,具体以控制台或文档里列出的为准。

如果你对模型能力还不确定,可以先去 模型对话 页面手动试一下,确认这个模型能正常返回,再回到代码里配。这一步看起来多余,但能帮你排除“Key 本身有问题”这种低级错误。

环境要求方面,JDK 17 或以上,Spring Boot 3.2+,Maven 3.8+。MCP Client 依赖 WebClient,所以spring-boot-starter-webflux必须引入。如果你项目里已经有spring-boot-starter-web,两者可以共存,但要注意 WebClient 的自动配置来自 webflux。

依赖片段如下,版本号按你项目实际用的 Spring AI 版本调整:

<dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-core</artifactId> <version>0.8.1</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-client</artifactId> <version>0.8.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>

这里有个细节:spring-ai-mcp-client内部依赖 WebClient 来做 HTTP 通信,所以 webflux 不能省。如果你只引了spring-boot-starter-web,启动时会报No qualifying bean of type WebClient.Builder,这个报错在第 5 节会再提。

工具注册这块,MCP Client 支持把本地方法注册成可被模型调用的工具。Spring AI 里通过@Tool注解或者ToolCallback接口来声明,然后在配置里开启工具发现。如果你暂时不需要工具调用,可以先跳过,但配置里建议把工具注册的开关留着,后面加功能不用改结构。

三件套准备好之后,进入下一步:把这些值写进application.yml。

3. 可复制配置:application.yml 与 MCP Client 初始化

这一节是整篇的核心,配置写对了,后面基本就顺了。先看application.yml的完整片段,路径和字段名都按 Spring AI 的约定来:

spring: ai: mcp: client: enabled: true base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} model-name: claude-sonnet-4-20250514 timeout: 120s tools: enabled: true chat: options: temperature: 0.7 max-tokens: 2048 logging: level: org.springframework.ai: DEBUG reactor.netty: DEBUG

几个关键点解释一下。base-url填https://taotoken.net/api,不要带尾斜杠,也不要在后面拼/v1,Spring AI 的 MCP Client 会自己处理路径拼接。api-key用环境变量注入,避免把 Key 硬编码进仓库,这是基本的安全习惯。model-name填你要用的模型 ID,和前面控制台里看到的一致。

timeout设成 120s,是因为模型生成有时候会超过默认的 60s,尤其是长文本。tools.enabled打开工具注册,后面加@Tool方法时不用再改配置。chat.options里的temperature和max-tokens按需调,本地联调先用默认值也行。

如果你用的是application.properties,等价写法是:

spring.ai.mcp.client.enabled=true spring.ai.mcp.client.base-url=https://taotoken.net/api spring.ai.mcp.client.api-key=${TAOTOKEN_API_KEY} spring.ai.mcp.client.model-name=claude-sonnet-4-20250514 spring.ai.mcp.client.timeout=120s

接下来是 MCP Client 的初始化代码。Spring AI 会自动装配McpClientBean,你直接注入就行:

import org.springframework.ai.mcp.client.McpClient; import org.springframework.stereotype.Service; @Service public class AIClientService { private final McpClient mcpClient; public AIClientService(McpClient mcpClient) { this.mcpClient = mcpClient; } public String generateText(String prompt) { return mcpClient.generate(prompt); } public Flux<String> streamText(String prompt) { return mcpClient.stream(prompt); } }

如果你需要更细粒度的控制,比如手动指定模型或加自定义头,可以注入WebClient.Builder自己构建:

import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @Service public class CustomAIService { private final WebClient webClient; public CustomAIService(WebClient.Builder builder, @Value("${spring.ai.mcp.client.api-key}") String apiKey) { this.webClient = builder .baseUrl("https://taotoken.net/api") .defaultHeader("Authorization", "Bearer " + apiKey) .build(); } public Mono<String> chat(String model, String prompt) { var body = Map.of( "model", model, "messages", List.of(Map.of("role", "user", "content", prompt)) ); return webClient.post() .uri("/v1/chat/completions") .bodyValue(body) .retrieve() .bodyToMono(Map.class) .map(resp -> { var choices = (List<Map<String, Object>>) resp.get("choices"); var message = (Map<String, Object>) choices.get(0).get("message"); return message.get("content").toString(); }); } }

注意这里手动构建时,baseUrl是https://taotoken.net/api,请求路径写/v1/chat/completions,拼起来就是完整的接口地址。鉴权头用Authorization: Bearer <key>,这是标准写法。

工具注册的配置,如果你要用@Tool注解,需要在配置类里显式注册:

import org.springframework.ai.tool.annotation.Tool; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ToolConfig { @Bean public ToolCallbackProvider toolCallbackProvider() { return ToolCallbackProvider.from(new MyTools()); } public static class MyTools { @Tool(description = "查询当前时间") public String currentTime() { return java.time.LocalDateTime.now().toString(); } } }

配置写完后,启动应用,如果日志里没有报错,说明 Bean 装配成功。下一步用curl验证通道是否真的通了。

4. 验证请求:curl 与代码双通道确认

配置写完不代表通道通了,必须实际发一次请求确认。先用curl从命令行验证,排除代码层面的干扰:

curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "user", "content": "用一句话说明什么是 MCP"} ], "max_tokens": 128 }'

如果返回的 JSON 里有choices数组,且choices[0].message.content有内容,说明 Base URL、Key、Model ID 三件套都是对的。如果返回401,检查 Key 是否复制完整;如果返回404,检查路径是不是/v1/chat/completions;如果返回model not found,检查 Model ID 拼写。

curl通了之后,回到 Spring Boot 应用里跑一次代码调用。写一个简单的CommandLineRunner或者测试类:

import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class StartupRunner implements CommandLineRunner { private final AIClientService aiClientService; public StartupRunner(AIClientService aiClientService) { this.aiClientService = aiClientService; } @Override public void run(String... args) { String result = aiClientService.generateText("用一句话说明什么是 MCP"); System.out.println("模型返回: " + result); } }

启动应用,观察控制台输出。如果看到模型返回的文本,说明整条链路通了。如果报错,对照日志里的异常类型,进入第 5 节排查。

流式输出的验证稍微不同,用stream方法:

aiClientService.streamText("写一首关于夏天的短诗") .doOnNext(System.out::println) .blockLast();

流式输出要求服务端返回text/event-stream类型,TaoToken 的接口支持这个。如果你在本地看到逐行打印的内容,说明流式通道也正常。

工具注册的验证,可以在 prompt 里让模型调用工具,比如“现在几点了”,如果模型返回的内容里包含时间信息,说明工具注册生效了。这一步不是必须的,但如果你后面要做 Agent 类应用,建议现在就验证一遍。

两个通道都通了之后,把StartupRunner里的测试代码删掉或者注释掉,避免每次启动都发请求。接下来看常见报错怎么排查。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

这一节按真实报错来对照,每个报错给出原因和修复动作。

401 Unauthorized:最常见。原因通常是 Key 没带、Key 过期、或者Authorization头格式不对。检查application.yml里api-key是否注入成功,可以在启动日志里打印一下(注意不要打印完整 Key)。手动构建 WebClient 时,确认defaultHeader("Authorization", "Bearer " + apiKey)里的Bearer后面有一个空格。

local proxy failed / Connection refused:这个报错通常出现在 Base URL 指向本地地址但本地服务没启动时。如果你已经把base-url改成https://taotoken.net/api,还报这个错,检查是不是有残留的localhost:8080配置没删干净,或者环境变量覆盖了 YAML 里的值。另外检查网络是否能正常访问外网接口,公司内网可能需要配置出口。

reading choices / JsonProcessingException:这个报错说明请求发出去了,但返回的 JSON 结构里没有choices字段,或者字段路径不对。先确认curl返回的结构,如果是标准 OpenAI 兼容格式,choices[0].message.content应该存在。如果返回的是错误信息,比如{"error": {"message": "..."}},那要先解决错误本身。代码里解析时加一层判空:

if (resp.containsKey("error")) { throw new RuntimeException("API error: " + resp.get("error")); }

OAuth / token endpoint 相关报错:如果你在配置里启用了 OAuth 模式,但没配token-uri和client-id,会报这个。本地联调用 API Key 模式就够了,把 OAuth 相关配置去掉,只保留api-key。如果你确实需要 OAuth,参考 接入文档 里的完整参数。

No qualifying bean of type WebClient.Builder:说明spring-boot-starter-webflux没引入。加上依赖重新构建即可。

工具注册不生效:检查tools.enabled是否为true,以及ToolCallbackProviderBean 是否被扫描到。如果用了@Tool注解但没注册 Provider,模型看不到工具。

排查时把日志级别开到 DEBUG,org.springframework.ai和reactor.netty两个包都打开,能看到完整的请求 URL、请求头和响应体。这比猜要快得多。

6. 长期编码与 Agent 场景的下一步

跑通一次调用只是起点。如果你后面要做长期编码辅助或者 Agent 类应用,配置层面还有几件事可以提前做。

第一,把McpClient的调用封装成带重试的模板。网络抖动在本地联调时也会遇到,加一个Retry.backoff(3, Duration.ofSeconds(1))能省很多手动重跑的时间。第二,把工具注册做成可插拔的模块,每个工具一个类,通过ToolCallbackProvider统一注册,后面加工具不用改核心配置。第三,流式输出在 Agent 场景里很关键,建议把stream方法的消费端做成Flux管道,方便接 SSE 或者 WebSocket 推给前端。

如果你打算把编码辅助做成日常工具,可以了解一下 Coding Plan,它针对长期编码场景做了额度优化。本地联调阶段先用按量计费的 Key 就够了,等调用量稳定了再考虑。

最后提醒一个实操细节:application.yml里的api-key用环境变量注入,本地开发时在 IDE 的 Run Configuration 里设TAOTOKEN_API_KEY,不要写进代码仓库。如果团队协作,把配置模板里的 Key 字段留空,文档里说明怎么获取。这样既安全,也方便别人复现你的环境。

通道通了之后,下一步就是往里面加你自己的业务逻辑。MCP Client 只是个管道,真正有价值的是你通过它协调的那些模型能力和工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询