MCP 伺服器註解
MCP 伺服器註解提供了一種宣告式的方式,使用 Java 註解實現 MCP 伺服器功能。這些註解簡化了工具、資源、提示和完成處理程式的建立。
伺服器註解
@McpTool
@McpTool 註解將方法標記為 MCP 工具實現,並自動生成 JSON 模式。
基本用法
@Component
public class CalculatorTools {
@McpTool(name = "add", description = "Add two numbers together")
public int add(
@McpToolParam(description = "First number", required = true) int a,
@McpToolParam(description = "Second number", required = true) int b) {
return a + b;
}
}
高階功能
@McpTool(name = "calculate-area",
description = "Calculate the area of a rectangle",
annotations = McpTool.McpAnnotations(
title = "Rectangle Area Calculator",
readOnlyHint = true,
destructiveHint = false,
idempotentHint = true
))
public AreaResult calculateRectangleArea(
@McpToolParam(description = "Width", required = true) double width,
@McpToolParam(description = "Height", required = true) double height) {
return new AreaResult(width * height, "square units");
}
帶請求上下文
工具可以訪問請求上下文以進行高階操作
@McpTool(name = "process-data", description = "Process data with request context")
public String processData(
McpSyncRequestContext context,
@McpToolParam(description = "Data to process", required = true) String data) {
// Send logging notification
context.info("Processing data: " + data);
// Send progress notification (using convenient method)
context.progress(p -> p.progress(0.5).total(1.0).message("Processing..."));
// Ping the client
context.ping();
return "Processed: " + data.toUpperCase();
}
動態模式支援
工具可以接受 CallToolRequest 以進行執行時模式處理
@McpTool(name = "flexible-tool", description = "Process dynamic schema")
public CallToolResult processDynamic(CallToolRequest request) {
Map<String, Object> args = request.arguments();
// Process based on runtime schema
String result = "Processed " + args.size() + " arguments dynamically";
return CallToolResult.builder()
.addTextContent(result)
.build();
}
進度跟蹤
工具可以接收進度令牌以跟蹤長時間執行的操作
@McpTool(name = "long-task", description = "Long-running task with progress")
public String performLongTask(
McpSyncRequestContext context,
@McpToolParam(description = "Task name", required = true) String taskName) {
// Access progress token from context
String progressToken = context.request().progressToken();
if (progressToken != null) {
context.progress(p -> p.progress(0.0).total(1.0).message("Starting task"));
// Perform work...
context.progress(p -> p.progress(1.0).total(1.0).message("Task completed"));
}
return "Task " + taskName + " completed";
}
@McpResource
@McpResource 註解透過 URI 模板提供對資源的訪問。
基本用法
@Component
public class ResourceProvider {
@McpResource(
uri = "config://{key}",
name = "Configuration",
description = "Provides configuration data")
public String getConfig(String key) {
return configData.get(key);
}
}
帶 ReadResourceResult
@McpResource(
uri = "user-profile://{username}",
name = "User Profile",
description = "Provides user profile information")
public ReadResourceResult getUserProfile(String username) {
String profileData = loadUserProfile(username);
return new ReadResourceResult(List.of(
new TextResourceContents(
"user-profile://" + username,
"application/json",
profileData)
));
}
帶請求上下文
@McpResource(
uri = "data://{id}",
name = "Data Resource",
description = "Resource with request context")
public ReadResourceResult getData(
McpSyncRequestContext context,
String id) {
// Send logging notification using convenient method
context.info("Accessing resource: " + id);
// Ping the client
context.ping();
String data = fetchData(id);
return new ReadResourceResult(List.of(
new TextResourceContents("data://" + id, "text/plain", data)
));
}
@McpPrompt
@McpPrompt 註解為 AI 互動生成提示訊息。
基本用法
@Component
public class PromptProvider {
@McpPrompt(
name = "greeting",
description = "Generate a greeting message")
public GetPromptResult greeting(
@McpArg(name = "name", description = "User's name", required = true)
String name) {
String message = "Hello, " + name + "! How can I help you today?";
return new GetPromptResult(
"Greeting",
List.of(new PromptMessage(Role.ASSISTANT, new TextContent(message)))
);
}
}
帶可選引數
@McpPrompt(
name = "personalized-message",
description = "Generate a personalized message")
public GetPromptResult personalizedMessage(
@McpArg(name = "name", required = true) String name,
@McpArg(name = "age", required = false) Integer age,
@McpArg(name = "interests", required = false) String interests) {
StringBuilder message = new StringBuilder();
message.append("Hello, ").append(name).append("!\n\n");
if (age != null) {
message.append("At ").append(age).append(" years old, ");
// Add age-specific content
}
if (interests != null && !interests.isEmpty()) {
message.append("Your interest in ").append(interests);
// Add interest-specific content
}
return new GetPromptResult(
"Personalized Message",
List.of(new PromptMessage(Role.ASSISTANT, new TextContent(message.toString())))
);
}
@McpComplete
@McpComplete 註解為提示提供自動完成功能。
基本用法
@Component
public class CompletionProvider {
@McpComplete(prompt = "city-search")
public List<String> completeCityName(String prefix) {
return cities.stream()
.filter(city -> city.toLowerCase().startsWith(prefix.toLowerCase()))
.limit(10)
.toList();
}
}
帶 CompleteRequest.CompleteArgument
@McpComplete(prompt = "travel-planner")
public List<String> completeTravelDestination(CompleteRequest.CompleteArgument argument) {
String prefix = argument.value().toLowerCase();
String argumentName = argument.name();
// Different completions based on argument name
if ("city".equals(argumentName)) {
return completeCities(prefix);
} else if ("country".equals(argumentName)) {
return completeCountries(prefix);
}
return List.of();
}
帶 CompleteResult
@McpComplete(prompt = "code-completion")
public CompleteResult completeCode(String prefix) {
List<String> completions = generateCodeCompletions(prefix);
return new CompleteResult(
new CompleteResult.CompleteCompletion(
completions,
completions.size(), // total
hasMoreCompletions // hasMore flag
)
);
}
無狀態與有狀態實現
統一請求上下文(推薦)
使用 McpSyncRequestContext 或 McpAsyncRequestContext 作為統一介面,適用於有狀態和無狀態操作
public record UserInfo(String name, String email, int age) {}
@McpTool(name = "unified-tool", description = "Tool with unified request context")
public String unifiedTool(
McpSyncRequestContext context,
@McpToolParam(description = "Input", required = true) String input) {
// Access request and metadata
String progressToken = context.request().progressToken();
// Logging with convenient methods
context.info("Processing: " + input);
// Progress notifications (Note client should set a progress token
// with its request to be able to receive progress updates)
context.progress(50); // Simple percentage
// Ping client
context.ping();
// Check capabilities before using
if (context.elicitEnabled()) {
// Request user input (only in stateful mode)
StructuredElicitResult<UserInfo> elicitResult = context.elicit(UserInfo.class);
if (elicitResult.action() == ElicitResult.Action.ACCEPT) {
// Use elicited data
}
}
if (context.sampleEnabled()) {
// Request LLM sampling (only in stateful mode)
CreateMessageResult samplingResult = context.sample("Generate response");
// Use sampling result
}
return "Processed with unified context";
}
簡單操作(無上下文)
對於簡單操作,您可以完全省略上下文引數
@McpTool(name = "simple-add", description = "Simple addition")
public int simpleAdd(
@McpToolParam(description = "First number", required = true) int a,
@McpToolParam(description = "Second number", required = true) int b) {
return a + b;
}
輕量級無狀態(帶 McpTransportContext)
適用於需要最少傳輸上下文的無狀態操作
@McpTool(name = "stateless-tool", description = "Stateless with transport context")
public String statelessTool(
McpTransportContext context,
@McpToolParam(description = "Input", required = true) String input) {
// Access transport-level context only
// No bidirectional operations (roots, elicitation, sampling)
return "Processed: " + input;
}
| 無狀態伺服器不支援雙向操作 |
因此,在無狀態模式下使用 McpSyncRequestContext 或 McpAsyncRequestContext 的方法將被忽略。
按伺服器型別過濾方法
MCP 註解框架根據伺服器型別和方法特性自動過濾帶註解的方法。這確保了只為每個伺服器配置註冊適當的方法。每個被過濾的方法都會記錄警告,以幫助除錯。
同步與非同步過濾
同步伺服器
同步伺服器(配置為 spring.ai.mcp.server.type=SYNC)使用同步提供程式,它
-
接受 具有非反應式返回型別的方法
-
基本型別(
int,double,boolean) -
物件型別(
String,Integer, 自定義 POJO) -
MCP 型別(
CallToolResult,ReadResourceResult,GetPromptResult,CompleteResult) -
集合(
List<String>,Map<String, Object>)
-
-
過濾掉 具有反應式返回型別的方法
-
Mono<T> -
Flux<T> -
Publisher<T>
-
@Component
public class SyncTools {
@McpTool(name = "sync-tool", description = "Synchronous tool")
public String syncTool(String input) {
// This method WILL be registered on sync servers
return "Processed: " + input;
}
@McpTool(name = "async-tool", description = "Async tool")
public Mono<String> asyncTool(String input) {
// This method will be FILTERED OUT on sync servers
// A warning will be logged
return Mono.just("Processed: " + input);
}
}
非同步伺服器
非同步伺服器(配置為 spring.ai.mcp.server.type=ASYNC)使用非同步提供程式,它
-
接受 具有反應式返回型別的方法
-
Mono<T>(用於單個結果) -
Flux<T>(用於流式結果) -
Publisher<T>(通用反應式型別)
-
-
過濾掉 具有非反應式返回型別的方法
-
基本型別
-
物件型別
-
集合
-
MCP 結果型別
-
@Component
public class AsyncTools {
@McpTool(name = "async-tool", description = "Async tool")
public Mono<String> asyncTool(String input) {
// This method WILL be registered on async servers
return Mono.just("Processed: " + input);
}
@McpTool(name = "sync-tool", description = "Sync tool")
public String syncTool(String input) {
// This method will be FILTERED OUT on async servers
// A warning will be logged
return "Processed: " + input;
}
}
有狀態與無狀態過濾
有狀態伺服器
有狀態伺服器支援雙向通訊,並接受具有以下特徵的方法:
-
雙向上下文引數:
-
McpSyncRequestContext(用於同步操作) -
McpAsyncRequestContext(用於非同步操作) -
McpSyncServerExchange(舊版,用於同步操作) -
McpAsyncServerExchange(舊版,用於非同步操作)
-
-
支援雙向操作
-
roots()- 訪問根目錄 -
elicit()- 請求使用者輸入 -
sample()- 請求 LLM 取樣
-
@Component
public class StatefulTools {
@McpTool(name = "interactive-tool", description = "Tool with bidirectional operations")
public String interactiveTool(
McpSyncRequestContext context,
@McpToolParam(description = "Input", required = true) String input) {
// This method WILL be registered on stateful servers
// Can use elicitation, sampling, roots
if (context.sampleEnabled()) {
var samplingResult = context.sample("Generate response");
// Process sampling result...
}
return "Processed with context";
}
}
無狀態伺服器
無狀態伺服器針對簡單的請求-響應模式進行了最佳化,並且
-
過濾掉 具有雙向上下文引數的方法
-
使用
McpSyncRequestContext的方法將被跳過 -
使用
McpAsyncRequestContext的方法將被跳過 -
使用
McpSyncServerExchange的方法將被跳過 -
使用
McpAsyncServerExchange的方法將被跳過 -
每個被過濾的方法都會記錄警告
-
-
接受 具有以下特徵的方法:
-
McpTransportContext(輕量級無狀態上下文) -
完全沒有上下文引數
-
只有常規的
@McpToolParam引數
-
-
不支援雙向操作
-
roots()- 不可用 -
elicit()- 不可用 -
sample()- 不可用
-
@Component
public class StatelessTools {
@McpTool(name = "simple-tool", description = "Simple stateless tool")
public String simpleTool(@McpToolParam(description = "Input") String input) {
// This method WILL be registered on stateless servers
return "Processed: " + input;
}
@McpTool(name = "context-tool", description = "Tool with transport context")
public String contextTool(
McpTransportContext context,
@McpToolParam(description = "Input") String input) {
// This method WILL be registered on stateless servers
return "Processed: " + input;
}
@McpTool(name = "bidirectional-tool", description = "Tool with bidirectional context")
public String bidirectionalTool(
McpSyncRequestContext context,
@McpToolParam(description = "Input") String input) {
// This method will be FILTERED OUT on stateless servers
// A warning will be logged
return "Processed with sampling";
}
}
過濾摘要
| 伺服器型別 | 接受的方法 | 被過濾的方法 |
|---|---|---|
同步有狀態 |
非反應式返回 + 雙向上下文 |
反應式返回 (Mono/Flux) |
非同步有狀態 |
反應式返回 (Mono/Flux) + 雙向上下文 |
非反應式返回 |
同步無狀態 |
非反應式返回 + 無雙向上下文 |
反應式返回 或 雙向上下文引數 |
非同步無狀態 |
反應式返回 (Mono/Flux) + 無雙向上下文 |
非反應式返回 或 雙向上下文引數 |
| 方法過濾的最佳實踐 |
-
保持方法與伺服器型別一致 - 同步伺服器使用同步方法,非同步伺服器使用非同步方法
-
將有狀態和無狀態實現分離到不同的類中,以提高畫質晰度
-
在啟動期間檢查日誌以獲取被過濾方法警告
-
使用正確的上下文 -
McpSyncRequestContext/McpAsyncRequestContext用於有狀態,McpTransportContext用於無狀態 -
如果支援有狀態和無狀態部署,請測試兩種模式
非同步支援
所有伺服器註解都支援使用 Reactor 實現非同步
@Component
public class AsyncTools {
@McpTool(name = "async-fetch", description = "Fetch data asynchronously")
public Mono<String> asyncFetch(
@McpToolParam(description = "URL", required = true) String url) {
return Mono.fromCallable(() -> {
// Simulate async operation
return fetchFromUrl(url);
}).subscribeOn(Schedulers.boundedElastic());
}
@McpResource(uri = "async-data://{id}", name = "Async Data")
public Mono<ReadResourceResult> asyncResource(String id) {
return Mono.fromCallable(() -> {
String data = loadData(id);
return new ReadResourceResult(List.of(
new TextResourceContents("async-data://" + id, "text/plain", data)
));
}).delayElements(Duration.ofMillis(100));
}
}
Spring Boot 整合
透過 Spring Boot 自動配置,帶註解的 bean 會自動檢測並註冊
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
}
@Component
public class MyMcpTools {
// Your @McpTool annotated methods
}
@Component
public class MyMcpResources {
// Your @McpResource annotated methods
}
自動配置將
-
掃描帶有 MCP 註解的 bean
-
建立適當的規範
-
將其註冊到 MCP 伺服器
-
根據配置處理同步和非同步實現