Skip to content

refactor(client): improve validation and remove server methods #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 27, 2025

Conversation

tzolov
Copy link
Contributor

@tzolov tzolov commented Feb 18, 2025

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

Resolves #13

@tzolov tzolov force-pushed the improve-client-validation branch from 6bb7fbd to 193b7b4 Compare February 25, 2025 12:12
@tzolov tzolov marked this pull request as ready for review February 25, 2025 12:12
}

public McpSchema.JSONRPCMessage getLastSentMessage() {
try {
latch.await(200, TimeUnit.MILLISECONDS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary since you are already dealing with a Sink which provides synchronization. The issue lies elsewhere.

@@ -33,30 +34,40 @@ public class MockMcpTransport implements ClientMcpTransport, ServerMcpTransport

private final Flux<McpSchema.JSONRPCMessage> outboundView = outgoing.asFlux().cache(1);

java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Flux above provides the means to synchronize actions. Something else is at fault.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The role of the latch is to ensure that the expected number of output messages are produced in result of the (simulated) incoming message.
I've added additional public void simulateIncomingMessage(McpSchema.JSONRPCMessage message, int expectedResponseMessagesCount) signature that would allow the test to specify the expected output message (could be 0 for some tests).

// Run in a separate reactive context to avoid blocking the main subscription
Mono.fromRunnable(() -> {
McpSchema.JSONRPCRequest initRequest = transport.getLastSentMessageAsRequest();
assertThat(initRequest.method()).isEqualTo(McpSchema.METHOD_INITIALIZE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this assertion failure would be swallowed and would not fail the test. Best to set an AtomicReference on the outer context and verify assumptions in the main test thread.

McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
initRequest.id(), mockInitResult, null);
transport.simulateIncomingMessage(initResponse);
}).subscribeOn(reactor.core.scheduler.Schedulers.boundedElastic()).subscribe();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be required. I think it's a source of the synchronization issues you are facing. Let me think about the way to address this.

Comment on lines 72 to 85
// Start initialization with reactive handling
InitializeResult result = asyncMcpClient.initialize().doOnSubscribe(subscription -> {
// Run in a separate reactive context to avoid blocking the main subscription
Mono.fromRunnable(() -> {
McpSchema.JSONRPCRequest initRequest = transport.getLastSentMessageAsRequest();
assertThat(initRequest.method()).isEqualTo(McpSchema.METHOD_INITIALIZE);

// Send mock server response
McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
initRequest.id(), mockInitResult, null);
transport.simulateIncomingMessage(initResponse);
// latch.countDown();
}).subscribeOn(reactor.core.scheduler.Schedulers.boundedElastic()).subscribe();
}).block();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code a duplication of the initialization method?

@tzolov
Copy link
Contributor Author

tzolov commented Feb 26, 2025

@chemicL latest commit, address some of the comments.

Copy link
Member

@chemicL chemicL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spotted a few copy-pastes here and there and added suggestions for error messages fixes.

Most importantly, please add a FIXME note to

  • the MockMcpTransport that the latch is a temporary workaround and needs to be revisited
  • the use of boundedElastic in the test
  • swallowed assertion failures

Aside from that, the production code checks improve the situation a bit, but make it hard to act upon. In general, it would be useful to not even allow acting upon an uninitialized instance and coordinate calls properly reducing the friction in usage, let's perhaps open an issue for redesigning the API a bit.

@chemicL chemicL force-pushed the improve-client-validation branch from de6790a to 1e3e37e Compare February 26, 2025 14:36
chemicL
chemicL previously approved these changes Feb 26, 2025
Copy link
Member

@chemicL chemicL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I redesigned the internals of MockMcpTransport. The tests are now synchronous and follow a linear pattern. Also applied the suggestions for the error messages.

@tzolov tzolov added this to the 0.8.0 milestone Feb 26, 2025
@tzolov tzolov added client bug Something isn't working labels Feb 26, 2025
@tzolov tzolov force-pushed the improve-client-validation branch from 57fb3f4 to c3532da Compare February 26, 2025 17:41
tzolov and others added 7 commits February 27, 2025 09:23
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

Resolves #13

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
@tzolov tzolov force-pushed the improve-client-validation branch from c3532da to 4bdd734 Compare February 27, 2025 08:24
@tzolov tzolov merged commit 7180426 into main Feb 27, 2025
@tzolov tzolov deleted the improve-client-validation branch February 27, 2025 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working client
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve MCP Client Validation and Protocol Compliance
2 participants