Spring AI / Model Context Protocol (MCP) / MCP Annotations / Client Annotations

📅 2026/6/30 22:53:46
Spring AI / Model Context Protocol (MCP) / MCP Annotations / Client Annotations
Spring AI 参考文档模型上下文协议MCPMCP 注解客户端注解MCP 客户端注解提供了一种使用 Java 注解实现 MCP 客户端处理器的声明式方式。这些注解简化了服务端通知的处理和客户端操作。所有 MCP 客户端注解必须包含clients参数用于将处理器与特定的 MCP 客户端连接关联起来。clients的值必须与应用程序配置中配置的连接名称匹配。客户端注解McpLoggingMcpLogging注解用于处理来自 MCP 服务端的日志消息通知。基本用法ComponentpublicclassLoggingHandler{McpLogging(clientsmy-mcp-server)publicvoidhandleLoggingMessage(LoggingMessageNotificationnotification){System.out.println(Received log: notification.level() - notification.data());}}使用独立参数McpLogging(clientsmy-mcp-server)publicvoidhandleLoggingWithParams(LoggingLevellevel,Stringlogger,Stringdata){System.out.println(String.format([%s] %s: %s,level,logger,data));}McpSamplingMcpSampling注解用于处理来自 MCP 服务端的采样请求以进行 LLM 补全。同步实现ComponentpublicclassSamplingHandler{McpSampling(clientsllm-server)publicCreateMessageResulthandleSamplingRequest(CreateMessageRequestrequest){// 处理请求并生成响应StringresponsegenerateLLMResponse(request);returnCreateMessageResult.builder(Role.ASSISTANT,response,gpt-4).build();}}异步实现ComponentpublicclassAsyncSamplingHandler{McpSampling(clientsllm-server)publicMonoCreateMessageResulthandleAsyncSampling(CreateMessageRequestrequest){returnMono.fromCallable(()-{StringresponsegenerateLLMResponse(request);returnCreateMessageResult.builder(Role.ASSISTANT,response,gpt-4).build();}).subscribeOn(Schedulers.boundedElastic());}}McpElicitationMcpElicitation注解用于处理征求请求以从用户收集额外信息。基本用法ComponentpublicclassElicitationHandler{McpElicitation(clientsinteractive-server)publicElicitResulthandleElicitationRequest(ElicitRequestrequest){// 向用户展示请求并收集输入MapString,ObjectuserDatapresentFormToUser(request.requestedSchema());if(userData!null){returnnewElicitResult(ElicitResult.Action.ACCEPT,userData);}else{returnnewElicitResult(ElicitResult.Action.DECLINE,null);}}}带用户交互McpElicitation(clientsinteractive-server)publicElicitResulthandleInteractiveElicitation(ElicitRequestrequest){MapString,Objectschemarequest.requestedSchema();MapString,ObjectuserDatanewHashMap();// 检查请求了哪些信息if(schema!nullschema.containsKey(properties)){MapString,Objectproperties(MapString,Object)schema.get(properties);// 根据 schema 收集用户输入if(properties.containsKey(name)){userData.put(name,promptUser(请输入您的姓名));}if(properties.containsKey(email)){userData.put(email,promptUser(请输入您的邮箱));}if(properties.containsKey(preferences)){userData.put(preferences,gatherPreferences());}}returnnewElicitResult(ElicitResult.Action.ACCEPT,userData);}异步征求McpElicitation(clientsinteractive-server)publicMonoElicitResulthandleAsyncElicitation(ElicitRequestrequest){returnMono.fromCallable(()-{// 异步用户交互MapString,ObjectuserDataasyncGatherUserInput(request);returnnewElicitResult(ElicitResult.Action.ACCEPT,userData);}).timeout(Duration.ofSeconds(30)).onErrorReturn(newElicitResult(ElicitResult.Action.CANCEL,null));}McpProgressMcpProgress注解用于处理长时间运行操作的进度通知。基本用法ComponentpublicclassProgressHandler{McpProgress(clientsmy-mcp-server)publicvoidhandleProgressNotification(ProgressNotificationnotification){doublepercentagenotification.progress()*100;System.out.println(String.format(Progress: %.2f%% - %s,percentage,notification.message()));}}使用独立参数McpProgress(clientsmy-mcp-server)publicvoidhandleProgressWithDetails(StringprogressToken,doubleprogress,Doubletotal,Stringmessage){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));}// 更新 UI 进度条updateProgressBar(progressToken,progress);}客户端特定的进度McpProgress(clientslong-running-server)publicvoidhandleLongRunningProgress(ProgressNotificationnotification){// 跟踪特定服务器的进度progressTracker.update(long-running-server,notification);// 如需则发送通知if(notification.progress()1.0){notifyCompletion(notification.progressToken());}}McpToolListChangedMcpToolListChanged注解用于处理服务端工具列表变更时的通知。基本用法ComponentpublicclassToolListChangedHandler{McpToolListChanged(clientstool-server)publicvoidhandleToolListChanged(ListMcpSchema.ToolupdatedTools){System.out.println(Tool list updated: updatedTools.size() tools available);// 更新本地工具注册表toolRegistry.updateTools(updatedTools);// 记录新工具for(McpSchema.Tooltool:updatedTools){System.out.println( - tool.name(): tool.description());}}}异步处理McpToolListChanged(clientstool-server)publicMonoVoidhandleAsyncToolListChanged(ListMcpSchema.ToolupdatedTools){returnMono.fromRunnable(()-{// 异步处理工具列表更新processToolListUpdate(updatedTools);// 通知相关组件eventBus.publish(newToolListUpdatedEvent(updatedTools));}).then();}客户端特定的工具更新McpToolListChanged(clientsdynamic-server)publicvoidhandleDynamicServerToolUpdate(ListMcpSchema.ToolupdatedTools){// 处理来自频繁变更工具的特定服务器的工具dynamicToolManager.updateServerTools(dynamic-server,updatedTools);// 重新评估工具能力reevaluateToolCapabilities();}McpResourceListChangedMcpResourceListChanged注解用于处理服务端资源列表变更时的通知。基本用法ComponentpublicclassResourceListChangedHandler{McpResourceListChanged(clientsresource-server)publicvoidhandleResourceListChanged(ListMcpSchema.ResourceupdatedResources){System.out.println(Resources updated: updatedResources.size());// 更新资源缓存resourceCache.clear();for(McpSchema.Resourceresource:updatedResources){resourceCache.register(resource);}}}带资源分析McpResourceListChanged(clientsresource-server)publicvoidanalyzeResourceChanges(ListMcpSchema.ResourceupdatedResources){// 分析变更内容SetStringnewUrisupdatedResources.stream().map(McpSchema.Resource::uri).collect(Collectors.toSet());SetStringremovedUrispreviousUris.stream().filter(uri-!newUris.contains(uri)).collect(Collectors.toSet());if(!removedUris.isEmpty()){handleRemovedResources(removedUris);}// 更新跟踪previousUrisnewUris;}McpPromptListChangedMcpPromptListChanged注解用于处理服务端提示词列表变更时的通知。基本用法ComponentpublicclassPromptListChangedHandler{McpPromptListChanged(clientsprompt-server)publicvoidhandlePromptListChanged(ListMcpSchema.PromptupdatedPrompts){System.out.println(Prompts updated: updatedPrompts.size());// 更新提示词目录promptCatalog.updatePrompts(updatedPrompts);// 如需则刷新 UIif(uiController!null){uiController.refreshPromptList(updatedPrompts);}}}异步处理McpPromptListChanged(clientsprompt-server)publicMonoVoidhandleAsyncPromptUpdate(ListMcpSchema.PromptupdatedPrompts){returnFlux.fromIterable(updatedPrompts).flatMap(prompt-validatePrompt(prompt)).collectList().doOnNext(validPrompts-{promptRepository.saveAll(validPrompts);}).then();}Spring Boot 集成借助 Spring Boot 自动配置客户端处理器会被自动检测和注册SpringBootApplicationpublicclassMcpClientApplication{publicstaticvoidmain(String[]args){SpringApplication.run(McpClientApplication.class,args);}}ComponentpublicclassMyClientHandlers{McpLogging(clientsmy-server)publicvoidhandleLogs(LoggingMessageNotificationnotification){// 处理日志}McpSampling(clientsmy-server)publicCreateMessageResulthandleSampling(CreateMessageRequestrequest){// 处理采样}McpProgress(clientsmy-server)publicvoidhandleProgress(ProgressNotificationnotification){// 处理进度}}自动配置将扫描带有 MCP 客户端注解的 Bean创建适当的规范将其注册到 MCP 客户端支持同步和异步实现使用客户端特定的处理器处理多个客户端配置属性配置客户端注解扫描器和客户端连接spring:ai:mcp:client:type:SYNC# 或 ASYNCannotation-scanner:enabled:true# 配置客户端连接 - 连接名称成为 clients 的值sse:connections:my-server:# 这成为 clientsurl:http://localhost:8080tool-server:# 另一个 clientsurl:http://localhost:8081stdio:connections:local-server:# 这成为 clientscommand:/path/to/mcp-serverargs:---modeproduction注解中的clients参数必须与配置中定义的连接名称匹配。在上面的示例中有效的clients值为my-server、tool-server和local-server。与 MCP 客户端一起使用带注解的处理器会自动与 MCP 客户端集成AutowiredprivateListMcpSyncClientmcpClients;// 客户端将根据 clients 自动使用您的注解处理器// 无需手动注册 - 处理器会按名称与客户端匹配对于每个 MCP 客户端连接具有匹配clients的处理器会自动注册并在相应事件发生时被调用。其他资源MCP 注解概述服务端注解特殊参数MCP 客户端启动器 Starter