forked from modelcontextprotocol/java-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMcpAsyncServerExchange.java
More file actions
186 lines (162 loc) · 7.17 KB
/
McpAsyncServerExchange.java
File metadata and controls
186 lines (162 loc) · 7.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import com.fasterxml.jackson.core.type.TypeReference;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
import io.modelcontextprotocol.spec.McpServerSession;
import io.modelcontextprotocol.util.Assert;
import reactor.core.publisher.Mono;
/**
* Represents an asynchronous exchange with a Model Context Protocol (MCP) client. The
* exchange provides methods to interact with the client and query its capabilities.
*
* @author Dariusz Jędrzejczyk
* @author Christian Tzolov
*/
public class McpAsyncServerExchange {
private final McpServerSession session;
private final McpSchema.ClientCapabilities clientCapabilities;
private final McpSchema.Implementation clientInfo;
private volatile LoggingLevel minLoggingLevel = LoggingLevel.INFO;
private static final TypeReference<McpSchema.CreateMessageResult> CREATE_MESSAGE_RESULT_TYPE_REF = new TypeReference<>() {
};
private static final TypeReference<McpSchema.ListRootsResult> LIST_ROOTS_RESULT_TYPE_REF = new TypeReference<>() {
};
private static final TypeReference<McpSchema.ElicitResult> ELICITATION_RESULT_TYPE_REF = new TypeReference<>() {
};
/**
* Create a new asynchronous exchange with the client.
* @param session The server session representing a 1-1 interaction.
* @param clientCapabilities The client capabilities that define the supported
* features and functionality.
* @param clientInfo The client implementation information.
*/
public McpAsyncServerExchange(McpServerSession session, McpSchema.ClientCapabilities clientCapabilities,
McpSchema.Implementation clientInfo) {
this.session = session;
this.clientCapabilities = clientCapabilities;
this.clientInfo = clientInfo;
}
/**
* Get the client capabilities that define the supported features and functionality.
* @return The client capabilities
*/
public McpSchema.ClientCapabilities getClientCapabilities() {
return this.clientCapabilities;
}
/**
* Get the client implementation information.
* @return The client implementation details
*/
public McpSchema.Implementation getClientInfo() {
return this.clientInfo;
}
/**
* Create a new message using the sampling capabilities of the client. The Model
* Context Protocol (MCP) provides a standardized way for servers to request LLM
* sampling (“completions” or “generations”) from language models via clients. This
* flow allows clients to maintain control over model access, selection, and
* permissions while enabling servers to leverage AI capabilities—with no server API
* keys necessary. Servers can request text or image-based interactions and optionally
* include context from MCP servers in their prompts.
* @param createMessageRequest The request to create a new message
* @return A Mono that completes when the message has been created
* @see McpSchema.CreateMessageRequest
* @see McpSchema.CreateMessageResult
* @see <a href=
* "https://spec.modelcontextprotocol.io/specification/client/sampling/">Sampling
* Specification</a>
*/
public Mono<McpSchema.CreateMessageResult> createMessage(McpSchema.CreateMessageRequest createMessageRequest) {
if (this.clientCapabilities == null) {
return Mono.error(new McpError("Client must be initialized. Call the initialize method first!"));
}
if (this.clientCapabilities.sampling() == null) {
return Mono.error(new McpError("Client must be configured with sampling capabilities"));
}
return this.session.sendRequest(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, createMessageRequest,
CREATE_MESSAGE_RESULT_TYPE_REF);
}
/**
* Creates a new elicitation. MCP provides a standardized way for servers to request
* additional information from users through the client during interactions. This flow
* allows clients to maintain control over user interactions and data sharing while
* enabling servers to gather necessary information dynamically. Servers can request
* structured data from users with optional JSON schemas to validate responses.
* @param elicitRequest The request to create a new elicitation
* @return A Mono that completes when the elicitation has been resolved.
* @see McpSchema.ElicitRequest
* @see McpSchema.ElicitResult
* @see <a href=
* "https://spec.modelcontextprotocol.io/specification/client/elicitation/">Elicitation
* Specification</a>
*/
public Mono<McpSchema.ElicitResult> createElicitation(McpSchema.ElicitRequest elicitRequest) {
if (this.clientCapabilities == null) {
return Mono.error(new McpError("Client must be initialized. Call the initialize method first!"));
}
if (this.clientCapabilities.elicitation() == null) {
return Mono.error(new McpError("Client must be configured with elicitation capabilities"));
}
return this.session.sendRequest(McpSchema.METHOD_ELICITATION_CREATE, elicitRequest,
ELICITATION_RESULT_TYPE_REF);
}
/**
* Retrieves the list of all roots provided by the client.
* @return A Mono that emits the list of roots result.
*/
public Mono<McpSchema.ListRootsResult> listRoots() {
return this.listRoots(null);
}
/**
* Retrieves a paginated list of roots provided by the client.
* @param cursor Optional pagination cursor from a previous list request
* @return A Mono that emits the list of roots result containing
*/
public Mono<McpSchema.ListRootsResult> listRoots(String cursor) {
return this.session.sendRequest(McpSchema.METHOD_ROOTS_LIST, new McpSchema.PaginatedRequest(cursor),
LIST_ROOTS_RESULT_TYPE_REF);
}
public Mono<Void> notification(String method, Object params) {
if (method == null || method.isEmpty()) {
return Mono.error(new McpError("Method must not be null or empty"));
}
if (params == null) {
return Mono.error(new McpError("Params must not be null"));
}
return this.session.sendNotification(method, params);
}
/**
* Send a logging message notification to the client. Messages below the current
* minimum logging level will be filtered out.
* @param loggingMessageNotification The logging message to send
* @return A Mono that completes when the notification has been sent
*/
public Mono<Void> loggingNotification(LoggingMessageNotification loggingMessageNotification) {
if (loggingMessageNotification == null) {
return Mono.error(new McpError("Logging message must not be null"));
}
return Mono.defer(() -> {
if (this.isNotificationForLevelAllowed(loggingMessageNotification.level())) {
return this.session.sendNotification(McpSchema.METHOD_NOTIFICATION_MESSAGE, loggingMessageNotification);
}
return Mono.empty();
});
}
/**
* Set the minimum logging level for the client. Messages below this level will be
* filtered out.
* @param minLoggingLevel The minimum logging level
*/
void setMinLoggingLevel(LoggingLevel minLoggingLevel) {
Assert.notNull(minLoggingLevel, "minLoggingLevel must not be null");
this.minLoggingLevel = minLoggingLevel;
}
private boolean isNotificationForLevelAllowed(LoggingLevel loggingLevel) {
return loggingLevel.level() >= this.minLoggingLevel.level();
}
}