ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Spring AI 整合 MCP Server Annotations:用 TaoToken 统一 Key 搭建可复现的 MCP 服务骨架

Spring AI 整合 MCP Server Annotations:用 TaoToken 统一 Key 搭建可复现的 MCP 服务骨架 1. 为什么要在 Spring AI 里用 MCP Server Annotations如果你正在用 Spring AI 做 Java 侧的 AI 应用大概率会遇到一个绕不开的问题怎么把本地已有的业务方法以标准化的方式暴露给模型调用。传统做法是手写一堆 JSON Schema、手动注册工具描述、再自己拼请求路由代码量不小而且一旦工具数量上来维护成本会迅速失控。Model Context ProtocolMCP解决的正是这件事。它把「模型能调用什么」抽象成工具、资源、提示、补全四类能力用统一协议描述。而 Spring AI 提供的 MCP Server Annotations则进一步把这件事变成声明式的——你只需要在方法上打McpTool、McpResource、McpPrompt、McpComplete框架会自动扫描、生成 Schema、注册到 MCP 服务端。这套注解适合谁适合已经用 Spring Boot 写业务、希望把现有 Service 方法快速变成模型可调用工具的 Java 开发者也适合需要把本地 MCP 服务接入统一模型通道、又不想在每家模型厂商 SDK 之间反复横跳的团队。但注解只是骨架真正跑通还需要一个稳定的模型通道。我这次的做法是MCP 服务端用 Spring AI 注解声明工具模型侧统一走 TaoToken 的 API 通道用同一个 Key 完成工具调用与日志验证。下面把 pom、application.yml、注解骨架和一次完整调用验证拆开讲你可以直接照着复现。2. TaoToken 前置统一 Key 与通道准备在写代码之前先把模型通道准备好。TaoToken 在这里扮演的角色是「统一模型入口」——你的 MCP 服务端不需要关心底层是哪家模型只需要拿到一个 API Key 和一个兼容的 base URL就能发起对话与工具调用。第一步打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册并登录。登录后进入控制台找到 API Keys 页面创建一个新的 Key。建议按项目命名比如spring-ai-mcp-demo方便后续排查。第二步记下两个地址。API 基础地址是 https://taotoken.net/api这个不加 UTM直接用于代码里的 base-url。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 后续查看用量、管理 Key 都在这里。第三步确认你要用的模型。如果你只是验证工具调用链路选一个支持 function calling 的对话模型即可。如果你打算长期做编码类 Agent可以关注 Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频编码场景。注意Key 只显示一次创建后立刻复制到安全位置。不要写进会提交到 Git 的配置文件里用环境变量或本地application-local.yml隔离。到这里你手里应该有一个sk-开头的 Key以及https://taotoken.net/api这个 base URL。接下来进入 Spring AI 工程配置。3. 可复制配置pom 依赖与 application.yml先建一个标准的 Spring Boot 3.x 工程JDK 17 起步。Spring AI 的版本迭代较快建议用当前稳定版下面以 1.0.x 系列为例。3.1 pom.xml 关键依赖properties java.version17/java.version spring-ai.version1.0.0/spring-ai.version /properties dependencies !-- Spring AI 核心 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-core/artifactId version${spring-ai.version}/version /dependency !-- MCP 服务端启动器提供注解扫描与自动注册 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency !-- 模型客户端用于走 TaoToken 通道 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies repositories repository idspring-milestones/id urlhttps://repo.spring.io/milestone/url /repository /repositories这里spring-ai-mcp-server-spring-boot-starter是注解能力的关键它负责扫描带 MCP 注解的 Bean 并注册到服务端。spring-ai-openai-spring-boot-starter用来对接兼容 OpenAI 协议的通道TaoToken 的 API 正好走这套协议。3.2 application.yml 配置server: port: 8080 spring: application: name: spring-ai-mcp-demo ai: openai: # TaoToken 统一通道 base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.2 mcp: server: name: demo-mcp-server version: 1.0.0 # 同步服务器注解方法用非响应式返回类型 type: SYNC annotation-scanner: enabled: true几个点解释一下。base-url指向 TaoToken 的 API 地址api-key从环境变量读取避免硬编码。spring.ai.mcp.server.type设为SYNC意味着注解方法要用同步返回类型如果你写MonoString会被过滤掉并打警告——这是后面排障的重点。annotation-scanner.enabled必须为 true否则注解不会被扫描。启动前设置环境变量export TAOTOKEN_API_KEYsk-你的KeyWindows 下用set TAOTOKEN_API_KEYsk-你的Key或者直接在 IDE 的运行配置里加环境变量。4. 注解骨架工具、资源、提示、补全配置就绪后写一个带注解的组件。下面这个类同时覆盖四类注解你可以按需删减。4.1 工具注解 McpToolComponent public class CalculatorTools { McpTool( name add, description 将两个数字相加, title 加法计算器, annotations McpTool.McpAnnotations( readOnlyHint true, destructiveHint false, idempotentHint true ) ) public int add( McpToolParam(description 第一个数字, required true) int a, McpToolParam(description 第二个数字, required true) int b) { return a b; } }McpTool会自动根据方法签名生成 JSON SchemaMcpToolParam描述每个参数。McpAnnotations是给客户端的提示readOnlyHint true表示这个工具不修改环境idempotentHint true表示相同参数重复调用结果一致。这些提示会影响模型是否愿意调用、以及调用时的谨慎程度。4.2 资源注解 McpResourceComponent public class ConfigResourceProvider { private final MapString, String configData Map.of( app.name, spring-ai-mcp-demo, app.env, dev ); McpResource( uri config://{key}, name Configuration, title 应用配置, description 按 key 读取配置项, mimeType text/plain ) public String getConfig(String key) { return configData.getOrDefault(key, NOT_FOUND); } }URI 模板里的{key}会映射到方法参数。资源适合暴露只读数据比如配置、文档片段、数据库查询结果。4.3 提示注解 McpPromptComponent public class PromptProvider { McpPrompt(name greeting, description 生成问候消息) public GetPromptResult greeting( McpArg(name name, description 用户姓名, required true) String name) { String message 你好 name 今天我能帮你什么; return GetPromptResult.builder( List.of(new PromptMessage(Role.ASSISTANT, TextContent.builder(message).build()))) .description(问候) .build(); } }提示是预置的对话模板模型可以按名字取用适合把常用指令固化下来。4.4 补全注解 McpCompleteComponent public class CompletionProvider { private final ListString cities List.of(Beijing, Shanghai, Shenzhen, Chengdu); McpComplete(prompt city-search) public ListString completeCityName(String prefix) { return cities.stream() .filter(c - c.toLowerCase().startsWith(prefix.toLowerCase())) .limit(10) .toList(); } }补全用于给提示参数或资源 URI 提供候选值输入前缀返回匹配列表。4.5 启动类SpringBootApplication public class McpServerApplication { public static void main(String[] args) { SpringApplication.run(McpServerApplication.class, args); } }启动后自动配置会扫描所有带 MCP 注解的 Bean生成规格说明并注册到 MCP 服务端。控制台会打印注册了哪些工具、资源、提示。如果某个方法被过滤会有一条 WARN 日志告诉你原因。5. 验证请求一次完整的工具调用与日志骨架搭好后怎么确认它真的能跑通我分两步验证先看服务端注册日志再走一次模型侧的工具调用。5.1 服务端注册日志启动应用观察控制台。正常情况你会看到类似输出Registered MCP tool: add (title加法计算器) Registered MCP resource: config://{key} Registered MCP prompt: greeting Registered MCP completion: promptcity-search如果某个方法没出现往下翻找 WARN。常见的是「method filtered due to reactive return type on SYNC server」说明你写了Mono但服务器是同步模式。5.2 模型侧工具调用写一个简单的 CommandLineRunner 或测试类让模型通过 TaoToken 通道发起一次对话并触发工具调用Component public class ToolCallRunner implements CommandLineRunner { private final ChatClient chatClient; public ToolCallRunner(ChatClient.Builder builder) { this.chatClient builder.build(); } Override public void run(String... args) { String response chatClient.prompt() .user(请帮我计算 12 加 30 等于多少) .call() .content(); System.out.println(模型回复: response); } }运行后日志里会看到模型先返回一个 tool_call参数是a12, b30然后框架调用CalculatorTools.add把结果42回传给模型模型再生成自然语言回复。整个过程的关键日志包括Tool call requested: add, arguments{a12, b30} Tool call result: 42 Final response: 12 加 30 等于 42如果你在add方法里加一行System.out.println(add invoked: a , b)能更直观确认工具真的被调用了。5.3 用模型对话页快速验证通道如果你不想先写代码也可以直接打开模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 手动发一条消息确认 Key 和通道是通的。这一步能帮你把「通道问题」和「代码问题」分开排查。6. 本篇常见错排查下面这几个坑是我在搭这套骨架时实际遇到过的按出现频率排序。第一个注解方法没被注册日志里也没有 WARN。先检查spring.ai.mcp.server.annotation-scanner.enabled是否为 true。再确认你的组件类是否被 Spring 扫描到——如果放在SpringBootApplication同级或子包下没问题放到外面需要手动ComponentScan。第二个同步服务器上写了Mono返回类型。这是最常见的过滤原因。spring.ai.mcp.server.typeSYNC时只有非响应式返回类型会被注册Mono、Flux、Publisher都会被跳过并打 WARN。解决办法要么改成同步返回要么把服务器类型改成ASYNC并统一用响应式。第三个无状态服务器上用了McpSyncRequestContext。无状态模式不支持双向操作带McpSyncRequestContext、McpAsyncRequestContext、McpSyncServerExchange的方法会被过滤。无状态场景下用McpTransportContext或干脆不写上下文参数。第四个Key 没生效报 401。检查环境变量名是否和 yml 里的${TAOTOKEN_API_KEY}一致检查 Key 有没有多余空格。如果用的是 IDE 运行配置确认环境变量加在了正确的启动项上。第五个工具被调用但参数为空。通常是McpToolParam的required和模型生成的参数不匹配。把required true的参数在描述里写清楚类型和含义模型更容易生成正确参数。第六个资源 URI 模板不匹配。config://{key}这种模板方法参数名必须和{key}一致否则映射不上。如果你用ReadResourceResult返回注意TextResourceContents的 URI 要和请求的 URI 对应。提示排障时把日志级别调到 DEBUGlogging.level.org.springframework.aiDEBUG能看到注解扫描和工具注册的详细过程。7. 下一步把骨架接进你的真实项目到这里你已经有了一个可复现的 MCP 服务骨架pom 依赖、application.yml、四类注解示例、一次完整的工具调用验证以及六个常见坑的排查路径。接下来可以做的几件事把CalculatorTools换成你真实的业务 Service比如订单查询、库存校验、报表生成。给每个工具补上准确的description和McpAnnotations提示模型调用会更稳。如果你要长期跑编码类 Agent建议把 Key 管理切到 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 按用量规划更清晰。需要新建或轮换 Key 时直接去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 操作。接入细节和参数说明可以对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 逐项核对。最后留一个实用习惯每次改完注解先看启动日志里注册了哪些能力再跑一次工具调用。注册日志是这套骨架最诚实的反馈比任何猜测都快。
返回列表