多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Spring AI MCP Server 跑通 @Tool,把调用端 Base URL 改到 TaoToken

Spring AI MCP Server 跑通 @Tool,把调用端 Base URL 改到 TaoToken Spring AI MCP Server 跑通 Tool把调用端 Base URL 改到 TaoToken本篇处理的问题是基于 spring-ai-starter-mcp-server-webmvc 的 MCP Server 已经在 8127 端口暴露了 ImageSearchToolTool、ToolCallbackProvider、application-sse.yml 与 application-stdio.yml 都按原步骤配好了但调用端模型凭据还空着AI 客户端不会真正发起推理工具也自然不会触发。TaoToken 在这里只做一件事接管调用端的模型通道。官网入口 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 先创建 Key客户端 Base URL 填 https://taotoken.net/apiMCP Server 本身保持原样。下面的内容保留步骤 2~5 的 Tool 注解、ToolCallbackProvider 注册、searchMediumImages 的 Pexels 业务逻辑和 8127 端口只改客户端里原先空着的模型凭据。原问题MCP Server 跑通 Tool 后模型调用通道还空着先把职责拆开。Spring AI MCP Server 是工具侧它把 ImageSearchTool 里的方法通过 Tool 暴露出去让支持 MCP 的客户端发现并调用。searchMediumImages 内部访问的是 Pexels 图片搜索接口走的是 HTTP 请求整个过程不消耗模型 Token。真正消耗 Token 的是调用这些工具并做推理的 AI 客户端也就是 /ai/manus/chat 背后的那层服务它接收 SuperAgent.vue 的 SSE 请求带着工具定义去请求大模型模型决定是否发起 tool call再把工具执行结果拼回上下文继续推理。原项目里这一层客户端的模型凭据是空着的。表现通常有三种一是前端 SSE 能连上后端日志也显示 MCP Server 在 8127 端口正常监听但发消息后模型没有回复二是返回鉴权错误工具列表虽然能通过 ToolCallbackProvider 注册成功模型请求却被拒绝三是本地写了临时地址或 mock工具调用链路看着通了一换环境就断。根因不在 Tool也不在 ToolCallbackProvider而在调用端没有可用的模型通道。目标很明确MCP Server 继续用 8127 端口SSE 模式与 stdio 模式继续用 profile 切换ImageSearchTool 的 Tool 注解和 Pexels 业务逻辑保持不动只把调用端请求模型时的 Base URL 改到 TaoTokenKey 用环境变量注入。这样 SuperAgent.vue 仍然走 /ai/manus/chat后端仍然负责推理推理过程中决定调用 searchImage最后回到 MCP Server 执行图片搜索。TaoToken 前置Key、Base URL 与两个不要混用的凭据先准备 TaoToken 的 API Key。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 按控制台指引创建 Key得到形如 YOUR_API_KEY 的凭据。这个 Key 只给调用端模型请求使用不要写进 MCP Server 的 application-sse.yml也不要提交到 Git。这里有两个 Key 必须区分清楚。Pexels 的 API Key 是给 searchMediumImages 内部搜索图片用的放在工具类的配置里TaoToken 的 Key 是给调用端请求模型推理用的放在客户端配置里。把两者混在同一份配置文件里排查时会非常难定位一个报 401 是图片搜索失败另一个报 401 是模型推理失败现象相似来源不同。Base URL 按 https://taotoken.net/api 填写。注意这是调用端的模型地址不是 MCP Server 的地址。MCP Server 自己的地址仍然是 http://localhost:8127由 Spring AI MCP Server WebMVC 提供 SSE 或 stdio 通信。如果调用端使用 Spring AI 的 OpenAI 兼容客户端base-url 就填 TaoToken 的 API 地址api-key 读环境变量如果调用端是手写 WebClient 或 RestClient则在请求头里加 Authorization: Bearer YOUR_API_KEY请求路径按 SDK 约定拼接不要在 base-url 后面重复追加 /v1。环境变量建议这样准备export TAOTOKEN_API_KEYYOUR_API_KEY export PEXELS_API_KEY你的PexelsKey export TAOTOKEN_MODEL_ID你的模型ID模型 ID 用占位符管理不要硬编码在 Java 类里。不同环境可以切不同模型MCP 工具侧不需要跟着改。可复制配置保留 ImageSearchTool 与 ToolCallbackProvider依赖部分仍然围绕 spring-ai-starter-mcp-server-webmvc 和 Hutool 展开这里不重复整段 pom只强调一点Spring AI BOM 与 MCP Server 依赖版本要对齐否则 Tool 注解在编译期或运行期可能找不到对应类。工具类保留原文步骤 2 的结构Tool 与 ToolParam 不改searchMediumImages 继续走 PexelsService public class ImageSearchTool { Value(${pexels.api-key}) private String pexelsApiKey; Tool(description search image from web) public String searchImage(ToolParam(description Search query keyword) String query) { return searchMediumImages(query).toString(); } ListString searchMediumImages(String query) { MapString, String headers new HashMap(); headers.put(Authorization, pexelsApiKey); MapString, Object params new HashMap(); params.put(query, query); String body HttpUtil.createGet(https://api.pexels.com/v1/search) .addHeaders(headers) .form(params) .execute() .body(); return JSONUtil.parseObj(body) .getJSONArray(photos) .stream() .map(obj - (JSONObject) obj) .map(obj - obj.getJSONObject(src)) .map(src - src.getStr(medium)) .filter(StrUtil::isNotBlank) .collect(Collectors.toList()); } }启动类里继续用 ToolCallbackProvider 注册工具这一步不能省。没有它MCP 客户端列出的工具列表就是空的模型再强也无工具可调SpringBootApplication public class ImageSearchMcpServerApplication { public static void main(String[] args) { SpringApplication.run(ImageSearchMcpServerApplication.class, args); } Bean ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) { return MethodToolCallbackProvider.builder() .toolObjects(imageSearchTool) .build(); } }MCP Server 的 profile 配置保持原样。application.yml 指定默认激活 sse端口 8127spring: application: name: image-search-mcp-server profiles: active: sse server: port: 8127application-sse.yml 保持 HTTP/SSE 模式spring: ai: mcp: server: name: image-search-mcp-server version: 0.0.1 type: SYNC stdio: falseapplication-stdio.yml 保持标准输入输出模式并关闭 Web 应用类型spring: ai: mcp: server: name: image-search-mcp-server version: 0.0.1 type: SYNC stdio: true main: web-application-type: none banner-mode: off真正的改动在调用端。以 Spring AI OpenAI 兼容配置为例把原先空着的凭据和地址换成 TaoTokenspring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: ${TAOTOKEN_MODEL_ID}如果 /ai/manus/chat 是手写客户端则把创建 WebClient 的地方改成WebClient.builder() .baseUrl(https://taotoken.net/api) .defaultHeader(HttpHeaders.AUTHORIZATION, Bearer taotokenApiKey) .build();调用端如果同时用 Spring AI MCP Client 连接 8127注意区分两组配置spring.ai.openai.*管模型通道spring.ai.mcp.client.*管 MCP 连接两边不要互相覆盖。模型推理走 TaoToken工具执行走本地 MCP Server这是两条独立的链路。启动与验证application-sse.yml 起 8127SuperAgent.vue 走 /ai/manus/chat先以 SSE 模式启动 MCP Server./mvnw spring-boot:run -Dspring-boot.run.profilessse打包后也可以直接指定 profilejava -jar target/image-search-mcp-server-0.0.1-SNAPSHOT.jar --spring.profiles.activesse启动成功的判断点有三个应用没有因为端口占用退出日志里能看到 MCP Server 以 SYNC 类型注册ToolCallbackProvider 把 ImageSearchTool 注册进来后工具数量不为 0。如果使用 MCP 客户端连接工具列表里应出现 searchImage 这一项。模型通道是否通可以先用一个最小请求确认。调用端配置完成后发起一次普通对话请求路径按 SDK 拼接Base URL 仍是 https://taotoken.net/apicurl https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: ping}] }能拿到正常回复说明 TaoToken 的 Key、Base URL 和模型 ID 这三项已经对齐。接下来再验证完整链路启动前端在 SuperAgent.vue 对应页面输入“帮我搜几张日落海滩的图片”。预期结果是 SSE 持续输出后端日志出现模型请求与 tool call 记录MCP Server 侧出现 searchMediumImages 调用最后前端收到包含图片 URL 的回复。此时工具执行结果来自 Pexels模型推理来自 TaoToken8127 端口只负责工具暴露与调用不参与 Token 消耗。stdio 模式可以单独验证./mvnw spring-boot:run -Dspring-boot.run.profilesstdio这个模式适合本地进程调用不适合直接给远程前端使用。切换 profile 时不要同时保留 SSE 的 Web 配置否则容易出现应用类型冲突。本篇常见错排查Base URL、8127 端口与 Tool 注册现象可能原因处理模型请求 401 或 403TaoToken Key 没读到或和 Pexels Key 混用检查环境变量名与 yaml 占位符是否一致模型 Key 只放调用端请求 404Base URL 拼错重复追加了 /v1 或少了 /api调用端统一用 https://taotoken.net/api路径交给 SDK 拼工具列表为空ToolCallbackProvider 没注册或 ImageSearchTool 没被扫描确认启动类里的 Bean 把工具对象传进去Service 生效8127 端口启动失败端口被占用或 profile 冲突换端口或结束占用进程确认 application.yml 的 active 与启动参数一致stdio 模式报 Web 应用异常stdio 为 true 时仍保留 Web 配置保留 main.web-application-type: none并关闭 banner模型不调用工具调用端没把工具定义传给模型或提示词与工具描述不匹配检查 /ai/manus/chat 里是否附带 ToolCallbackProvider 暴露的工具定义前端一直转圈SSE 连接未正常 [DONE]或异常中断检查 SuperAgent.vue 的 onerror 与 eventSource.close工具异常要捕获后返回图片搜索返回空Pexels Key 无效或响应结构变化单独调用 searchMediumImages 验证过滤空 URL 后再返回换环境后失效Key 硬编码或写进提交文件全部改用环境变量按 profile 分环境排查顺序建议从外到内先确认 TaoToken 模型请求单独能通再确认 MCP Server 8127 能列出工具最后确认 /ai/manus/chat 把两者串起来。不要一上来就改 Tool 注解工具侧通常不是第一故障点。接入配置收口API Keys 与文档入口到这一步Spring AI MCP Server 的 Tool、ToolCallbackProvider、searchMediumImages、8127 端口和两个 profile 都保持原样变化只发生在调用端的模型凭据Base URL 指向 TaoTokenKey 从环境变量读取SuperAgent.vue 继续走 /ai/manus/chat模型推理和工具执行各自归位。如果你是来排障或做类似接入建议先从 API Keys 页面确认凭据再对照接入文档核对 Base URL 与客户端填写方式API Keyshttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentspring-ai-mcp-server接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentspring-ai-mcp-server如果后续要把这套 MCP 工具链用于长期编码或 Agent 场景可以把调用端的模型通道固定到 Coding Plan减少每次调试时反复切换 Key 的成本Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentspring-ai-mcp-server
返回列表