diff --git a/src/mcp_stdio_client.cpp b/src/mcp_stdio_client.cpp index 11e8ee6..8d89a90 100644 --- a/src/mcp_stdio_client.cpp +++ b/src/mcp_stdio_client.cpp @@ -9,7 +9,7 @@ #include "mcp_stdio_client.h" -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) #include #include #else @@ -218,7 +218,7 @@ bool stdio_client::start_server_process() { throw std::runtime_error("Unsupported type"); }; -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) // Windows implementation SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); @@ -272,32 +272,33 @@ bool stdio_client::start_server_process() { ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - // Prepare environment variables - std::string env_block; + // Add custom environment variables if (!env_vars_.empty()) { for (const auto& [key, value] : env_vars_.items()) { - std::string env_var = key + "=" + convert_to_string(value); - env_block += env_var + '\0'; + std::string env_var_value = convert_to_string(value); + SetEnvironmentVariableA(key.c_str(), env_var_value.c_str()); } - env_block += '\0'; } + + std::string cmd_line = "cmd.exe /c " + command_; + + char* cmd_line_ptr = _strdup(cmd_line.c_str()); // Create child process - std::string cmd_line = command_; - char* cmd_str = const_cast(cmd_line.c_str()); - BOOL success = CreateProcessA( - NULL, // Application name - cmd_str, // Command line - NULL, // Process security attributes - NULL, // Thread security attributes - TRUE, // Inherit handles - CREATE_NO_WINDOW, // Creation flags - env_vars_.empty() ? NULL : (LPVOID)env_block.c_str(), // Environment variables - NULL, // Current directory - &si, // Startup info - &pi // Process info + NULL, // Application name + cmd_line_ptr, // Command line + NULL, // Process security attributes + NULL, // Thread security attributes + TRUE, // Inherit handles + CREATE_NO_WINDOW, // Creation flags + NULL, // Environment variables + NULL, // Current directory + &si, // Startup info + &pi // Process info ); + + free(cmd_line_ptr); if (!success) { LOG_ERROR("Failed to create process: ", GetLastError()); @@ -323,7 +324,8 @@ bool stdio_client::start_server_process() { // Set non-blocking mode DWORD mode = PIPE_NOWAIT; - SetNamedPipeHandleState(stdout_pipe_[0], &mode, NULL, NULL); + DWORD timeout = 100; // milliseconds + SetNamedPipeHandleState(stdout_pipe_[0], &mode, NULL, &timeout); #else // POSIX implementation @@ -459,7 +461,7 @@ bool stdio_client::start_server_process() { // Wait for a while to ensure process starts std::this_thread::sleep_for(std::chrono::milliseconds(500)); -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) // Check if process is still running DWORD exit_code; if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code != STILL_ACTIVE) { @@ -491,7 +493,7 @@ void stdio_client::stop_server_process() { running_ = false; -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) // Windows implementation // Close pipes if (stdin_pipe_[1] != NULL) { @@ -584,15 +586,21 @@ void stdio_client::read_thread_func() { char buffer[buffer_size]; std::string data_buffer; -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) // Windows implementation DWORD bytes_read; + int retry_count = 0; + + // Give the process some startup time (similar to UNIX implementation) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); while (running_) { // Read data BOOL success = ReadFile(stdout_pipe_[0], buffer, buffer_size - 1, &bytes_read, NULL); if (success && bytes_read > 0) { + // Successfully read data + retry_count = 0; // Reset retry count buffer[bytes_read] = '\0'; data_buffer.append(buffer, bytes_read); @@ -644,20 +652,55 @@ void stdio_client::read_thread_func() { } } else if (!success) { DWORD error = GetLastError(); - if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) { - // Pipe is closed or no data available - LOG_WARNING("Pipe closed by server or no data available"); - break; + + if (error == ERROR_BROKEN_PIPE) { + // The pipe is closed - check if the process is still running + DWORD exit_code; + if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code != STILL_ACTIVE) { + LOG_WARNING("Service process exited, exit code: ", exit_code); + break; + } + + // The pipe is closed but the process is still running - it might be a temporary state + retry_count++; + if (retry_count > 5) { + LOG_ERROR("The pipe is closed but the process is still running, retry count has reached the limit"); + break; + } + + // Retry after a short delay + std::this_thread::sleep_for(std::chrono::milliseconds(50 * retry_count)); + } else if (error == ERROR_NO_DATA) { + // Simulate UNIX's EAGAIN/EWOULDBLOCK behavior + // The pipe is temporarily empty - this is normal for non-blocking mode + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } else if (error != ERROR_IO_PENDING) { + // Other errors, log and retry LOG_ERROR("Error reading from pipe: ", error); + retry_count++; + + if (retry_count > 10) { + LOG_ERROR("Read error retry count has reached the limit, exiting read loop"); + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(20 * retry_count)); + } else { + // IO_PENDING is the normal state for asynchronous IO + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } else { + // ReadFile successfully but no data - similar to reading 0 bytes but not EOF on UNIX + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + // Periodically check if the process is still running (similar to UNIX's waitpid check) + if (retry_count > 3) { + DWORD exit_code; + if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code != STILL_ACTIVE) { + LOG_WARNING("Service process exited, exit code: ", exit_code); break; } - - // No data to read in non-blocking mode - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } else { - // No data to read in non-blocking mode - std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } #else @@ -743,7 +786,7 @@ json stdio_client::send_jsonrpc(const request& req) { json req_json = req.to_json(); std::string req_str = req_json.dump() + "\n"; -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) // Windows implementation DWORD bytes_written; BOOL success = WriteFile(stdin_pipe_[1], req_str.c_str(), static_cast(req_str.size()), &bytes_written, NULL);