【Spring AI MCP】十一、SpringAI MCP 客户端注解
Spring AI MCP注解模块为Java开发者提供了基于注解的声明式编程模型,简化MCP服务器和客户端的开发。该模块包含服务器端和客户端两大核心部分:服务器端提供@McpTool、@McpResource等注解实现工具注册和资源访问;客户端通过@McpLogging、@McpSampling等注解处理服务器通知。支持同步和异步处理方式,提供McpSyncRequestContext等特殊参数类
Spring AI MCP学习目录
一、MCP 原理详解
二、MCP 客户端
三、MCP 客户端
四、MCP 服务端
五、SpringAI MCP 服务端
六、SpringAI MCP 服务端 STDIO & SSE
七、SpringAI MCP 服务端 Streamable-HTTP
八、SpringAI MCP 服务端 Stateless Streamable-HTTP
九、 MCP 安全(Security)
十、SpringAI MCP 安全(Security)
十一、SpringAI MCP 客户端注解
十二、SpringAI MCP 服务端注解
十三、SpringAI MCP 特殊参数(Special Parameters)
Spring AI MCP注解模块为Java中的模型上下文协议(MCP)服务器和客户端提供了基于注解的方法处理。它通过使用Java注解的简洁声明式方法,简化了MCP服务器方法和客户端处理程序的创建和注册。
MCP注解使开发者能够使用声明式注解来创建和注册MCP操作处理程序。这种方法通过减少样板代码并提高可维护性,简化了MCP服务器和客户端功能的实现。该库构建在MCP Java SDK之上,为实施MCP服务器和客户端提供了更高层次的基于注解的编程模型。
架构概览
MCP注解模块包含两大核心部分:
服务器端注解
针对MCP服务器的开发需求,提供以下注解:
- @McpTool - 实现MCP工具并自动生成JSON模式
- @McpResource - 通过URI模板提供资源访问能力
- @McpPrompt - 生成提示消息
- @McpComplete - 提供自动补全功能
客户端注解
面向MCP客户端的开发场景,提供以下注解:
- @McpLogging - 处理日志消息通知
- @McpSampling - 处理采样请求
- @McpElicitation - 处理用于收集额外信息的启发请求
- @McpProgress - 处理长时间运行操作中的进度通知
- @McpToolListChanged - 处理工具列表变更通知
- @McpResourceListChanged - 处理资源列表变更通知
- @McpPromptListChanged - 处理提示列表变更通知
特殊参数与注解说明
-
McpSyncRequestContext - 同步操作专用参数类型,提供统一的MCP请求上下文访问接口,包括原始请求、服务器交换(用于有状态操作)、传输上下文(用于无状态操作),以及日志记录、进度报告、采样和启发式请求等便捷方法。该参数自动注入且不参与JSON模式生成,支持Complete、Prompt、Resource和Tool方法。
-
McpAsyncRequestContext - 异步操作专用参数类型,提供与McpSyncRequestContext相同的统一接口,但使用响应式(基于Mono)返回类型。该参数自动注入且不参与JSON模式生成,支持Complete、Prompt、Resource和Tool方法。
-
McpTransportContext - 无状态操作专用参数类型,提供轻量级的传输层上下文访问,无需完整服务器交换功能。该参数自动注入且不参与JSON模式生成。
-
@McpProgressToken - 标记方法参数以接收请求中的进度令牌。该参数自动注入且不参与生成的JSON模式。注意:使用McpSyncRequestContext或McpAsyncRequestContext时,可通过ctx.request().progressToken()获取进度令牌,无需使用此注解。
-
McpMeta - 特殊参数类型,提供对MCP请求、通知和结果中元数据的访问。该参数自动注入且不受参数数量限制和JSON模式生成的影响。注意:使用McpSyncRequestContext或McpAsyncRequestContext时,可通过ctx.requestMeta()获取元数据。
客户端注解
MCP客户端注解提供了一种声明式方式,通过Java注解实现MCP客户端处理程序。这些注解简化了服务器通知和客户端操作的处理。
所有MCP客户端注解必须包含一个clients参数,以将处理程序与特定的MCP客户端连接关联。clients必须与应用程序属性中配置的连接名称匹配。
@McpLogging
@McpLogging注解用于处理MCP服务器发送的日志消息通知。
基础
@Component
public class LoggingHandler {
@McpLogging(clients = "my-mcp-server")
public void handleLoggingMessage(LoggingMessageNotification notification) {
System.out.println("Received log: " + notification.level() +
" - " + notification.data());
}
}
独立参数
@McpLogging(clients = "my-mcp-server")
public void handleLoggingWithParams(LoggingLevel level, String logger, String data) {
System.out.println(String.format("[%s] %s: %s", level, logger, data));
}
@McpSampling
@McpSampling注解用于处理MCP服务器为LLM补全请求发送的采样请求。
同步实现
@Component
public class SamplingHandler {
@McpSampling(clients = "llm-server")
public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
// Process the request and generate a response
String response = generateLLMResponse(request);
return CreateMessageResult.builder()
.role(Role.ASSISTANT)
.content(new TextContent(response))
.model("gpt-4")
.build();
}
}
异步实现
@Component
public class AsyncSamplingHandler {
@McpSampling(clients = "llm-server")
public Mono<CreateMessageResult> handleAsyncSampling(CreateMessageRequest request) {
return Mono.fromCallable(() -> {
String response = generateLLMResponse(request);
return CreateMessageResult.builder()
.role(Role.ASSISTANT)
.content(new TextContent(response))
.model("gpt-4")
.build();
}).subscribeOn(Schedulers.boundedElastic());
}
}
@McpElicitation
@McpElicitation注解用于处理向用户收集额外信息的启发式请求。
基础
@Component
public class ElicitationHandler {
@McpElicitation(clients = "interactive-server")
public ElicitResult handleElicitationRequest(ElicitRequest request) {
// Present the request to the user and gather input
Map<String, Object> userData = presentFormToUser(request.requestedSchema());
if (userData != null) {
return new ElicitResult(ElicitResult.Action.ACCEPT, userData);
} else {
return new ElicitResult(ElicitResult.Action.DECLINE, null);
}
}
}
用户交互
@McpElicitation(clients = "interactive-server")
public ElicitResult handleInteractiveElicitation(ElicitRequest request) {
Map<String, Object> schema = request.requestedSchema();
Map<String, Object> userData = new HashMap<>();
// Check what information is being requested
if (schema != null && schema.containsKey("properties")) {
Map<String, Object> properties = (Map<String, Object>) schema.get("properties");
// Gather user input based on schema
if (properties.containsKey("name")) {
userData.put("name", promptUser("Enter your name:"));
}
if (properties.containsKey("email")) {
userData.put("email", promptUser("Enter your email:"));
}
if (properties.containsKey("preferences")) {
userData.put("preferences", gatherPreferences());
}
}
return new ElicitResult(ElicitResult.Action.ACCEPT, userData);
}
异步启发式请求
@McpElicitation(clients = "interactive-server")
public Mono<ElicitResult> handleAsyncElicitation(ElicitRequest request) {
return Mono.fromCallable(() -> {
// Async user interaction
Map<String, Object> userData = asyncGatherUserInput(request);
return new ElicitResult(ElicitResult.Action.ACCEPT, userData);
}).timeout(Duration.ofSeconds(30))
.onErrorReturn(new ElicitResult(ElicitResult.Action.CANCEL, null));
}
@McpProgress
@McpProgress注解用于处理长时间运行操作的进度通知。
基础
@Component
public class ProgressHandler {
@McpProgress(clients = "my-mcp-server")
public void handleProgressNotification(ProgressNotification notification) {
double percentage = notification.progress() * 100;
System.out.println(String.format("Progress: %.2f%% - %s",
percentage, notification.message()));
}
}
独立参数
@McpProgress(clients = "my-mcp-server")
public void handleProgressWithDetails(
String progressToken,
double progress,
Double total,
String message) {
if (total != null) {
System.out.println(String.format("[%s] %.0f/%.0f - %s",
progressToken, progress, total, message));
} else {
System.out.println(String.format("[%s] %.2f%% - %s",
progressToken, progress * 100, message));
}
// Update UI progress bar
updateProgressBar(progressToken, progress);
}
特定进度
@McpProgress(clients = "long-running-server")
public void handleLongRunningProgress(ProgressNotification notification) {
// Track progress for specific server
progressTracker.update("long-running-server", notification);
// Send notifications if needed
if (notification.progress() >= 1.0) {
notifyCompletion(notification.progressToken());
}
}
@McpToolListChanged
@McpToolListChanged注解用于处理服务器工具列表变更时的通知。
基础
@Component
public class ToolListChangedHandler {
@McpToolListChanged(clients = "tool-server")
public void handleToolListChanged(List<McpSchema.Tool> updatedTools) {
System.out.println("Tool list updated: " + updatedTools.size() + " tools available");
// Update local tool registry
toolRegistry.updateTools(updatedTools);
// Log new tools
for (McpSchema.Tool tool : updatedTools) {
System.out.println(" - " + tool.name() + ": " + tool.description());
}
}
}
同步处理
@McpToolListChanged(clients = "tool-server")
public Mono<Void> handleAsyncToolListChanged(List<McpSchema.Tool> updatedTools) {
return Mono.fromRunnable(() -> {
// Process tool list update asynchronously
processToolListUpdate(updatedTools);
// Notify interested components
eventBus.publish(new ToolListUpdatedEvent(updatedTools));
}).then();
}
专用工具更新
@McpToolListChanged(clients = "dynamic-server")
public void handleDynamicServerToolUpdate(List<McpSchema.Tool> updatedTools) {
// Handle tools from a specific server that frequently changes its tools
dynamicToolManager.updateServerTools("dynamic-server", updatedTools);
// Re-evaluate tool availability
reevaluateToolCapabilities();
}
@McpResourceListChanged
@McpResourceListChanged注解用于处理服务器资源列表变更时的通知。
基础
@Component
public class ResourceListChangedHandler {
@McpResourceListChanged(clients = "resource-server")
public void handleResourceListChanged(List<McpSchema.Resource> updatedResources) {
System.out.println("Resources updated: " + updatedResources.size());
// Update resource cache
resourceCache.clear();
for (McpSchema.Resource resource : updatedResources) {
resourceCache.register(resource);
}
}
}
结合资源分析
@McpResourceListChanged(clients = "resource-server")
public void analyzeResourceChanges(List<McpSchema.Resource> updatedResources) {
// Analyze what changed
Set<String> newUris = updatedResources.stream()
.map(McpSchema.Resource::uri)
.collect(Collectors.toSet());
Set<String> removedUris = previousUris.stream()
.filter(uri -> !newUris.contains(uri))
.collect(Collectors.toSet());
if (!removedUris.isEmpty()) {
handleRemovedResources(removedUris);
}
// Update tracking
previousUris = newUris;
}
@McpPromptListChanged
@McpPromptListChanged 注解用于处理服务器提示列表变更时的通知。
基础
@Component
public class PromptListChangedHandler {
@McpPromptListChanged(clients = "prompt-server")
public void handlePromptListChanged(List<McpSchema.Prompt> updatedPrompts) {
System.out.println("Prompts updated: " + updatedPrompts.size());
// Update prompt catalog
promptCatalog.updatePrompts(updatedPrompts);
// Refresh UI if needed
if (uiController != null) {
uiController.refreshPromptList(updatedPrompts);
}
}
}
同步进程
@McpPromptListChanged(clients = "prompt-server")
public Mono<Void> handleAsyncPromptUpdate(List<McpSchema.Prompt> updatedPrompts) {
return Flux.fromIterable(updatedPrompts)
.flatMap(prompt -> validatePrompt(prompt))
.collectList()
.doOnNext(validPrompts -> {
promptRepository.saveAll(validPrompts);
})
.then();
}
Spring Boot集成
通过Spring Boot自动配置,客户端处理程序会被自动检测并注册:
@SpringBootApplication
public class McpClientApplication {
public static void main(String[] args) {
SpringApplication.run(McpClientApplication.class, args);
}
}
@Component
public class MyClientHandlers {
@McpLogging(clients = "my-server")
public void handleLogs(LoggingMessageNotification notification) {
// Handle logs
}
@McpSampling(clients = "my-server")
public CreateMessageResult handleSampling(CreateMessageRequest request) {
// Handle sampling
}
@McpProgress(clients = "my-server")
public void handleProgress(ProgressNotification notification) {
// Handle progress
}
}
自动配置将执行以下操作:
- 扫描带有MCP客户端注解的Bean
- 创建相应的规范
- 将其注册到MCP客户端
- 支持同步和异步实现
- 通过客户端特定处理程序处理多个客户端
配置
配置客户端注解扫描器和客户端连接:
spring:
ai:
mcp:
client:
type: SYNC # or ASYNC
annotation-scanner:
enabled: true
# Configure client connections - the connection names become clients values
sse:
connections:
my-server: # This becomes the clients
url: http://localhost:8080
tool-server: # Another clients
url: http://localhost:8081
stdio:
connections:
local-server: # This becomes the clients
command: /path/to/mcp-server
args:
- --mode=production
MCP客户端使用指南
@Autowired
private List<McpSyncClient> mcpClients;
// The clients will automatically use your annotated handlers based on clients
// No manual registration needed - handlers are matched to clients by name
更多推荐



所有评论(0)