MCP 註解特殊引數

MCP 註解支援幾種特殊的引數型別,為帶註解的方法提供額外的上下文和功能。這些引數由框架自動注入,並排除在 JSON 模式生成之外。

特殊引數型別

McpMeta

McpMeta 類提供對 MCP 請求、通知和結果中元資料的訪問。

概述

  • 作為方法引數使用時自動注入

  • 排除在引數計數限制和 JSON 模式生成之外

  • 透過 get(String key) 方法方便地訪問元資料

  • 如果請求中不存在元資料,則注入一個空的 McpMeta 物件

在工具中的用法

@McpTool(name = "contextual-tool", description = "Tool with metadata access")
public String processWithContext(
        @McpToolParam(description = "Input data", required = true) String data,
        McpMeta meta) {

    // Access metadata from the request
    String userId = (String) meta.get("userId");
    String sessionId = (String) meta.get("sessionId");
    String userRole = (String) meta.get("userRole");

    // Use metadata to customize behavior
    if ("admin".equals(userRole)) {
        return processAsAdmin(data, userId);
    } else {
        return processAsUser(data, userId);
    }
}

在資源中的用法

@McpResource(uri = "secure-data://{id}", name = "Secure Data")
public ReadResourceResult getSecureData(String id, McpMeta meta) {

    String requestingUser = (String) meta.get("requestingUser");
    String accessLevel = (String) meta.get("accessLevel");

    // Check access permissions using metadata
    if (!"admin".equals(accessLevel)) {
        return new ReadResourceResult(List.of(
            new TextResourceContents("secure-data://" + id,
                "text/plain", "Access denied")
        ));
    }

    String data = loadSecureData(id);
    return new ReadResourceResult(List.of(
        new TextResourceContents("secure-data://" + id,
            "text/plain", data)
    ));
}

在提示中的用法

@McpPrompt(name = "localized-prompt", description = "Localized prompt generation")
public GetPromptResult localizedPrompt(
        @McpArg(name = "topic", required = true) String topic,
        McpMeta meta) {

    String language = (String) meta.get("language");
    String region = (String) meta.get("region");

    // Generate localized content based on metadata
    String message = generateLocalizedMessage(topic, language, region);

    return new GetPromptResult("Localized Prompt",
        List.of(new PromptMessage(Role.ASSISTANT, new TextContent(message)))
    );
}

@McpProgressToken

@McpProgressToken 註解標記一個引數以接收來自 MCP 請求的進度令牌。

概述

  • 引數型別應為 String

  • 自動從請求中接收進度令牌值

  • 排除在生成的 JSON 模式之外

  • 如果不存在進度令牌,則注入 null

  • 用於跟蹤長時間執行的操作

在工具中的用法

@McpTool(name = "long-operation", description = "Long-running operation with progress")
public String performLongOperation(
        @McpProgressToken String progressToken,
        @McpToolParam(description = "Operation name", required = true) String operation,
        @McpToolParam(description = "Duration in seconds", required = true) int duration,
        McpSyncServerExchange exchange) {

    if (progressToken != null) {
        // Send initial progress
        exchange.progressNotification(new ProgressNotification(
            progressToken, 0.0, 1.0, "Starting " + operation));

        // Simulate work with progress updates
        for (int i = 1; i <= duration; i++) {
            Thread.sleep(1000);
            double progress = (double) i / duration;

            exchange.progressNotification(new ProgressNotification(
                progressToken, progress, 1.0,
                String.format("Processing... %d%%", (int)(progress * 100))));
        }
    }

    return "Operation " + operation + " completed";
}

在資源中的用法

@McpResource(uri = "large-file://{path}", name = "Large File Resource")
public ReadResourceResult getLargeFile(
        @McpProgressToken String progressToken,
        String path,
        McpSyncServerExchange exchange) {

    File file = new File(path);
    long fileSize = file.length();

    if (progressToken != null) {
        // Track file reading progress
        exchange.progressNotification(new ProgressNotification(
            progressToken, 0.0, fileSize, "Reading file"));
    }

    String content = readFileWithProgress(file, progressToken, exchange);

    if (progressToken != null) {
        exchange.progressNotification(new ProgressNotification(
            progressToken, fileSize, fileSize, "File read complete"));
    }

    return new ReadResourceResult(List.of(
        new TextResourceContents("large-file://" + path, "text/plain", content)
    ));
}

McpSyncRequestContext / McpAsyncRequestContext

