收藏本站,收获最前沿的人工智能与编程资讯!!

Spring AI ​MCP 客户端注解

技术文档 32℃ 0

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 服务端发送的大语言模型补全采样请求。

同步实现

@Component
public class SamplingHandler {

    @McpSampling(clients = "llm-server")
    public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
        // 处理请求并生成响应
        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 MonohandleAsyncSampling(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) {
        // 向用户展示请求并收集输入信息
        MapuserData = 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) {
    Mapschema = request.requestedSchema();
    MapuserData = new HashMap<>();

    // 检查需要请求的信息
    if (schema != null && schema.containsKey("properties")) {
        Mapproperties = (Map) schema.get("properties");

        // 根据模式收集用户输入
        if (properties.containsKey("name")) {
            userData.put("name", promptUser("请输入你的姓名:"));
        }
        if (properties.containsKey("email")) {
            userData.put("email", promptUser("请输入你的邮箱:"));
        }
        if (properties.containsKey("preferences")) {
            userData.put("preferences", gatherPreferences());
        }
    }

    return new ElicitResult(ElicitResult.Action.ACCEPT, userData);
}

异步信息诱导

@McpElicitation(clients = "interactive-server")
public MonohandleAsyncElicitation(ElicitRequest request) {
    return Mono.fromCallable(() -> {
        // 异步用户交互
        MapuserData = 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("进度:%.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));
    }

    // 更新界面进度条
    updateProgressBar(progressToken, progress);
}

客户端专属进度处理

@McpProgress(clients = "long-running-server")
public void handleLongRunningProgress(ProgressNotification notification) {
    // 跟踪指定服务端的进度
    progressTracker.update("long-running-server", notification);

    // 按需发送通知
    if (notification.progress() >= 1.0) {
        notifyCompletion(notification.progressToken());
    }
}

@McpToolListChanged

@McpToolListChanged 注解用于处理服务端工具列表发生变更时的通知。

基础用法

@Component
public class ToolListChangedHandler {

    @McpToolListChanged(clients = "tool-server")
    public void handleToolListChanged(ListupdatedTools) {
        System.out.println("工具列表已更新:可用工具数量 " + updatedTools.size());

        // 更新本地工具注册表
        toolRegistry.updateTools(updatedTools);

        // 记录新工具
        for (McpSchema.Tool tool : updatedTools) {
            System.out.println("  - " + tool.name() + ": " + tool.description());
        }
    }
}

异步处理

@McpToolListChanged(clients = "tool-server")
public MonohandleAsyncToolListChanged(ListupdatedTools) {
    return Mono.fromRunnable(() -> {
        // 异步处理工具列表更新
        processToolListUpdate(updatedTools);

        // 通知相关组件
        eventBus.publish(new ToolListUpdatedEvent(updatedTools));
    }).then();
}

客户端专属工具更新

@McpToolListChanged(clients = "dynamic-server")
public void handleDynamicServerToolUpdate(ListupdatedTools) {
    // 处理来自频繁变更工具的指定服务端的工具
    dynamicToolManager.updateServerTools("dynamic-server", updatedTools);

    // 重新评估工具可用性
    reevaluateToolCapabilities();
}

@McpResourceListChanged

@McpResourceListChanged 注解用于处理服务端资源列表发生变更时的通知。

基础用法

@Component
public class ResourceListChangedHandler {

    @McpResourceListChanged(clients = "resource-server")
    public void handleResourceListChanged(ListupdatedResources) {
        System.out.println("资源已更新:数量 " + updatedResources.size());

        // 更新资源缓存
        resourceCache.clear();
        for (McpSchema.Resource resource : updatedResources) {
            resourceCache.register(resource);
        }
    }
}

资源变更分析

@McpResourceListChanged(clients = "resource-server")
public void analyzeResourceChanges(ListupdatedResources) {
    // 分析变更内容
    SetnewUris = updatedResources.stream()
        .map(McpSchema.Resource::uri)
        .collect(Collectors.toSet());

    SetremovedUris = previousUris.stream()
        .filter(uri -> !newUris.contains(uri))
        .collect(Collectors.toSet());

    if (!removedUris.isEmpty()) {
        handleRemovedResources(removedUris);
    }

    // 更新跟踪记录
    previousUris = newUris;
}

@McpPromptListChanged

@McpPromptListChanged 注解用于处理服务端提示词列表发生变更时的通知。

基础用法

@Component
public class PromptListChangedHandler {

    @McpPromptListChanged(clients = "prompt-server")
    public void handlePromptListChanged(ListupdatedPrompts) {
        System.out.println("提示词已更新:数量 " + updatedPrompts.size());

        // 更新提示词目录
        promptCatalog.updatePrompts(updatedPrompts);

        // 按需刷新界面
        if (uiController != null) {
            uiController.refreshPromptList(updatedPrompts);
        }
    }
}

异步处理

@McpPromptListChanged(clients = "prompt-server")
public MonohandleAsyncPromptUpdate(ListupdatedPrompts) {
    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) {
        // 处理日志
    }

    @McpSampling(clients = "my-server")
    public CreateMessageResult handleSampling(CreateMessageRequest request) {
        // 处理采样
    }

    @McpProgress(clients = "my-server")
    public void handleProgress(ProgressNotification notification) {
        // 处理进度
    }
}

自动配置会执行以下操作:

  • 扫描带有 MCP 客户端注解的 Bean

  • 创建对应的规范定义

  • 将其注册到 MCP 客户端

  • 支持同步和异步两种实现方式

  • 通过客户端专属处理器支持多客户端场景

配置属性

配置客户端注解扫描器和客户端连接:

spring:
  ai:
    mcp:
      client:
        type: SYNC  # 或 ASYNC
        annotation-scanner:
          enabled: true
        # 配置客户端连接 - 连接名称会作为注解中clients的值
        sse:
          connections:
            my-server:  # 该名称作为注解的clients值
              url: http://localhost:8080
            tool-server:  # 另一个clients值
              url: http://localhost:8081
        stdio:
          connections:
            local-server:  # 该名称作为注解的clients值
              command: /path/to/mcp-server
              args:
                - --mode=production

注解中的 clients 参数必须与配置中定义的连接名称匹配。在上述示例中,合法的 clients 值为:"my-server"、"tool-server" 和 "local-server"。

MCP 客户端使用方式

带注解的处理器会自动与 MCP 客户端集成:

@Autowired
private ListmcpClients;

// 客户端会根据clients配置自动使用你定义的注解处理器
// 无需手动注册 - 处理器会通过名称与客户端匹配关联

对于每个 MCP 客户端连接,带有匹配 clients 值的处理器会被自动注册,并在对应事件触发时自动调用。

相关推荐