1. Spring AI MCP 核心注解深度解析
在构建基于Spring框架的AI应用时,MCP(Model Context Protocol)协议提供的三大核心注解是开发者必须掌握的关键工具。作为在AI集成领域实践多年的开发者,我发现很多团队在使用这些注解时存在概念混淆和场景误用的问题。本文将结合真实项目经验,详细拆解@McpTool、@McpResource和@McpPrompt的设计哲学、实现原理和最佳实践。
1.1 注解定位与核心差异
这三个注解虽然都属于MCP协议栈,但各自解决的问题域截然不同:
- @McpTool:赋予AI执行能力(动作)
- @McpResource:赋予AI感知能力(数据)
- @McpPrompt:赋予AI表达规范(交互)
在实际项目中,我曾遇到一个典型错误案例:开发者将数据库查询逻辑放在@McpTool中实现,这相当于让AI"亲自"操作数据库,既不符合安全规范,也违背了MCP的设计初衷。正确的做法应该是通过@McpResource提供数据访问能力,而@McpTool只负责业务动作的执行。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. @McpTool 工具注解详解
2.1 设计原理与运行时机制
@McpTool的本质是将Java方法暴露为AI可调用的功能端点。其底层实现依赖于Spring AI的Function Calling机制,工作流程如下:
- AI模型识别用户意图
- 匹配已注册的Tool描述
- 生成符合工具签名的参数
- 通过反射调用目标方法
- 将执行结果返回AI上下文
java复制// 正确示例:符合单一职责的工具类
public class FinanceTools {
@Tool(description = "计算复利终值,参数:本金amount,年利率rate,年数years")
public BigDecimal calculateCompoundInterest(
@Param("本金") double amount,
@Param("年利率") double rate,
@Param("年数") int years
) {
return BigDecimal.valueOf(amount * Math.pow(1 + rate, years));
}
}
2.2 高级配置技巧
在金融AI项目中,我们总结出以下最佳实践:
- 参数校验:工具方法内部必须包含健壮性检查
java复制@Tool(description = "股票交易模拟")
public TradeResult simulateTrade(
@Param("股票代码") String symbol,
@Param("交易数量") int quantity
) {
if(quantity <= 0) {
throw new IllegalArgumentException("交易数量必须大于0");
}
// ...
}
- 性能监控:通过AOP对所有工具调用添加监控
java复制@Aspect
@Component
public class ToolMonitorAspect {
@Around("@annotation(org.springframework.ai.tool.annotation.Tool)")
public Object monitorTool(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
Metrics.timer("tool.execution.time")
.record(System.currentTimeMillis() - start, MILLISECONDS);
}
}
}
2.3 典型应用场景
- 业务操作:订单创建、支付处理
- 计算服务:税费计算、金融建模
- 系统集成:调用外部API或微服务
重要原则:工具方法应当是无状态的,且每次调用都应是独立事务
3. @McpResource 资源注解实战
3.1 资源加载机制剖析
@McpResource的核心价值在于为AI提供数据感知能力。其资源定位采用Spring的统一资源抽象(Resource接口),支持以下协议:
| 协议前缀 | 示例 | 适用场景 |
|---|---|---|
| file:// | file:/data/knowledge | 本地文件系统 |
| classpath: | classpath:config/ | 类路径资源 |
| http:// | https://api.example | 网络资源 |
| s3:// | s3://bucket/path | AWS S3存储 |
java复制// 动态资源加载示例
@McpResource(
uri = "file:/data/knowledge/{category}.txt",
description = "按分类加载知识库文档"
)
public String loadKnowledgeDocument(
@Param("分类") String category
) throws IOException {
Resource resource = new PathResource("/data/knowledge/" + category + ".txt");
return StreamUtils.copyToString(resource.getInputStream(), UTF_8);
}
3.2 资源缓存策略
在高并发场景下,我们实现了多级缓存方案:
- 本地缓存:Caffeine实现的内存缓存
java复制@Bean
public CacheManager resourceCacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(30, MINUTES)
.maximumSize(1000));
return manager;
}
- 分布式缓存:通过Redis实现集群共享
java复制@Cacheable(value = "knowledgeCache", key = "#category")
@McpResource(uri = "file:/data/knowledge/{category}.txt")
public String getKnowledgeWithCache(String category) {
// ...
}
3.3 安全注意事项
- 资源路径必须进行规范化处理,防止目录遍历攻击
java复制String safePath = Paths.get("/data/knowledge", category)
.normalize()
.toString();
if(!safePath.startsWith("/data/knowledge")) {
throw new SecurityException("非法路径访问");
}
- 网络资源应设置超时限制
java复制@Bean
public RestTemplate secureRestTemplate() {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(10))
.build();
}
4. @McpPrompt 提示工程实践
4.1 模板引擎深度集成
Spring AI的PromptTemplate支持多种模板语法,我们扩展了以下实用功能:
- 变量校验:确保必填参数已提供
java复制@McpPrompt(name = "emailGenerator")
public Prompt generateEmailPrompt(
@Prompt("收件人") String to,
@Prompt(value = "紧急程度", required = false) String urgency
) {
Map<String, Object> variables = new HashMap<>();
variables.put("to", to);
variables.put("urgency", Optional.ofNullable(urgency).orElse("普通"));
return new PromptTemplate("""
请为{{to}}撰写一封{{urgency}}邮件:
主题:{{subject}}
正文:{{content}}
""")
.withValidation() // 启用参数校验
.create(variables);
}
- 多语言支持:根据上下文自动选择模板
java复制@McpPrompt(name = "greeting")
public Prompt getGreetingPrompt(Locale locale) {
String template = switch(locale.getLanguage()) {
case "zh" -> "你好,{{name}}!";
case "en" -> "Hello, {{name}}!";
default -> "Greetings, {{name}}!";
};
return new PromptTemplate(template);
}
4.2 上下文感知提示
在客服系统中,我们实现了动态提示生成:
java复制@McpPrompt(name = "responseTemplate")
public Prompt buildResponsePrompt(
@Prompt("用户问题") String question,
@Prompt("历史记录") List<ChatMessage> history
) {
String context = history.stream()
.map(m -> m.getRole() + ": " + m.getContent())
.collect(joining("\n"));
String template = """
基于以下对话历史:
{{context}}
请用{{tone}}语气回答:
{{question}}
""";
return new PromptTemplate(template)
.withVariable("tone", shouldFormal(question) ? "正式" : "友好")
.create(Map.of(
"context", context,
"question", question
));
}
4.3 性能优化技巧
- 模板预编译:避免重复解析开销
java复制private static final PromptTemplate PRECOMPILED_TEMPLATE =
new PromptTemplate("...").compile();
@McpPrompt(name = "fastTemplate")
public Prompt getFastPrompt() {
return PRECOMPILED_TEMPLATE.create(params);
}
- 批量渲染:高效处理多个提示
java复制public List<Prompt> batchRender(List<PromptSpec> specs) {
return specs.stream()
.map(spec -> templateCache
.computeIfAbsent(spec.templateName(), this::loadTemplate)
.create(spec.params()))
.toList();
}
5. 综合应用架构设计
5.1 典型交互流程
在电商推荐系统中,我们设计了如下工作流:
- 资源加载阶段:
java复制@McpResource(uri = "file:/product/catalog.json")
public String loadProductCatalog() {
// 加载商品目录
}
- 提示生成阶段:
java复制@McpPrompt(name = "recommendationPrompt")
public Prompt buildRecommendation(
@Prompt("用户偏好") String preferences,
@Prompt("商品数据") String catalog
) {
// 构建个性化推荐提示
}
- 工具执行阶段:
java复制@Tool(description = "添加商品到购物车")
public void addToCart(
@Param("商品ID") String productId,
@Param("数量") int quantity
) {
// 执行加购操作
}
5.2 异常处理策略
我们建立了统一的错误处理机制:
java复制@ControllerAdvice
public class McpExceptionHandler {
@ExceptionHandler(ToolExecutionException.class)
public ResponseEntity<ErrorResponse> handleToolError(ToolExecutionException ex) {
return ResponseEntity.status(500)
.body(new ErrorResponse("TOOL_ERROR", ex.getMessage()));
}
@ExceptionHandler(ResourceAccessException.class)
public ResponseEntity<ErrorResponse> handleResourceError(ResourceAccessException ex) {
return ResponseEntity.status(404)
.body(new ErrorResponse("RESOURCE_NOT_FOUND", "请求的资源不可用"));
}
}
5.3 监控与可观测性
通过Micrometer实现全方位监控:
java复制@Configuration
public class McpMonitoringConfig {
@Bean
public MeterRegistry meterRegistry() {
return new CompositeMeterRegistry(
new PrometheusMeterRegistry(PrometheusConfig.DEFAULT),
new JmxMeterRegistry(JmxConfig.DEFAULT, Clock.SYSTEM)
);
}
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
在工具方法上添加监控注解:
java复制@Tool(description = "支付处理")
@Timed(value = "payment.process", description = "支付处理耗时")
@Counted(value = "payment.requests", description = "支付请求计数")
public PaymentResult processPayment(PaymentRequest request) {
// 支付逻辑
}
6. 进阶开发技巧
6.1 动态注解注册
通过编程方式动态注册工具:
java复制@Autowired
private ToolRegistry toolRegistry;
public void registerDynamicTool(Object toolInstance) {
MethodToolCallbackProvider provider = MethodToolCallbackProvider.builder()
.toolObjects(toolInstance)
.build();
toolRegistry.register(provider);
}
6.2 资源版本控制
实现带版本号的资源访问:
java复制@McpResource(uri = "file:/docs/{version}/{filename}")
public String getVersionedDocument(
@Param("版本号") String version,
@Param("文件名") String filename
) {
// 验证版本有效性
if(!isValidVersion(version)) {
throw new IllegalArgumentException("无效的文档版本");
}
// 加载文档内容
}
6.3 提示模板继承
构建可扩展的提示体系:
java复制public abstract class BasePromptTemplates {
@McpPrompt(name = "baseStyle")
public Prompt baseTemplate() {
return new PromptTemplate("""
请用专业、礼貌的语气回答。
回答必须包含以下要素:
- 明确的主题
- 结构化的内容
- 友好的结束语
""");
}
}
@Component
public class CustomerServicePrompt extends BasePromptTemplates {
@McpPrompt(name = "welcomePrompt")
public Prompt welcomeMessage() {
Prompt base = baseTemplate();
String content = base.getContents() + """
欢迎信息专用扩展:
- 包含公司LOGO
- 显示客服工号
""";
return new Prompt(content);
}
}
在实际开发中,我们发现合理组合这三个注解可以构建出既灵活又可靠的AI集成方案。比如在智能客服系统中,通过@McpResource加载知识库,用@McpPrompt标准化应答格式,最后通过@McpTool执行工单创建等操作,形成完整的业务闭环。