請求上下文物件提供對 MCP 請求資訊和伺服器端操作的統一訪問。

概述

  • 為有狀態和無狀態操作提供統一介面

  • 作為引數使用時自動注入

  • 排除在 JSON 模式生成之外

  • 啟用日誌記錄、進度通知、取樣和引導等高階功能

  • 適用於有狀態(伺服器交換)和無狀態(傳輸上下文)模式

McpSyncRequestContext 功能

public record UserInfo(String name, String email, int age) {}

@McpTool(name = "advanced-tool", description = "Tool with full server capabilities")
public String advancedTool(
        McpSyncRequestContext context,
        @McpToolParam(description = "Input", required = true) String input) {

    // Send logging notification
    context.info("Processing: " + input);

    // Ping the client
    context.ping();

    // Send progress updates
    context.progress(50); // 50% complete

    // Check if elicitation is supported before using it
    if (context.elicitEnabled()) {
        // Request additional information from user
        StructuredElicitResult<UserInfo> elicitResult = context.elicit(
            e -> e.message("Need additional information"),
            UserInfo.class
        );

        if (elicitResult.action() == ElicitResult.Action.ACCEPT) {
            UserInfo userInfo = elicitResult.structuredContent();
            // Use the user information
        }
    }

    // Check if sampling is supported before using it
    if (context.sampleEnabled()) {
        // Request LLM sampling
        CreateMessageResult samplingResult = context.sample(
            s -> s.message("Process: " + input)
                .modelPreferences(pref -> pref.modelHints("gpt-4"))
        );
    }

    return "Processed with advanced features";
}

McpAsyncRequestContext 功能

public record UserInfo(String name, String email, int age) {}

@McpTool(name = "async-advanced-tool", description = "Async tool with server capabilities")
public Mono<String> asyncAdvancedTool(
        McpAsyncRequestContext context,
        @McpToolParam(description = "Input", required = true) String input) {

    return context.info("Async processing: " + input)
        .then(context.progress(25))
        .then(context.ping())
        .flatMap(v -> {
            // Perform elicitation if supported
            if (context.elicitEnabled()) {
                return context.elicitation(UserInfo.class)
                    .map(userInfo -> "Processing for user: " + userInfo.name());
            }
            return Mono.just("Processing...");
        })
        .flatMap(msg -> {
            // Perform sampling if supported
            if (context.sampleEnabled()) {
                return context.sampling("Process: " + input)
                    .map(result -> "Completed: " + result);
            }
            return Mono.just("Completed: " + msg);
        });
}

McpTransportContext

用於無狀態操作的輕量級上下文。

概述

  • 提供最小上下文,無需完整的伺服器交換

  • 用於無狀態實現

  • 作為引數使用時自動注入

  • 排除在 JSON 模式生成之外

使用示例

@McpTool(name = "stateless-tool", description = "Stateless tool with context")
public String statelessTool(
        McpTransportContext context,
        @McpToolParam(description = "Input", required = true) String input) {

    // Limited context access
    // Useful for transport-level operations

    return "Processed in stateless mode: " + input;
}

@McpResource(uri = "stateless://{id}", name = "Stateless Resource")
public ReadResourceResult statelessResource(
        McpTransportContext context,
        String id) {

    // Access transport context if needed
    String data = loadData(id);

    return new ReadResourceResult(List.of(
        new TextResourceContents("stateless://" + id, "text/plain", data)
    ));
}

CallToolRequest

需要訪問帶有動態模式的完整請求的工具的特殊引數。

概述

  • 提供對完整工具請求的訪問

  • 在執行時實現動態模式處理

  • 自動注入並排除在模式生成之外

  • 適用於適應不同輸入模式的靈活工具

使用示例

@McpTool(name = "dynamic-tool", description = "Tool with dynamic schema support")
public CallToolResult processDynamicSchema(CallToolRequest request) {
    Map<String, Object> args = request.arguments();

    // Process based on whatever schema was provided at runtime
    StringBuilder result = new StringBuilder("Processed:\n");

    for (Map.Entry<String, Object> entry : args.entrySet()) {
        result.append("  ").append(entry.getKey())
              .append(": ").append(entry.getValue()).append("\n");
    }

    return CallToolResult.builder()
        .addTextContent(result.toString())
        .build();
}

混合引數

