@@ -12,67 +12,87 @@ namespace ModelContextProtocol.Client;
1212/// <inheritdoc/>
1313internal sealed class McpClient : McpEndpoint , IMcpClient
1414{
15+ private static Implementation DefaultImplementation { get ; } = new ( )
16+ {
17+ Name = DefaultAssemblyName . Name ?? nameof ( McpClient ) ,
18+ Version = DefaultAssemblyName . Version ? . ToString ( ) ?? "1.0.0" ,
19+ } ;
20+
1521 private readonly IClientTransport _clientTransport ;
1622 private readonly McpClientOptions _options ;
1723
1824 private ITransport ? _sessionTransport ;
1925 private CancellationTokenSource ? _connectCts ;
2026
27+ private ServerCapabilities ? _serverCapabilities ;
28+ private Implementation ? _serverInfo ;
29+ private string ? _serverInstructions ;
30+
2131 /// <summary>
2232 /// Initializes a new instance of the <see cref="McpClient"/> class.
2333 /// </summary>
2434 /// <param name="clientTransport">The transport to use for communication with the server.</param>
2535 /// <param name="options">Options for the client, defining protocol version and capabilities.</param>
2636 /// <param name="serverConfig">The server configuration.</param>
2737 /// <param name="loggerFactory">The logger factory.</param>
28- public McpClient ( IClientTransport clientTransport , McpClientOptions options , McpServerConfig serverConfig , ILoggerFactory ? loggerFactory )
38+ public McpClient ( IClientTransport clientTransport , McpClientOptions ? options , McpServerConfig serverConfig , ILoggerFactory ? loggerFactory )
2939 : base ( loggerFactory )
3040 {
41+ options ??= new ( ) ;
42+
3143 _clientTransport = clientTransport ;
3244 _options = options ;
3345
3446 EndpointName = $ "Client ({ serverConfig . Id } : { serverConfig . Name } )";
3547
36- if ( options . Capabilities ? . Sampling is { } samplingCapability )
48+ if ( options . Capabilities is { } capabilities )
3749 {
38- if ( samplingCapability . SamplingHandler is not { } samplingHandler )
50+ if ( capabilities . NotificationHandlers is { } notificationHandlers )
3951 {
40- throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler." ) ;
52+ NotificationHandlers . AddRange ( notificationHandlers ) ;
4153 }
4254
43- SetRequestHandler (
44- RequestMethods . SamplingCreateMessage ,
45- ( request , cancellationToken ) => samplingHandler (
46- request ,
47- request ? . Meta ? . ProgressToken is { } token ? new TokenProgress ( this , token ) : NullProgress . Instance ,
48- cancellationToken ) ,
49- McpJsonUtilities . JsonContext . Default . CreateMessageRequestParams ,
50- McpJsonUtilities . JsonContext . Default . CreateMessageResult ) ;
51- }
52-
53- if ( options . Capabilities ? . Roots is { } rootsCapability )
54- {
55- if ( rootsCapability . RootsHandler is not { } rootsHandler )
55+ if ( capabilities . Sampling is { } samplingCapability )
5656 {
57- throw new InvalidOperationException ( $ "Roots capability was set but it did not provide a handler.") ;
57+ if ( samplingCapability . SamplingHandler is not { } samplingHandler )
58+ {
59+ throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler.") ;
60+ }
61+
62+ RequestHandlers . Set (
63+ RequestMethods . SamplingCreateMessage ,
64+ ( request , cancellationToken ) => samplingHandler (
65+ request ,
66+ request ? . Meta ? . ProgressToken is { } token ? new TokenProgress ( this , token ) : NullProgress . Instance ,
67+ cancellationToken ) ,
68+ McpJsonUtilities . JsonContext . Default . CreateMessageRequestParams ,
69+ McpJsonUtilities . JsonContext . Default . CreateMessageResult ) ;
5870 }
5971
60- SetRequestHandler (
61- RequestMethods . RootsList ,
62- rootsHandler ,
63- McpJsonUtilities . JsonContext . Default . ListRootsRequestParams ,
64- McpJsonUtilities . JsonContext . Default . ListRootsResult ) ;
72+ if ( capabilities . Roots is { } rootsCapability )
73+ {
74+ if ( rootsCapability . RootsHandler is not { } rootsHandler )
75+ {
76+ throw new InvalidOperationException ( $ "Roots capability was set but it did not provide a handler.") ;
77+ }
78+
79+ RequestHandlers . Set (
80+ RequestMethods . RootsList ,
81+ rootsHandler ,
82+ McpJsonUtilities . JsonContext . Default . ListRootsRequestParams ,
83+ McpJsonUtilities . JsonContext . Default . ListRootsResult ) ;
84+ }
6585 }
6686 }
6787
6888 /// <inheritdoc/>
69- public ServerCapabilities ? ServerCapabilities { get ; private set ; }
89+ public ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException ( "The client is not connected." ) ;
7090
7191 /// <inheritdoc/>
72- public Implementation ? ServerInfo { get ; private set ; }
92+ public Implementation ServerInfo => _serverInfo ?? throw new InvalidOperationException ( "The client is not connected." ) ;
7393
7494 /// <inheritdoc/>
75- public string ? ServerInstructions { get ; private set ; }
95+ public string ? ServerInstructions => _serverInstructions ;
7696
7797 /// <inheritdoc/>
7898 public override string EndpointName { get ; }
@@ -92,46 +112,46 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
92112 using var initializationCts = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
93113 initializationCts . CancelAfter ( _options . InitializationTimeout ) ;
94114
95- try
96- {
97- // Send initialize request
98- var initializeResponse = await this . SendRequestAsync (
99- RequestMethods . Initialize ,
100- new InitializeRequestParams
101- {
102- ProtocolVersion = _options . ProtocolVersion ,
103- Capabilities = _options . Capabilities ?? new ClientCapabilities ( ) ,
104- ClientInfo = _options . ClientInfo
105- } ,
106- McpJsonUtilities . JsonContext . Default . InitializeRequestParams ,
107- McpJsonUtilities . JsonContext . Default . InitializeResult ,
108- cancellationToken : initializationCts . Token ) . ConfigureAwait ( false ) ;
115+ try
116+ {
117+ // Send initialize request
118+ var initializeResponse = await this . SendRequestAsync (
119+ RequestMethods . Initialize ,
120+ new InitializeRequestParams
121+ {
122+ ProtocolVersion = _options . ProtocolVersion ,
123+ Capabilities = _options . Capabilities ?? new ClientCapabilities ( ) ,
124+ ClientInfo = _options . ClientInfo ?? DefaultImplementation ,
125+ } ,
126+ McpJsonUtilities . JsonContext . Default . InitializeRequestParams ,
127+ McpJsonUtilities . JsonContext . Default . InitializeResult ,
128+ cancellationToken : initializationCts . Token ) . ConfigureAwait ( false ) ;
109129
110130 // Store server information
111131 _logger . ServerCapabilitiesReceived ( EndpointName ,
112132 capabilities : JsonSerializer . Serialize ( initializeResponse . Capabilities , McpJsonUtilities . JsonContext . Default . ServerCapabilities ) ,
113133 serverInfo : JsonSerializer . Serialize ( initializeResponse . ServerInfo , McpJsonUtilities . JsonContext . Default . Implementation ) ) ;
114134
115- ServerCapabilities = initializeResponse . Capabilities ;
116- ServerInfo = initializeResponse . ServerInfo ;
117- ServerInstructions = initializeResponse . Instructions ;
135+ _serverCapabilities = initializeResponse . Capabilities ;
136+ _serverInfo = initializeResponse . ServerInfo ;
137+ _serverInstructions = initializeResponse . Instructions ;
118138
119139 // Validate protocol version
120140 if ( initializeResponse . ProtocolVersion != _options . ProtocolVersion )
121141 {
122142 _logger . ServerProtocolVersionMismatch ( EndpointName , _options . ProtocolVersion , initializeResponse . ProtocolVersion ) ;
123- throw new McpClientException ( $ "Server protocol version mismatch. Expected { _options . ProtocolVersion } , got { initializeResponse . ProtocolVersion } ") ;
143+ throw new McpException ( $ "Server protocol version mismatch. Expected { _options . ProtocolVersion } , got { initializeResponse . ProtocolVersion } ") ;
124144 }
125145
126146 // Send initialized notification
127147 await SendMessageAsync (
128148 new JsonRpcNotification { Method = NotificationMethods . InitializedNotification } ,
129149 initializationCts . Token ) . ConfigureAwait ( false ) ;
130150 }
131- catch ( OperationCanceledException ) when ( initializationCts . IsCancellationRequested )
151+ catch ( OperationCanceledException oce ) when ( initializationCts . IsCancellationRequested )
132152 {
133153 _logger . ClientInitializationTimeout ( EndpointName ) ;
134- throw new McpClientException ( "Initialization timed out" ) ;
154+ throw new McpException ( "Initialization timed out" , oce ) ;
135155 }
136156 }
137157 catch ( Exception e )
0 commit comments