-
Notifications
You must be signed in to change notification settings - Fork 530
refactor: extract MCP client initialization logic into LifecyleInitializer #370
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
base: main
Are you sure you want to change the base?
Conversation
…lizer - Create new LifecyleInitializer class to handle protocol initialization phase - Move initialization logic from McpAsyncClient to dedicated initializer - Add javadocs for MCP initialization process - Implement protocol version negotiation and capability exchange - Add exception handling for transport session recovery - Include test suite for LifecyleInitializer - Simplify McpAsyncClient by delegating initialization responsibilities This refactoring improves separation of concerns and makes the initialization process more maintainable and testable. Signed-off-by: Christian Tzolov <[email protected]>
85545f4
to
ef140fc
Compare
* the initialized notification</li> | ||
* </ul> | ||
*/ | ||
public class LifecyleInitializer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public class LifecyleInitializer { | |
class LifecyleInitializer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not make it public
* clients and servers. | ||
*/ | ||
private final Function<ContextView, McpClientSession> sessionSupplier; | ||
private LifecyleInitializer initializer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private LifecyleInitializer initializer; | |
private final LifecyleInitializer initializer; |
* @param operation The operation to execute when the client is initialized | ||
* @return A Mono that completes with the result of the operation | ||
*/ | ||
public <T> Mono<T> withIntitialization(String actionName, Function<Initialization, Mono<T>> operation) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public <T> Mono<T> withIntitialization(String actionName, Function<Initialization, Mono<T>> operation) { | |
public <T> Mono<T> ensureInitialized(String actionName, Function<Initialization, Mono<T>> operation) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think ensureInitialized reflects the functionality It performs. The withIntitialization
implies some additional functionality is applied. The ensure
implies a validation check.
} | ||
|
||
@Test | ||
void shouldTimeoutOnSlowInitialization() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long does this test take? Can we use VirtualTimeScheduler for it?
return mockSession; | ||
}); | ||
|
||
// Start multiple concurrent initializations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intend to make them parallel? You could do that with subscribeOn and using a parallel Scheduler
// re-initialization | ||
// We need to wait a bit for the async re-initialization to start | ||
try { | ||
Thread.sleep(10); // Small delay to allow async processing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every time we use sleep we almost certainly introduce flakiness into the test suite, please let's avoid it.
Refactor MCP client initialization logic by extracting it from
McpAsyncClient
into a dedicatedLifecyleInitializer
class.This improves separation of concerns, maintainability, and testability of the initialization process.
Motivation and Context
The
McpAsyncClient
class was becoming too complex with initialization logic mixed in with client operations.This refactoring addresses several issues:
McpAsyncClient
interface is now cleaner and focused on its primary responsibilitiesHow Has This Been Tested?
LifecyleInitializerTests.java
covering:McpAsyncClient
tests continue to pass, ensuring backward compatibilityBreaking Changes
No breaking changes - This is an internal refactoring that maintains full API compatibility. All public methods of
McpAsyncClient
remain unchanged and behave identically.Types of changes
Note: While marked as potentially breaking, this is actually a non-breaking internal refactoring.
Checklist
Additional context
Implementation Notes:
LifecyleInitializer
follows the MCP specification for initialization phase requirementsMcpTransportSessionNotFoundException
with automatic re-initializationFiles Changed:
LifecyleInitializer.java
(348 lines) - Core initialization logicLifecyleInitializerTests.java
(403 lines) - Comprehensive test suiteMcpAsyncClient.java
- Simplified by delegating toLifecyleInitializer