Commit 468fde90e for llama.cpp

commit 468fde90e8118fe7c90dbce478095e1a74758e92
Author: Adrien Gallouët <angt@huggingface.co>
Date:   Thu Nov 5 15:34:54 2026 +0100

    server : simplify windows MCP command resolution (#31728)

    * server : simplify windows MCP command resolution

    Signed-off-by: Adrien Gallouët <angt@huggingface.co>

    * common : remove utf8_to_wstring()

    Signed-off-by: Adrien Gallouët <angt@huggingface.co>

    ---------

    Signed-off-by: Adrien Gallouët <angt@huggingface.co>

diff --git a/common/common.cpp b/common/common.cpp
index bc51de053..ba4a3e892 100644
--- a/common/common.cpp
+++ b/common/common.cpp
@@ -895,27 +895,7 @@ bool fs_validate_filename(const std::string & filename, bool allow_subdirs) {
     return true;
 }

-#include <iostream>
-
-
 #ifdef _WIN32
-std::wstring utf8_to_wstring(const std::string & str) {
-    if (str.empty()) {
-        return std::wstring();
-    }
-
-    int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), NULL, 0);
-
-    if (size <= 0) {
-        return std::wstring();
-    }
-
-    std::wstring wstr(size, 0);
-    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], size);
-
-    return wstr;
-}
-
 std::string wstring_to_utf8(const std::wstring & str) {
     if (str.empty()) {
         return std::string();
diff --git a/common/common.h b/common/common.h
index 303e0c293..4005275ca 100644
--- a/common/common.h
+++ b/common/common.h
@@ -906,8 +906,7 @@ bool glob_match(const std::string & pattern, const std::string & str);
 //

 #ifdef _WIN32
-std::wstring utf8_to_wstring(const std::string & str);
-std::string  wstring_to_utf8(const std::wstring & str);
+std::string wstring_to_utf8(const std::wstring & str);
 #endif

 //
diff --git a/tools/server/server-mcp.cpp b/tools/server/server-mcp.cpp
index c2baa16db..22401e08a 100644
--- a/tools/server/server-mcp.cpp
+++ b/tools/server/server-mcp.cpp
@@ -348,23 +348,22 @@ struct server_mcp_stdio::process_handle {
     FILE * err = nullptr; // child stderr
 };

-#if defined(_WIN32)
-// config strings are UTF-8 (from JSON) and subprocess.h converts them with CP_UTF8, so inputs must be UTF-8, not the active code page
-static std::string wide_to_utf8(const wchar_t * s, int len /* -1 for NUL-terminated */) {
-    return wstring_to_utf8(len == -1 ? std::wstring(s) : std::wstring(s, s + len));
-}
-#endif
-
 static std::string mcp_resolve_command(const std::string & command) {
 #if defined(_WIN32)
-    // For Windows: make sure we handle ".exe" correctly, as well as UTF-8
-    std::wstring wcmd = utf8_to_wstring(command);
-    wchar_t      buf[MAX_PATH * 4];
-    const DWORD  cap = (DWORD) (sizeof(buf) / sizeof(buf[0]));
-
-    auto search = [&](const wchar_t * ext) -> std::string {
-        DWORD n = SearchPathW(NULL, wcmd.c_str(), ext, cap, buf, NULL);
-        return (n > 0 && n < cap) ? wide_to_utf8(buf, (int) n) : std::string();
+    // the application manifest makes SearchPathA use UTF-8
+    auto search = [&](const char * ext) -> std::string {
+        std::string result;
+        for (;;) {
+            DWORD n = SearchPathA(NULL, command.c_str(), ext, result.size(), result.data(), NULL);
+            if (n == 0) {
+                return {};
+            }
+            if (n < result.size()) {
+                result.resize(n);
+                return result;
+            }
+            result.resize((size_t) n + 1);
+        }
     };

     std::string found = search(NULL); // exact path / already-extensioned / .exe on PATH
@@ -372,26 +371,20 @@ static std::string mcp_resolve_command(const std::string & command) {
         return found;
     }

-    std::wstring pathext;
-    DWORD        need = GetEnvironmentVariableW(L"PATHEXT", NULL, 0);
-    if (need > 0) {
-        pathext.resize(need);
-        DWORD got = GetEnvironmentVariableW(L"PATHEXT", &pathext[0], need);
-        pathext.resize(got);
-    }
+    std::string pathext = common_get_env("PATHEXT");
     if (pathext.empty()) {
-        pathext = L".COM;.EXE;.BAT;.CMD";
+        pathext = ".COM;.EXE;.BAT;.CMD";
     }
     for (size_t start = 0; start <= pathext.size();) {
-        size_t       sep = pathext.find(L';', start);
-        std::wstring ext = pathext.substr(start, sep == std::wstring::npos ? std::wstring::npos : sep - start);
+        size_t      sep = pathext.find(';', start);
+        std::string ext = pathext.substr(start, sep == std::string::npos ? std::string::npos : sep - start);
         if (!ext.empty()) {
             found = search(ext.c_str());
             if (!found.empty()) {
                 return found;
             }
         }
-        if (sep == std::wstring::npos) {
+        if (sep == std::string::npos) {
             break;
         }
         start = sep + 1;
@@ -408,7 +401,7 @@ static std::vector<std::string> mcp_parent_env() {
     LPWCH block = GetEnvironmentStringsW();
     if (block) {
         for (LPWCH e = block; *e; e += wcslen(e) + 1) {
-            env.emplace_back(wide_to_utf8(e, -1));
+            env.emplace_back(wstring_to_utf8(e));
         }
         FreeEnvironmentStringsW(block);
     }