@@ -12,67 +12,87 @@ namespace ModelContextProtocol.Client;
12
12
/// <inheritdoc/>
13
13
internal sealed class McpClient : McpEndpoint , IMcpClient
14
14
{
15
+ private static Implementation DefaultImplementation { get ; } = new ( )
16
+ {
17
+ Name = DefaultAssemblyName . Name ?? nameof ( McpClient ) ,
18
+ Version = DefaultAssemblyName . Version ? . ToString ( ) ?? "1.0.0" ,
19
+ } ;
20
+
15
21
private readonly IClientTransport _clientTransport ;
16
22
private readonly McpClientOptions _options ;
17
23
18
24
private ITransport ? _sessionTransport ;
19
25
private CancellationTokenSource ? _connectCts ;
20
26
27
+ private ServerCapabilities ? _serverCapabilities ;
28
+ private Implementation ? _serverInfo ;
29
+ private string ? _serverInstructions ;
30
+
21
31
/// <summary>
22
32
/// Initializes a new instance of the <see cref="McpClient"/> class.
23
33
/// </summary>
24
34
/// <param name="clientTransport">The transport to use for communication with the server.</param>
25
35
/// <param name="options">Options for the client, defining protocol version and capabilities.</param>
26
36
/// <param name="serverConfig">The server configuration.</param>
27
37
/// <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 )
29
39
: base ( loggerFactory )
30
40
{
41
+ options ??= new ( ) ;
42
+
31
43
_clientTransport = clientTransport ;
32
44
_options = options ;
33
45
34
46
EndpointName = $ "Client ({ serverConfig . Id } : { serverConfig . Name } )";
35
47
36
- if ( options . Capabilities ? . Sampling is { } samplingCapability )
48
+ if ( options . Capabilities is { } capabilities )
37
49
{
38
- if ( samplingCapability . SamplingHandler is not { } samplingHandler )
50
+ if ( capabilities . NotificationHandlers is { } notificationHandlers )
39
51
{
40
- throw new InvalidOperationException ( $ "Sampling capability was set but it did not provide a handler." ) ;
52
+ NotificationHandlers . AddRange ( notificationHandlers ) ;
41
53
}
42
54
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 )
56
56
{
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 ) ;
58
70
}
59
71
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
+ }
65
85
}
66
86
}
67
87
68
88
/// <inheritdoc/>
69
- public ServerCapabilities ? ServerCapabilities { get ; private set ; }
89
+ public ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException ( "The client is not connected." ) ;
70
90
71
91
/// <inheritdoc/>
72
- public Implementation ? ServerInfo { get ; private set ; }
92
+ public Implementation ServerInfo => _serverInfo ?? throw new InvalidOperationException ( "The client is not connected." ) ;
73
93
74
94
/// <inheritdoc/>
75
- public string ? ServerInstructions { get ; private set ; }
95
+ public string ? ServerInstructions => _serverInstructions ;
76
96
77
97
/// <inheritdoc/>
78
98
public override string EndpointName { get ; }
@@ -92,46 +112,46 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
92
112
using var initializationCts = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
93
113
initializationCts . CancelAfter ( _options . InitializationTimeout ) ;
94
114
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 ) ;
109
129
110
130
// Store server information
111
131
_logger . ServerCapabilitiesReceived ( EndpointName ,
112
132
capabilities : JsonSerializer . Serialize ( initializeResponse . Capabilities , McpJsonUtilities . JsonContext . Default . ServerCapabilities ) ,
113
133
serverInfo : JsonSerializer . Serialize ( initializeResponse . ServerInfo , McpJsonUtilities . JsonContext . Default . Implementation ) ) ;
114
134
115
- ServerCapabilities = initializeResponse . Capabilities ;
116
- ServerInfo = initializeResponse . ServerInfo ;
117
- ServerInstructions = initializeResponse . Instructions ;
135
+ _serverCapabilities = initializeResponse . Capabilities ;
136
+ _serverInfo = initializeResponse . ServerInfo ;
137
+ _serverInstructions = initializeResponse . Instructions ;
118
138
119
139
// Validate protocol version
120
140
if ( initializeResponse . ProtocolVersion != _options . ProtocolVersion )
121
141
{
122
142
_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 } ") ;
124
144
}
125
145
126
146
// Send initialized notification
127
147
await SendMessageAsync (
128
148
new JsonRpcNotification { Method = NotificationMethods . InitializedNotification } ,
129
149
initializationCts . Token ) . ConfigureAwait ( false ) ;
130
150
}
131
- catch ( OperationCanceledException ) when ( initializationCts . IsCancellationRequested )
151
+ catch ( OperationCanceledException oce ) when ( initializationCts . IsCancellationRequested )
132
152
{
133
153
_logger . ClientInitializationTimeout ( EndpointName ) ;
134
- throw new McpClientException ( "Initialization timed out" ) ;
154
+ throw new McpException ( "Initialization timed out" , oce ) ;
135
155
}
136
156
}
137
157
catch ( Exception e )
0 commit comments