@@ -12,6 +12,12 @@ 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
@@ -29,43 +35,53 @@ internal sealed class McpClient : McpEndpoint, IMcpClient
2935 /// <param name="options">Options for the client, defining protocol version and capabilities.</param>
3036 /// <param name="serverConfig">The server configuration.</param>
3137 /// <param name="loggerFactory">The logger factory.</param>
32- public McpClient ( IClientTransport clientTransport , McpClientOptions options , McpServerConfig serverConfig , ILoggerFactory ? loggerFactory )
38+ public McpClient ( IClientTransport clientTransport , McpClientOptions ? options , McpServerConfig serverConfig , ILoggerFactory ? loggerFactory )
3339 : base ( loggerFactory )
3440 {
41+ options ??= new ( ) ;
42+
3543 _clientTransport = clientTransport ;
3644 _options = options ;
3745
3846 EndpointName = $ "Client ({ serverConfig . Id } : { serverConfig . Name } )";
3947
40- if ( options . Capabilities ? . Sampling is { } samplingCapability )
48+ if ( options . Capabilities is { } capabilities )
4149 {
42- if ( samplingCapability . SamplingHandler is not { } samplingHandler )
50+ if ( capabilities . NotificationHandlers is { } notificationHandlers )
4351 {
44- throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler." ) ;
52+ NotificationHandlers . AddRange ( notificationHandlers ) ;
4553 }
4654
47- SetRequestHandler (
48- RequestMethods . SamplingCreateMessage ,
49- ( request , cancellationToken ) => samplingHandler (
50- request ,
51- request ? . Meta ? . ProgressToken is { } token ? new TokenProgress ( this , token ) : NullProgress . Instance ,
52- cancellationToken ) ,
53- McpJsonUtilities . JsonContext . Default . CreateMessageRequestParams ,
54- McpJsonUtilities . JsonContext . Default . CreateMessageResult ) ;
55- }
56-
57- if ( options . Capabilities ? . Roots is { } rootsCapability )
58- {
59- if ( rootsCapability . RootsHandler is not { } rootsHandler )
55+ if ( capabilities . Sampling is { } samplingCapability )
6056 {
61- 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 ) ;
6270 }
6371
64- SetRequestHandler (
65- RequestMethods . RootsList ,
66- rootsHandler ,
67- McpJsonUtilities . JsonContext . Default . ListRootsRequestParams ,
68- 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+ }
6985 }
7086 }
7187
@@ -96,20 +112,20 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
96112 using var initializationCts = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
97113 initializationCts . CancelAfter ( _options . InitializationTimeout ) ;
98114
99- try
100- {
101- // Send initialize request
102- var initializeResponse = await this . SendRequestAsync (
103- RequestMethods . Initialize ,
104- new InitializeRequestParams
105- {
106- ProtocolVersion = _options . ProtocolVersion ,
107- Capabilities = _options . Capabilities ?? new ClientCapabilities ( ) ,
108- ClientInfo = _options . ClientInfo
109- } ,
110- McpJsonUtilities . JsonContext . Default . InitializeRequestParams ,
111- McpJsonUtilities . JsonContext . Default . InitializeResult ,
112- 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 ) ;
113129
114130 // Store server information
115131 _logger . ServerCapabilitiesReceived ( EndpointName ,
0 commit comments