55using ModelContextProtocol . Protocol . Types ;
66using ModelContextProtocol . Shared ;
77using ModelContextProtocol . Utils . Json ;
8+ using System . Diagnostics ;
9+ using System . Reflection ;
810using System . Text . Json ;
911
1012namespace ModelContextProtocol . Client ;
1113
1214/// <inheritdoc/>
1315internal sealed class McpClient : McpEndpoint , IMcpClient
1416{
17+ /// <summary>Cached naming information used for client name/version when none is specified.</summary>
18+ private static readonly AssemblyName s_asmName = ( Assembly . GetEntryAssembly ( ) ?? Assembly . GetExecutingAssembly ( ) ) . GetName ( ) ;
19+
1520 private readonly IClientTransport _clientTransport ;
1621 private readonly McpClientOptions _options ;
1722
@@ -25,43 +30,61 @@ internal sealed class McpClient : McpEndpoint, IMcpClient
2530 /// <param name="options">Options for the client, defining protocol version and capabilities.</param>
2631 /// <param name="serverConfig">The server configuration.</param>
2732 /// <param name="loggerFactory">The logger factory.</param>
28- public McpClient ( IClientTransport clientTransport , McpClientOptions options , McpServerConfig serverConfig , ILoggerFactory ? loggerFactory )
33+ public McpClient ( IClientTransport clientTransport , McpClientOptions ? options , McpServerConfig serverConfig , ILoggerFactory ? loggerFactory )
2934 : base ( loggerFactory )
3035 {
3136 _clientTransport = clientTransport ;
37+
38+ if ( options ? . ClientInfo is null )
39+ {
40+ options = options ? . Clone ( ) ?? new ( ) ;
41+ options . ClientInfo = new ( )
42+ {
43+ Name = s_asmName . Name ?? nameof ( McpClient ) ,
44+ Version = s_asmName . Version ? . ToString ( ) ?? "1.0.0" ,
45+ } ;
46+ }
3247 _options = options ;
3348
3449 EndpointName = $ "Client ({ serverConfig . Id } : { serverConfig . Name } )";
3550
36- if ( options . Capabilities ? . Sampling is { } samplingCapability )
51+ if ( options . Capabilities is { } capabilities )
3752 {
38- if ( samplingCapability . SamplingHandler is not { } samplingHandler )
53+ if ( capabilities . NotificationHandlers is { } notificationHandlers )
3954 {
40- throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler." ) ;
55+ AddNotificationHandlers ( notificationHandlers ) ;
4156 }
4257
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 )
58+ if ( capabilities . Sampling is { } samplingCapability )
5659 {
57- throw new InvalidOperationException ( $ "Roots capability was set but it did not provide a handler.") ;
60+ if ( samplingCapability . SamplingHandler is not { } samplingHandler )
61+ {
62+ throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler.") ;
63+ }
64+
65+ SetRequestHandler (
66+ RequestMethods . SamplingCreateMessage ,
67+ ( request , cancellationToken ) => samplingHandler (
68+ request ,
69+ request ? . Meta ? . ProgressToken is { } token ? new TokenProgress ( this , token ) : NullProgress . Instance ,
70+ cancellationToken ) ,
71+ McpJsonUtilities . JsonContext . Default . CreateMessageRequestParams ,
72+ McpJsonUtilities . JsonContext . Default . CreateMessageResult ) ;
5873 }
5974
60- SetRequestHandler (
61- RequestMethods . RootsList ,
62- rootsHandler ,
63- McpJsonUtilities . JsonContext . Default . ListRootsRequestParams ,
64- McpJsonUtilities . JsonContext . Default . ListRootsResult ) ;
75+ if ( capabilities . Roots is { } rootsCapability )
76+ {
77+ if ( rootsCapability . RootsHandler is not { } rootsHandler )
78+ {
79+ throw new InvalidOperationException ( $ "Roots capability was set but it did not provide a handler.") ;
80+ }
81+
82+ SetRequestHandler (
83+ RequestMethods . RootsList ,
84+ rootsHandler ,
85+ McpJsonUtilities . JsonContext . Default . ListRootsRequestParams ,
86+ McpJsonUtilities . JsonContext . Default . ListRootsResult ) ;
87+ }
6588 }
6689 }
6790
@@ -95,13 +118,14 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
95118 try
96119 {
97120 // Send initialize request
98- var initializeResponse = await this . SendRequestAsync (
121+ Debug . Assert ( _options . ClientInfo is not null , "ClientInfo should be set by the constructor" ) ;
122+ var initializeResponse = await this . SendRequestAsync (
99123 RequestMethods . Initialize ,
100124 new InitializeRequestParams
101125 {
102126 ProtocolVersion = _options . ProtocolVersion ,
103127 Capabilities = _options . Capabilities ?? new ClientCapabilities ( ) ,
104- ClientInfo = _options . ClientInfo
128+ ClientInfo = _options . ClientInfo !
105129 } ,
106130 McpJsonUtilities . JsonContext . Default . InitializeRequestParams ,
107131 McpJsonUtilities . JsonContext . Default . InitializeResult ,
0 commit comments