5
5
using ModelContextProtocol . Protocol . Types ;
6
6
using ModelContextProtocol . Shared ;
7
7
using ModelContextProtocol . Utils . Json ;
8
+ using System . Diagnostics ;
9
+ using System . Reflection ;
8
10
using System . Text . Json ;
9
11
10
12
namespace ModelContextProtocol . Client ;
11
13
12
14
/// <inheritdoc/>
13
15
internal sealed class McpClient : McpEndpoint , IMcpClient
14
16
{
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
+
15
20
private readonly IClientTransport _clientTransport ;
16
21
private readonly McpClientOptions _options ;
17
22
@@ -25,43 +30,61 @@ internal sealed class McpClient : McpEndpoint, IMcpClient
25
30
/// <param name="options">Options for the client, defining protocol version and capabilities.</param>
26
31
/// <param name="serverConfig">The server configuration.</param>
27
32
/// <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 )
29
34
: base ( loggerFactory )
30
35
{
31
36
_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
+ }
32
47
_options = options ;
33
48
34
49
EndpointName = $ "Client ({ serverConfig . Id } : { serverConfig . Name } )";
35
50
36
- if ( options . Capabilities ? . Sampling is { } samplingCapability )
51
+ if ( options . Capabilities is { } capabilities )
37
52
{
38
- if ( samplingCapability . SamplingHandler is not { } samplingHandler )
53
+ if ( capabilities . NotificationHandlers is { } notificationHandlers )
39
54
{
40
- throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler." ) ;
55
+ AddNotificationHandlers ( notificationHandlers ) ;
41
56
}
42
57
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 )
56
59
{
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 ) ;
58
73
}
59
74
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
+ }
65
88
}
66
89
}
67
90
@@ -95,13 +118,14 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
95
118
try
96
119
{
97
120
// 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 (
99
123
RequestMethods . Initialize ,
100
124
new InitializeRequestParams
101
125
{
102
126
ProtocolVersion = _options . ProtocolVersion ,
103
127
Capabilities = _options . Capabilities ?? new ClientCapabilities ( ) ,
104
- ClientInfo = _options . ClientInfo
128
+ ClientInfo = _options . ClientInfo !
105
129
} ,
106
130
McpJsonUtilities . JsonContext . Default . InitializeRequestParams ,
107
131
McpJsonUtilities . JsonContext . Default . InitializeResult ,
0 commit comments