このバージョンはまだ開発中であり、まだ安定しているとは考えられていません。最新のスナップショットバージョンについては、Spring AI 1.0.3 を使用してください。 |
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)
));
}
Usage in Prompts
@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
The @McpProgressToken
annotation marks a parameter to receive progress tokens from MCP requests.
概要
Parameter type should be
String
Automatically receives the progress token value from the request
Excluded from the generated JSON schema
If no progress token is present,
null
is injectedUsed for tracking long-running operations
ツールでの使用
@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)
));
}
McpSyncServerExchange/McpAsyncServerExchange
Server exchange objects provide full access to server-side MCP operations.
概要
Provides stateful context for server operations
Automatically injected when used as a parameter
Excluded from JSON schema generation
Enables advanced features like logging, progress notifications, and client calls
McpSyncServerExchange の機能
@McpTool(name = "advanced-tool", description = "Tool with full server capabilities")
public String advancedTool(
McpSyncServerExchange exchange,
@McpToolParam(description = "Input", required = true) String input) {
// Send logging notification
exchange.loggingNotification(LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.logger("advanced-tool")
.data("Processing: " + input)
.build());
// Ping the client
exchange.ping();
// Request additional information from user
ElicitRequest elicitRequest = ElicitRequest.builder()
.message("Need additional information")
.requestedSchema(Map.of(
"type", "object",
"properties", Map.of(
"confirmation", Map.of("type", "boolean")
)
))
.build();
ElicitResult elicitResult = exchange.createElicitation(elicitRequest);
// Request LLM sampling
CreateMessageRequest messageRequest = CreateMessageRequest.builder()
.messages(List.of(new SamplingMessage(Role.USER,
new TextContent("Process: " + input))))
.modelPreferences(ModelPreferences.builder()
.hints(List.of(ModelHint.of("gpt-4")))
.build())
.build();
CreateMessageResult samplingResult = exchange.createMessage(messageRequest);
return "Processed with advanced features";
}
McpAsyncServerExchange の機能
@McpTool(name = "async-advanced-tool", description = "Async tool with server capabilities")
public Mono<String> asyncAdvancedTool(
McpAsyncServerExchange exchange,
@McpToolParam(description = "Input", required = true) String input) {
return Mono.fromCallable(() -> {
// Send async logging
exchange.loggingNotification(LoggingMessageNotification.builder()
.level(LoggingLevel.INFO)
.data("Async processing: " + input)
.build());
return "Started processing";
})
.flatMap(msg -> {
// Chain async operations
return exchange.createMessage(/* request */)
.map(result -> "Completed: " + result);
});
}
McpTransportContext
Lightweight context for stateless operations.
概要
Provides minimal context without full server exchange
Used in stateless implementations
Automatically injected when used as a parameter
Excluded from JSON schema generation
使用例
@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
Special parameter for tools that need access to the full request with dynamic schema.
概要
Provides access to the complete tool request
Enables dynamic schema handling at runtime
Automatically injected and excluded from schema generation
Useful for flexible tools that adapt to different input schemas
使用例
@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();
}
Mixed Parameters
@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;
}
With Progress Token
@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();
}
Parameter Injection Rules
Automatic Injection
The following parameters are automatically injected by the framework:
McpMeta
- Metadata from the request@McpProgressToken String
- Progress token if availableMcpSyncServerExchange
/McpAsyncServerExchange
- Server exchange contextMcpTransportContext
- Transport context for stateless operationsCallToolRequest
- Full tool request for dynamic schema
ベストプラクティス
Use McpMeta for Context
@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 Null Checks
@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";
}