1. 为什么 Java 开发者需要搞懂这三个注解
如果你正在用 Spring AI 搭一个能被 AI 工具调用的服务端,大概率会遇到一个绕不开的问题:MCP 协议里 Tool、Resource、Prompt 到底该怎么分工?我见过不少项目把三者混着用,结果 AI 客户端要么调不到工具,要么读资源时返回一堆无用上下文,排查起来非常痛苦。
MCP(Model Context Protocol)本质上是给大模型和外部系统之间定的一套“对话规则”。Spring AI 把这套规则封装成了三个注解:@McpTool负责“做事”,@McpResource负责“给数据”,@McpPrompt负责“定格式”。你可以把它们理解成一个餐厅:Tool 是厨师(执行动作),Resource 是冰箱(提供原料),Prompt 是菜谱模板(规定怎么做)。三者配合,AI 才能稳定地完成复杂任务。
这篇内容面向已经会用 Spring Boot、但还没把 MCP 注解跑通的 Java 开发者。我会先讲清楚三个注解的语义边界,再给出一份可以直接复制的application.yml和 TaoToken 统一 Key 配置骨架,最后用 Cline 或 CC Switch 连接后,逐步验证@McpTool触发、@McpResource读取、@McpPrompt渲染的完整链路。整个过程不需要你额外折腾网络环境,只要本地能跑 Spring Boot 就行。
2. TaoToken 前置:统一 Key 与 MCP 服务端的关系
在动手写注解之前,先把 Key 的事情理清楚。MCP 服务端本身不直接调用大模型,它只是把能力暴露给 AI 客户端(比如 Cline、CC Switch)。真正需要 Key 的地方是客户端侧——客户端拿着 Key 去请求模型,模型决定要不要调用你暴露的 Tool、读取你注册的 Resource。
TaoToken 在这里的角色是提供一个统一的 API 入口,让你在客户端配置一次 Key,就能访问多个模型。对于 MCP 调试来说,这意味着你不需要为每个模型单独换 Key,切换模型时只改模型名就行。
你需要提前准备两样东西:
第一,一个可用的 API Key。到 TaoToken 控制台创建即可,地址是https://taotoken.net/console,创建完记得复制保存,页面刷新后不会再显示完整 Key。
第二,确认你的 Spring Boot 项目已经引入 Spring AI MCP 相关依赖。下面是一个最小化的pom.xml片段,Spring Boot 3.2+ 和 Spring AI 1.0.0-M6 以上版本都适用:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId> <version>1.0.0-M6</version> </dependency>如果你用的是 Gradle,对应换成implementation即可。依赖拉不下来时,先检查仓库里有没有配 Spring Milestone,M6 版本还在里程碑仓库里。
注意:TaoToken 的 API 地址是
https://taotoken.net/api,这个地址是给客户端调模型用的,不是给你的 MCP 服务端用的。服务端只负责暴露能力,不负责转发模型请求。
3. 可复制配置:application.yml 与三个注解的完整骨架
3.1 application.yml 骨架
先给一份可以直接粘贴的配置。关键点是spring.ai.mcp.server这一段,它决定了你的服务端以什么方式暴露给客户端。这里用 WebMVC 的 SSE 模式,适合本地调试:
server: port: 8080 spring: application: name: spring-ai-mcp-demo ai: mcp: server: name: demo-mcp-server version: 1.0.0 protocol: SSE sse-endpoint: /sse sse-message-endpoint: /mcp/message capabilities: tool: true resource: true prompt: truecapabilities三个开关建议全开,否则客户端可能看不到对应类型的能力。sse-endpoint是客户端连接地址,后面 Cline 里填的就是http://localhost:8080/sse。
3.2 @McpTool:让 AI 执行动作
Tool 的语义是“执行一个动作并返回结果”。它适合做计算、调外部 API、写文件、查数据库这类有副作用的操作。Spring AI 里注册 Tool 有两种方式,推荐用ToolCallbackProvider显式注册,避免扫描不到。
package com.example.mcp.tool; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; @Component public class OrderTool { @Tool(description = "根据订单号查询订单状态,返回状态码和描述") public String queryOrderStatus(String orderId) { if (orderId == null || orderId.isBlank()) { return "订单号不能为空"; } // 模拟查询 return "订单 " + orderId + " 状态:已发货,预计明天送达"; } }注册到容器:
package com.example.mcp.config; import com.example.mcp.tool.OrderTool; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class McpToolConfig { @Bean public ToolCallbackProvider orderToolProvider(OrderTool orderTool) { return MethodToolCallbackProvider.builder() .toolObjects(orderTool) .build(); } }description一定要写清楚,模型是根据这段描述决定要不要调用的。我试过把描述写成“查询订单”,结果模型经常不触发;改成“根据订单号查询订单状态,返回状态码和描述”之后,触发率明显提升。
3.3 @McpResource:让 AI 读取数据
Resource 的语义是“读取一份数据”,它不应该有副作用。适合暴露配置文件、知识库文档、只读的数据库视图。URI 是资源的唯一标识,客户端通过 URI 来读取。
package com.example.mcp.resource; import org.springframework.ai.mcp.annotation.McpResource; import org.springframework.stereotype.Component; @Component public class ConfigResource { @McpResource( uri = "config://app/features", name = "应用功能开关", description = "返回当前应用的功能开关配置,JSON 格式" ) public String getFeatureFlags() { return """ { "newCheckout": true, "betaSearch": false, "maxRetry": 3 } """; } }注意 URI 的 scheme 可以自定义,config://、file://、db://都行,只要客户端能识别。Resource 返回的内容会直接进入模型上下文,所以不要返回超大文本,否则会挤占 token。
3.4 @McpPrompt:让 AI 按模板渲染
Prompt 的语义是“生成一段标准化的提示词”。它不执行动作,也不读数据,而是把参数填充进模板,返回给客户端。适合统一 AI 的回复格式,比如总结、翻译、代码审查。
package com.example.mcp.prompt; import org.springframework.ai.mcp.annotation.McpPrompt; import org.springframework.ai.mcp.annotation.McpPromptArg; import org.springframework.stereotype.Component; import java.util.Map; @Component public class SummaryPrompt { @McpPrompt( name = "summarize", description = "总结一段文本,输出不超过100字" ) public String summarize( @McpPromptArg(name = "content", description = "待总结的文本") String content) { return "请用不超过100字总结以下内容,保留关键数字和结论:\n" + content; } }Prompt 的返回值就是最终提示词,客户端拿到后会直接发给模型。参数用@McpPromptArg标注,客户端会提示用户填写。
4. 验证请求:用 Cline 或 CC Switch 跑通完整链路
4.1 启动服务端
先确认端口没被占用,然后启动 Spring Boot:
mvn spring-boot:run看到日志里出现Registered tool: queryOrderStatus、Registered resource: config://app/features、Registered prompt: summarize就说明三个注解都生效了。如果只看到部分,检查对应的@Component有没有被扫描到。
4.2 在 Cline 里配置 MCP 服务端
打开 Cline 的 MCP 配置,添加一个 SSE 类型的服务端:
{ "mcpServers": { "spring-ai-demo": { "url": "http://localhost:8080/sse", "disabled": false } } }保存后 Cline 会自动连接,连接成功会在工具列表里看到queryOrderStatus、config://app/features、summarize三项。
4.3 验证 @McpTool 触发
在 Cline 对话框里输入:“帮我查一下订单 A12345 的状态”。模型应该会调用queryOrderStatus,返回“订单 A12345 状态:已发货,预计明天送达”。如果没触发,把description再写具体一点,或者在对话里明确说“使用 queryOrderStatus 工具”。
4.4 验证 @McpResource 读取
输入:“读取 config://app/features 的内容”。客户端会发起资源读取请求,返回那段 JSON。如果客户端不支持直接读 URI,可以换成“当前应用有哪些功能开关”,模型会自己决定去读资源。
4.5 验证 @McpPrompt 渲染
输入:“用 summarize 模板总结这段话:Spring AI 的 MCP 支持三种注解……”。客户端会弹出参数填写框,填入 content 后,模型收到的是渲染后的提示词,输出一段不超过100字的总结。
4.6 客户端侧 Key 配置
如果你在 Cline 里同时配置了模型,Key 填 TaoToken 控制台创建的那个,API 地址填https://taotoken.net/api。这样模型请求走 TaoToken,MCP 能力走本地服务端,两边互不干扰。想换模型时只改模型名,Key 不用动。
5. 本篇常见错排查
问题一:客户端连不上 SSE,报 404。检查sse-endpoint配置是不是/sse,以及 Spring Boot 有没有正常启动。如果用了 Spring Security,需要放行/sse和/mcp/message。
问题二:Tool 注册了但模型不调用。九成是description太模糊。把动作、输入、输出都写进去,比如“根据订单号查询订单状态,输入为字符串订单号,返回状态码和描述”。另外确认ToolCallbackProvider的 Bean 被 Spring 管理了。
问题三:Resource 返回内容乱码。检查返回的字符串编码,Spring 默认 UTF-8,但如果你的文件读取用了其他编码,需要显式指定。Resource 的 URI 不要带空格和中文。
问题四:Prompt 参数填了但没渲染。@McpPromptArg的name要和模板里的占位符对应。如果你用的是字符串拼接而不是模板引擎,确认拼接逻辑没有把参数丢掉。
问题五:三个能力只显示一个。检查application.yml里的capabilities是不是全开了。有些客户端只显示 Tool,不显示 Resource 和 Prompt,换 CC Switch 试试,它对三类能力的展示更完整。
问题六:TaoToken Key 在客户端报 401。确认 Key 没有多余空格,API 地址是https://taotoken.net/api而不是带 UTM 的官网地址。如果刚创建 Key,等几秒再试,控制台有缓存。
6. 把注解用对,比堆功能更重要
跑通这三个注解之后,你会发现 MCP 服务端的设计其实很克制:Tool 只做动作,Resource 只读数据,Prompt 只出模板。很多项目出问题,不是因为功能不够,而是因为把三者混在一起——比如在 Tool 里返回一大段配置,或者在 Resource 里做写操作。边界清晰了,模型调用才稳定。
如果你准备长期用这套东西做编码助手或 Agent,建议把 Key 统一放在 TaoToken 的 Coding Plan 里管理,地址是https://taotoken.net/coding-plan,这样多个客户端共用一个 Key,切换模型时不用到处改配置。接入文档在https://taotoken.net/doc,里面有各客户端的详细配置示例。想先验证模型对话效果,可以直接用https://taotoken.net/models里的对话入口试几句,确认 Key 没问题再往客户端里填。
最后留一个我踩过的坑:Resource 的 URI 不要用file://去读项目外的绝对路径,客户端可能会因为权限问题读不到,换成自定义 scheme 加内存数据更稳。