refactor(client): improve validation and remove server methods (#14)
Add client initialization and capability validation checks - New isInitialized() method to check client state - Validate server capabilities before tool/resource operations - Add clear error messages for common failure cases - Remove server-side notification methods from client: sendResourcesListChanged(), promptListChangedNotification() - Improve protocol version handling - Testing improvements and new initialization tests - Redesign MockMcpTransport internals and adapt tests - Correct McpError messages - clean unused imports Resolves #13 Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com> Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com> Co-authored-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
This commit is contained in:
parent
d9c4818607
commit
718042644a
3 changed files with 184 additions and 112 deletions
|
|
@ -296,6 +296,14 @@ public class McpAsyncClient {
|
|||
return this.serverInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the client-server connection is initialized.
|
||||
* @return true if the client-server connection is initialized
|
||||
*/
|
||||
public boolean isInitialized() {
|
||||
return this.serverCapabilities != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client capabilities that define the supported features and functionality.
|
||||
* @return The client capabilities
|
||||
|
|
@ -456,6 +464,12 @@ public class McpAsyncClient {
|
|||
* (false/absent)
|
||||
*/
|
||||
public Mono<McpSchema.CallToolResult> callTool(McpSchema.CallToolRequest callToolRequest) {
|
||||
if (!this.isInitialized()) {
|
||||
return Mono.error(new McpError("Client must be initialized before calling tools"));
|
||||
}
|
||||
if (this.serverCapabilities.tools() == null) {
|
||||
return Mono.error(new McpError("Server does not provide tools capability"));
|
||||
}
|
||||
return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_CALL, callToolRequest, CALL_TOOL_RESULT_TYPE_REF);
|
||||
}
|
||||
|
||||
|
|
@ -477,6 +491,12 @@ public class McpAsyncClient {
|
|||
* Optional cursor for pagination if more tools are available
|
||||
*/
|
||||
public Mono<McpSchema.ListToolsResult> listTools(String cursor) {
|
||||
if (!this.isInitialized()) {
|
||||
return Mono.error(new McpError("Client must be initialized before listing tools"));
|
||||
}
|
||||
if (this.serverCapabilities.tools() == null) {
|
||||
return Mono.error(new McpError("Server does not provide tools capability"));
|
||||
}
|
||||
return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_LIST, new McpSchema.PaginatedRequest(cursor),
|
||||
LIST_TOOLS_RESULT_TYPE_REF);
|
||||
}
|
||||
|
|
@ -532,6 +552,12 @@ public class McpAsyncClient {
|
|||
* @return A Mono that completes with the list of resources result
|
||||
*/
|
||||
public Mono<McpSchema.ListResourcesResult> listResources(String cursor) {
|
||||
if (!this.isInitialized()) {
|
||||
return Mono.error(new McpError("Client must be initialized before listing resources"));
|
||||
}
|
||||
if (this.serverCapabilities.resources() == null) {
|
||||
return Mono.error(new McpError("Server does not provide the resources capability"));
|
||||
}
|
||||
return this.mcpSession.sendRequest(McpSchema.METHOD_RESOURCES_LIST, new McpSchema.PaginatedRequest(cursor),
|
||||
LIST_RESOURCES_RESULT_TYPE_REF);
|
||||
}
|
||||
|
|
@ -551,6 +577,12 @@ public class McpAsyncClient {
|
|||
* @return A Mono that completes with the resource content
|
||||
*/
|
||||
public Mono<McpSchema.ReadResourceResult> readResource(McpSchema.ReadResourceRequest readResourceRequest) {
|
||||
if (!this.isInitialized()) {
|
||||
return Mono.error(new McpError("Client must be initialized before reading resources"));
|
||||
}
|
||||
if (this.serverCapabilities.resources() == null) {
|
||||
return Mono.error(new McpError("Server does not provide the resources capability"));
|
||||
}
|
||||
return this.mcpSession.sendRequest(McpSchema.METHOD_RESOURCES_READ, readResourceRequest,
|
||||
READ_RESOURCE_RESULT_TYPE_REF);
|
||||
}
|
||||
|
|
@ -575,19 +607,16 @@ public class McpAsyncClient {
|
|||
* @return A Mono that completes with the list of resource templates result
|
||||
*/
|
||||
public Mono<McpSchema.ListResourceTemplatesResult> listResourceTemplates(String cursor) {
|
||||
if (!this.isInitialized()) {
|
||||
return Mono.error(new McpError("Client must be initialized before listing resource templates"));
|
||||
}
|
||||
if (this.serverCapabilities.resources() == null) {
|
||||
return Mono.error(new McpError("Server does not provide the resources capability"));
|
||||
}
|
||||
return this.mcpSession.sendRequest(McpSchema.METHOD_RESOURCES_TEMPLATES_LIST,
|
||||
new McpSchema.PaginatedRequest(cursor), LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF);
|
||||
}
|
||||
|
||||
/**
|
||||
* List Changed Notification. When the list of available resources changes, servers
|
||||
* that declared the listChanged capability SHOULD send a notification.
|
||||
* @return A Mono that completes when the notification is sent
|
||||
*/
|
||||
public Mono<Void> sendResourcesListChanged() {
|
||||
return this.mcpSession.sendNotification(McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriptions. The protocol supports optional subscriptions to resource changes.
|
||||
* Clients can subscribe to specific resources and receive notifications when they
|
||||
|
|
@ -660,16 +689,6 @@ public class McpAsyncClient {
|
|||
return this.mcpSession.sendRequest(McpSchema.METHOD_PROMPT_GET, getPromptRequest, GET_PROMPT_RESULT_TYPE_REF);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Server) An optional notification from the server to the client, informing it that
|
||||
* the list of prompts it offers has changed. This may be issued by servers without
|
||||
* any previous subscription from the client.
|
||||
* @return A Mono that completes when the notification is sent
|
||||
*/
|
||||
public Mono<Void> promptListChangedNotification() {
|
||||
return this.mcpSession.sendNotification(McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED);
|
||||
}
|
||||
|
||||
private NotificationHandler asyncPromptsChangeNotificationHandler(
|
||||
List<Function<List<McpSchema.Prompt>, Mono<Void>>> promptsChangeConsumers) {
|
||||
return params -> listPrompts().flatMap(listPromptsResult -> Flux.fromIterable(promptsChangeConsumers)
|
||||
|
|
|
|||
|
|
@ -284,14 +284,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
return this.delegate.listResourceTemplates().block();
|
||||
}
|
||||
|
||||
/**
|
||||
* List Changed Notification. When the list of available resources changes, servers
|
||||
* that declared the listChanged capability SHOULD send a notification:
|
||||
*/
|
||||
public void sendResourcesListChanged() {
|
||||
this.delegate.sendResourcesListChanged().block();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriptions. The protocol supports optional subscriptions to resource changes.
|
||||
* Clients can subscribe to specific resources and receive notifications when they
|
||||
|
|
@ -329,15 +321,6 @@ public class McpSyncClient implements AutoCloseable {
|
|||
return this.delegate.getPrompt(getPromptRequest).block();
|
||||
}
|
||||
|
||||
/**
|
||||
* (Server) An optional notification from the server to the client, informing it that
|
||||
* the list of prompts it offers has changed. This may be issued by servers without
|
||||
* any previous subscription from the client.
|
||||
*/
|
||||
public void promptListChangedNotification() {
|
||||
this.delegate.promptListChangedNotification().block();
|
||||
}
|
||||
|
||||
/**
|
||||
* Client can set the minimum logging level it wants to receive from the server.
|
||||
* @param loggingLevel the min logging level
|
||||
|
|
|
|||
|
|
@ -188,9 +188,13 @@ public class McpAsyncServer {
|
|||
initializeRequest.protocolVersion(), initializeRequest.capabilities(),
|
||||
initializeRequest.clientInfo());
|
||||
|
||||
// The server MUST respond with the highest protocol version it supports if
|
||||
// it does not support the requested (e.g. Client) version.
|
||||
String serverProtocolVersion = this.protocolVersions.get(this.protocolVersions.size() - 1);
|
||||
|
||||
if (this.protocolVersions.contains(initializeRequest.protocolVersion())) {
|
||||
// If the server supports the requested protocol version, it MUST respond
|
||||
// with the same version.
|
||||
serverProtocolVersion = initializeRequest.protocolVersion();
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue