@@ -14,14 +14,25 @@ use rmcp::transport::{SseClientTransport, StreamableHttpClientTransport};
1414use rmcp:: { ServiceExt , RoleClient } ;
1515use rmcp:: model:: { ClientInfo , ClientCapabilities , Implementation } ;
1616
17- // Global client instance (simplified approach - one client per process)
17+ // Global runtime instance - single runtime for entire process
18+ static GLOBAL_RUNTIME : OnceLock < tokio:: runtime:: Runtime > = OnceLock :: new ( ) ;
19+
20+ // Global client instance
1821static GLOBAL_CLIENT : OnceLock < Mutex < Option < McpClient > > > = OnceLock :: new ( ) ;
1922
23+ /// Get or create the global Tokio runtime
24+ fn get_runtime ( ) -> & ' static tokio:: runtime:: Runtime {
25+ GLOBAL_RUNTIME . get_or_init ( || {
26+ tokio:: runtime:: Runtime :: new ( ) . expect ( "Failed to create Tokio runtime" )
27+ } )
28+ }
29+
2030/// Initialize the MCP library
2131/// Returns 0 on success, non-zero on error
2232#[ no_mangle]
2333pub extern "C" fn mcp_init ( ) -> i32 {
24- // Initialize any global state here
34+ // Initialize the runtime early
35+ let _ = get_runtime ( ) ;
2536 0
2637}
2738
@@ -51,7 +62,6 @@ type RunningClient = rmcp::service::RunningService<RoleClient, ClientInfo>;
5162
5263/// Opaque handle for MCP client
5364pub struct McpClient {
54- runtime : tokio:: runtime:: Runtime ,
5565 service : Mutex < Option < RunningClient > > ,
5666 server_url : Mutex < Option < String > > ,
5767}
@@ -60,17 +70,14 @@ pub struct McpClient {
6070/// Returns NULL on error
6171#[ no_mangle]
6272pub extern "C" fn mcp_client_new ( ) -> * mut McpClient {
63- match tokio:: runtime:: Runtime :: new ( ) {
64- Ok ( runtime) => {
65- let client = Box :: new ( McpClient {
66- runtime,
67- service : Mutex :: new ( None ) ,
68- server_url : Mutex :: new ( None ) ,
69- } ) ;
70- Box :: into_raw ( client)
71- }
72- Err ( _) => ptr:: null_mut ( ) ,
73- }
73+ // Initialize runtime early
74+ let _ = get_runtime ( ) ;
75+
76+ let client = Box :: new ( McpClient {
77+ service : Mutex :: new ( None ) ,
78+ server_url : Mutex :: new ( None ) ,
79+ } ) ;
80+ Box :: into_raw ( client)
7481}
7582
7683/// Free an MCP client
@@ -137,24 +144,18 @@ pub extern "C" fn mcp_connect(
137144 }
138145 } ;
139146
140- // Create a new McpClient with runtime
147+ // Create a new McpClient
141148 let new_client = McpClient {
142- runtime : match tokio:: runtime:: Runtime :: new ( ) {
143- Ok ( r) => r,
144- Err ( e) => {
145- let error = format ! ( r#"{{"error": "Failed to create runtime: {}"}}"# , e) ;
146- return CString :: new ( error) . unwrap_or_default ( ) . into_raw ( ) ;
147- }
148- } ,
149149 service : Mutex :: new ( None ) ,
150150 server_url : Mutex :: new ( None ) ,
151151 } ;
152152
153153 let use_sse = legacy_sse != 0 ;
154+ let runtime = get_runtime ( ) ;
154155
155156 let ( result, maybe_service) = if use_sse {
156157 // Use SSE transport (legacy) with optional custom headers
157- new_client . runtime . block_on ( async {
158+ runtime. block_on ( async {
158159 // Create HTTP client with optional custom headers
159160 let mut client_builder = reqwest:: Client :: builder ( ) ;
160161 if let Some ( ref headers_map) = headers_map {
@@ -240,7 +241,7 @@ pub extern "C" fn mcp_connect(
240241 } )
241242 } else {
242243 // Use streamable HTTP transport (default) with optional custom headers
243- new_client . runtime . block_on ( async {
244+ runtime. block_on ( async {
244245 // For Streamable HTTP, we need to extract the Authorization header specifically
245246 // since it has a dedicated field, and we'll use a custom HTTP client for other headers
246247 let auth_header_value = headers_map. as_ref ( ) . and_then ( |m| m. get ( "Authorization" ) ) . map ( |s| s. clone ( ) ) ;
@@ -368,7 +369,8 @@ pub extern "C" fn mcp_list_tools(_client_ptr: *mut McpClient) -> *mut c_char {
368369 }
369370 } ;
370371
371- let result = client. runtime . block_on ( async {
372+ let runtime = get_runtime ( ) ;
373+ let result = runtime. block_on ( async {
372374 let service_guard = client. service . lock ( ) . unwrap ( ) ;
373375 let service = match service_guard. as_ref ( ) {
374376 Some ( s) => s,
@@ -462,7 +464,8 @@ pub extern "C" fn mcp_call_tool(
462464 }
463465 } ;
464466
465- let result = client. runtime . block_on ( async {
467+ let runtime = get_runtime ( ) ;
468+ let result = runtime. block_on ( async {
466469 let service_guard = client. service . lock ( ) . unwrap ( ) ;
467470 let service = match service_guard. as_ref ( ) {
468471 Some ( s) => s,
0 commit comments