@@ -17,10 +17,14 @@ use std::sync::atomic::AtomicI64;
17
17
use std:: sync:: atomic:: Ordering ;
18
18
use std:: time:: Duration ;
19
19
20
+ use anyhow:: Context ;
20
21
use anyhow:: Result ;
21
22
use anyhow:: anyhow;
22
23
use mcp_types:: CallToolRequest ;
23
24
use mcp_types:: CallToolRequestParams ;
25
+ use mcp_types:: InitializeRequest ;
26
+ use mcp_types:: InitializeRequestParams ;
27
+ use mcp_types:: InitializedNotification ;
24
28
use mcp_types:: JSONRPC_VERSION ;
25
29
use mcp_types:: JSONRPCMessage ;
26
30
use mcp_types:: JSONRPCNotification ;
@@ -29,6 +33,7 @@ use mcp_types::JSONRPCResponse;
29
33
use mcp_types:: ListToolsRequest ;
30
34
use mcp_types:: ListToolsRequestParams ;
31
35
use mcp_types:: ListToolsResult ;
36
+ use mcp_types:: ModelContextProtocolNotification ;
32
37
use mcp_types:: ModelContextProtocolRequest ;
33
38
use mcp_types:: RequestId ;
34
39
use serde:: Serialize ;
@@ -74,6 +79,8 @@ pub struct McpClient {
74
79
75
80
impl McpClient {
76
81
/// Spawn the given command and establish an MCP session over its STDIO.
82
+ /// Caller is responsible for sending the `initialize` request. See
83
+ /// [`initialize`](Self::initialize) for details.
77
84
pub async fn new_stdio_client (
78
85
program : String ,
79
86
args : Vec < String > ,
@@ -273,6 +280,52 @@ impl McpClient {
273
280
}
274
281
}
275
282
283
+ pub async fn send_notification < N > ( & self , params : N :: Params ) -> Result < ( ) >
284
+ where
285
+ N : ModelContextProtocolNotification ,
286
+ N :: Params : Serialize ,
287
+ {
288
+ // Serialize params -> JSON. For many request types `Params` is
289
+ // `Option<T>` and `None` should be encoded as *absence* of the field.
290
+ let params_json = serde_json:: to_value ( & params) ?;
291
+ let params_field = if params_json. is_null ( ) {
292
+ None
293
+ } else {
294
+ Some ( params_json)
295
+ } ;
296
+
297
+ let method = N :: METHOD . to_string ( ) ;
298
+ let jsonrpc_notification = JSONRPCNotification {
299
+ jsonrpc : JSONRPC_VERSION . to_string ( ) ,
300
+ method : method. clone ( ) ,
301
+ params : params_field,
302
+ } ;
303
+
304
+ let notification = JSONRPCMessage :: Notification ( jsonrpc_notification) ;
305
+ self . outgoing_tx
306
+ . send ( notification)
307
+ . await
308
+ . with_context ( || format ! ( "failed to send notification `{method}` to writer task" ) )
309
+ }
310
+
311
+ /// Negotiates the initialization with the MCP server. Sends an `initialize`
312
+ /// request with the specified `initialize_params` and then the
313
+ /// `notifications/initialized` notification once the response has been
314
+ /// received. Returns the response to the `initialize` request.
315
+ pub async fn initialize (
316
+ & self ,
317
+ initialize_params : InitializeRequestParams ,
318
+ initialize_notification_params : Option < serde_json:: Value > ,
319
+ timeout : Option < Duration > ,
320
+ ) -> Result < mcp_types:: InitializeResult > {
321
+ let response = self
322
+ . send_request :: < InitializeRequest > ( initialize_params, timeout)
323
+ . await ?;
324
+ self . send_notification :: < InitializedNotification > ( initialize_notification_params)
325
+ . await ?;
326
+ Ok ( response)
327
+ }
328
+
276
329
/// Convenience wrapper around `tools/list`.
277
330
pub async fn list_tools (
278
331
& self ,
0 commit comments