跳到主要内容
版本:2.0.1

MCP 注释特殊参数

QWen 3.8 中英对照 Special Parameters MCP Annotations Special Parameters

MCP 注解支持多种特殊参数类型,为被注解的方法提供额外的上下文和功能。这些参数由框架自动注入,并且不包含在 JSON Schema 生成中。

特殊参数类型​

MetaProvider​

MetaProvider 接口为工具、提示和资源声明中的 _meta 字段提供数据。

概述​

  • 实现为在 @McpTool(metaProvider = …​)、@McpPrompt(metaProvider = …​) 或 @McpResource(metaProvider = …​) 中引用的类

  • 允许在启动时向工具/提示词/资源规范附加静态或计算得出的元数据

  • 默认的 DefaultMetaProvider 返回一个空映射(不附加 _meta)

自定义 MetaProvider​

public class MyToolMetaProvider implements MetaProvider {

@Override
public Map<String, Object> getMeta() {
return Map.of(
"version", "1.0",
"team", "platform",
"experimental", false
);
}
}

@McpTool(name = "my-tool",
description = "Tool with metadata",
metaProvider = MyToolMetaProvider.class)
public String myTool(@McpToolParam(description = "Input") String input) {
return "Processed: " + input;
}

同样的模式也适用于 @McpPrompt 和 @McpResource。

McpMeta​

McpMeta 类提供了对 MCP 请求、通知和结果元数据的访问。

概览​

  • 用作方法参数时自动注入

  • 不参与参数数量限制和 JSON Schema 生成

  • 通过 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 ReadResourceResult.builder(List.of(
TextResourceContents.builder("secure-data://" + id,
"Access denied").mimeType("text/plain").build()
)).build();
}

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

在提示词中使用​

@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 GetPromptResult.builder(List.of(new PromptMessage(Role.ASSISTANT, TextContent.builder(message).build())))
.description("Localized Prompt")
.build();
}

@McpProgressToken​

@McpProgressToken 注解用于标记一个参数,以从 MCP 请求中接收进度令牌。

概览​

  • 参数类型应为 String
  • 自动从请求中接收进度令牌值
  • 不生成在 JSON Schema 中
  • 如果不存在进度令牌,则注入 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(ProgressNotification.builder(progressToken, 0.0)
.total(1.0).message("Starting " + operation).build());

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

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

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(ProgressNotification.builder(progressToken, 0.0)
.total(fileSize).message("Reading file").build());
}

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

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

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

McpSyncRequestContext / McpAsyncRequestContext​

请求上下文对象为 MCP 请求信息和服务器端操作提供了统一的访问方式。

概述​

  • 为有状态和无状态操作提供统一接口

  • 作为参数使用时自动注入

  • 从 JSON 模式生成中排除

  • 支持高级功能,如日志记录、进度通知、采样、询问(elicitation)和根目录访问(roots access)

  • 兼容有状态(服务器交换)和无状态(传输上下文)模式

上下文获取器​

McpSyncRequestContext 和 McpAsyncRequestContext 都暴露了以下只读上下文:

方法描述
request()原始的 MCP 请求(例如 CallToolRequest、ReadResourceRequest)。使用 request().progressToken() 访问进度令牌。
exchange()底层服务器交换对象(McpSyncServerExchange / McpAsyncServerExchange)。仅在无状态模式下不可用,无状态模式下为 null。
sessionId()当前会话标识符。
clientInfo()客户端实现信息(Implementation)。
clientCapabilities()客户端声明的能力。
requestMeta()来自请求 _meta 字段的元数据映射。如果已经使用上下文对象,优先使用此方法而不是注入 McpMeta。
transportContext()传输层上下文(McpTransportContext)。

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"))
);
}

// Access client root directories (only available in stateful mode)
if (context.rootsEnabled()) {
ListRootsResult roots = context.roots();
roots.roots().forEach(root -> context.info("Client root: " + root.uri()));
}

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 Schema 生成中排除

使用示例​

@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 ReadResourceResult.builder(List.of(
TextResourceContents.builder("stateless://" + id, data).mimeType("text/plain").build()
)).build();
}

CallToolRequest​

对于需要动态架构并访问完整请求的工具,使用的特殊参数。

概述​

  • 提供对完整工具请求的访问
  • 支持在运行时动态处理 schema
  • 自动注入并从 schema 生成中排除
  • 适用于可根据不同输入 schema 进行适配的灵活工具

使用示例​

@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(ProgressNotification.builder(progressToken, 0.0)
.total(1.0).message("Processing dynamic request").build());
}

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

if (progressToken != null) {
exchange.progressNotification(ProgressNotification.builder(progressToken, 1.0)
.total(1.0).message("Complete").build());
}

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

参数注入规则​

自动注入​

以下参数由框架自动注入:

  1. McpMeta - 来自请求 _meta 字段的元数据

  2. @McpProgressToken String - 如果可用,则为进度令牌

  3. McpSyncRequestContext / McpAsyncRequestContext - 统一请求上下文(推荐)

  4. McpSyncServerExchange / McpAsyncServerExchange - 低级服务器交换上下文(仅适用于有状态操作)

  5. McpTransportContext - 用于无状态操作的传输上下文

  6. CallToolRequest - 用于动态模式的完整工具请求(仅适用于工具)

模式生成​

特殊参数将被排除在 JSON schema 生成之外:

  • 它们不会出现在工具输入模式(input schema)中

  • 它们不计入参数限制

  • 它们对 MCP 客户端不可见

空值处理​

  • McpMeta - 永不为 null,若无元数据则为空对象

  • @McpProgressToken - 若未提供 token,可为 null

  • Server exchanges - 在正确配置时永不为 null

  • CallToolRequest - 对于 tool 方法,永不为 null

最佳实践​

使用 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);
}

Progress Token 空值检查​

@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(ProgressNotification.builder(progressToken, 0.0)
.total(1.0).message("Starting").build());
}

// Perform work...

if (progressToken != null) {
exchange.progressNotification(ProgressNotification.builder(progressToken, 1.0)
.total(1.0).message("Complete").build());
}

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";
}

附加资源​