@McpTool(name = "hybrid-tool", description = "Tool with typed and dynamic parameters")
public String processHybrid(
        @McpToolParam(description = "Operation", required = true) String operation,
        @McpToolParam(description = "Priority", required = false) Integer priority,
        CallToolRequest request) {

    // Use typed parameters for known fields
    String result = "Operation: " + operation;
    if (priority != null) {
        result += " (Priority: " + priority + ")";
    }

    // Access additional dynamic arguments
    Map<String, Object> allArgs = request.arguments();

    // Remove known parameters to get only additional ones
    Map<String, Object> additionalArgs = new HashMap<>(allArgs);
    additionalArgs.remove("operation");
    additionalArgs.remove("priority");

    if (!additionalArgs.isEmpty()) {
        result += " with " + additionalArgs.size() + " additional parameters";
    }

    return result;
}

帶進度令牌

@McpTool(name = "flexible-with-progress", description = "Flexible tool with progress")
public CallToolResult flexibleWithProgress(
        @McpProgressToken String progressToken,
        CallToolRequest request,
        McpSyncServerExchange exchange) {

    Map<String, Object> args = request.arguments();

    if (progressToken != null) {
        exchange.progressNotification(new ProgressNotification(
            progressToken, 0.0, 1.0, "Processing dynamic request"));
    }

    // Process dynamic arguments
    String result = processDynamicArgs(args);

    if (progressToken != null) {
        exchange.progressNotification(new ProgressNotification(
            progressToken, 1.0, 1.0, "Complete"));
    }

    return CallToolResult.builder()
        .addTextContent(result)
        .build();
}

引數注入規則

自動注入

以下引數由框架自動注入

  1. McpMeta - 來自請求的元資料

  2. @McpProgressToken String - 如果可用則為進度令牌

  3. McpSyncServerExchange / McpAsyncServerExchange - 伺服器交換上下文

  4. McpTransportContext - 用於無狀態操作的傳輸上下文

  5. CallToolRequest - 用於動態模式的完整工具請求

模式生成

特殊引數排除在 JSON 模式生成之外

  • 它們不會出現在工具的輸入模式中

  • 它們不計入引數限制

  • 它們對 MCP 客戶端不可見

空值處理

  • McpMeta - 永不為空,如果沒有元資料則為空物件

  • @McpProgressToken - 如果未提供令牌,則可能為 null

  • 伺服器交換 - 正確配置時永不為空

  • CallToolRequest - 對於工具方法永不為空

最佳實踐

使用 McpMeta 獲取上下文

@McpTool(name = "context-aware", description = "Context-aware tool")
public String contextAware(
        @McpToolParam(description = "Data", required = true) String data,
        McpMeta meta) {

    // Always check for null values in metadata
    String userId = (String) meta.get("userId");
    if (userId == null) {
        userId = "anonymous";
    }

    return processForUser(data, userId);
}

進度令牌空值檢查

@McpTool(name = "safe-progress", description = "Safe progress handling")
public String safeProgress(
        @McpProgressToken String progressToken,
        @McpToolParam(description = "Task", required = true) String task,
        McpSyncServerExchange exchange) {

    // Always check if progress token is available
    if (progressToken != null) {
        exchange.progressNotification(new ProgressNotification(
            progressToken, 0.0, 1.0, "Starting"));
    }

    // Perform work...

    if (progressToken != null) {
        exchange.progressNotification(new ProgressNotification(
            progressToken, 1.0, 1.0, "Complete"));
    }

    return "Task completed";
}

選擇正確的上下文

  • 使用 McpSyncRequestContext / McpAsyncRequestContext 統一訪問請求上下文,支援有狀態和無狀態操作,並提供便捷的輔助方法

  • 當只需要傳輸級別上下文時,使用 McpTransportContext 進行簡單的無狀態操作

  • 對於最簡單的情況,完全省略上下文引數

能力檢查

在使用客戶端功能之前,務必檢查功能支援

@McpTool(name = "capability-aware", description = "Tool that checks capabilities")
public String capabilityAware(
        McpSyncRequestContext context,
        @McpToolParam(description = "Data", required = true) String data) {

    // Check if elicitation is supported before using it
    if (context.elicitEnabled()) {
        // Safe to use elicitation
        var result = context.elicit(UserInfo.class);
        // Process result...
    }

    // Check if sampling is supported before using it
    if (context.sampleEnabled()) {
        // Safe to use sampling
        var samplingResult = context.sample("Process: " + data);
        // Process result...
    }

    // Note: Stateless servers do not support bidirectional operations
    // (roots, elicitation, sampling) and will return false for these checks

    return "Processed with capability awareness";
}
© . This site is unofficial and not affiliated with VMware.