Skip to content

Commit 4c6b742

Browse files
committed
fix(lib): ensure global Tokio runtime is entered on first call for Windows compatibility
1 parent ec43539 commit 4c6b742

File tree

1 file changed

+19
-3
lines changed

1 file changed

+19
-3
lines changed

src/lib.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ use serde_json;
1818
// Global runtime - one runtime per process that stays alive
1919
static GLOBAL_RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
2020

21+
// Global runtime enter guard - keeps runtime as current for the entire process (Windows fix)
22+
static GLOBAL_RUNTIME_GUARD: OnceLock<Mutex<Option<tokio::runtime::EnterGuard<'static>>>> = OnceLock::new();
23+
2124
// Global client instance - one client per process
2225
static GLOBAL_CLIENT: OnceLock<Mutex<Option<McpClient>>> = OnceLock::new();
2326

@@ -244,11 +247,24 @@ pub struct McpClient {
244247
server_url: Mutex<Option<String>>,
245248
}
246249

247-
/// Get or create the global Tokio runtime
250+
/// Get or create the global Tokio runtime and ensure it's entered
248251
fn get_runtime() -> &'static tokio::runtime::Runtime {
249-
GLOBAL_RUNTIME.get_or_init(|| {
252+
let runtime = GLOBAL_RUNTIME.get_or_init(|| {
250253
tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime")
251-
})
254+
});
255+
256+
// On first call, enter the runtime and keep the guard alive forever (Windows fix for nested spawns)
257+
GLOBAL_RUNTIME_GUARD.get_or_init(|| {
258+
// SAFETY: The runtime is stored in a static and lives for the entire program duration
259+
let guard = unsafe {
260+
let runtime_ptr: *const tokio::runtime::Runtime = runtime;
261+
let runtime_ref: &'static tokio::runtime::Runtime = &*runtime_ptr;
262+
runtime_ref.enter()
263+
};
264+
Mutex::new(Some(guard))
265+
});
266+
267+
runtime
252268
}
253269

254270
/// Create a new MCP client

0 commit comments

Comments
 (0)