From 796b1ada8d1f99b883576b5535e41191c9876c91 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Wed, 24 Jun 2026 12:56:51 +0200 Subject: [PATCH 1/3] server: use status code 403 for disabled features --- tools/server/server.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/server/server.cpp b/tools/server/server.cpp index 4165c1015e8..6559a608979 100644 --- a/tools/server/server.cpp +++ b/tools/server/server.cpp @@ -241,6 +241,13 @@ int llama_server(int argc, char ** argv) { // Google Cloud Platform (Vertex AI) compat ctx_http.register_gcp_compat(); + // return 403 for disabled endpoints + server_http_context::handler_t res_403 = [](const server_http_req &) { + auto res = std::make_unique(); + res->status = 403; + return res; + }; + // CORS proxy (EXPERIMENTAL, only used by the Web UI for MCP) if (params.ui_mcp_proxy) { SRV_WRN("%s", "-----------------\n"); @@ -249,6 +256,9 @@ int llama_server(int argc, char ** argv) { SRV_WRN("%s", "-----------------\n"); ctx_http.get ("/cors-proxy", ex_wrapper(proxy_handler_get)); ctx_http.post("/cors-proxy", ex_wrapper(proxy_handler_post)); + } else { + ctx_http.get ("/cors-proxy", ex_wrapper(res_403)); + ctx_http.post("/cors-proxy", ex_wrapper(res_403)); } // EXPERIMENTAL built-in tools if (!params.server_tools.empty()) { @@ -264,6 +274,9 @@ int llama_server(int argc, char ** argv) { SRV_WRN("%s", "-----------------\n"); ctx_http.get ("/tools", ex_wrapper(tools.handle_get)); ctx_http.post("/tools", ex_wrapper(tools.handle_post)); + } else { + ctx_http.get ("/tools", ex_wrapper(res_403)); + ctx_http.post("/tools", ex_wrapper(res_403)); } // From d9a0c0fe9b5f073a5fa0575f1b9c70198f34f56c Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Wed, 24 Jun 2026 13:29:28 +0200 Subject: [PATCH 2/3] cont --- tools/server/server.cpp | 9 ++++++++- tools/ui/src/lib/stores/tools.svelte.ts | 9 ++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/server/server.cpp b/tools/server/server.cpp index 6559a608979..266e2240ee4 100644 --- a/tools/server/server.cpp +++ b/tools/server/server.cpp @@ -241,10 +241,16 @@ int llama_server(int argc, char ** argv) { // Google Cloud Platform (Vertex AI) compat ctx_http.register_gcp_compat(); - // return 403 for disabled endpoints + // return 403 for disabled features server_http_context::handler_t res_403 = [](const server_http_req &) { auto res = std::make_unique(); res->status = 403; + res->data = safe_json_to_str({ + {"error", { + {"message", "this feature is disabled"}, + {"type", "feature_disabled"}, + }} + }); return res; }; @@ -260,6 +266,7 @@ int llama_server(int argc, char ** argv) { ctx_http.get ("/cors-proxy", ex_wrapper(res_403)); ctx_http.post("/cors-proxy", ex_wrapper(res_403)); } + // EXPERIMENTAL built-in tools if (!params.server_tools.empty()) { try { diff --git a/tools/ui/src/lib/stores/tools.svelte.ts b/tools/ui/src/lib/stores/tools.svelte.ts index 9f0101a82e4..a6378198850 100644 --- a/tools/ui/src/lib/stores/tools.svelte.ts +++ b/tools/ui/src/lib/stores/tools.svelte.ts @@ -392,11 +392,14 @@ class ToolsStore { } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err); this._error = errorMessage; - // 404 from /tools means the server was started without --tools - if (errorMessage.includes('404') || errorMessage.toLowerCase().includes('not found')) { + // 403 from /tools means the server was started without --tools + // TODO: check status code instead of relying on message + if (errorMessage.includes('this feature is disabled')) { this._toolsEndpointUnreachable = true; + console.info('[ToolsStore] Built-in tools are disabled on the server'); + } else { + console.error('[ToolsStore] Failed to fetch built-in tools:', err); } - console.error('[ToolsStore] Failed to fetch built-in tools:', err); } finally { this._loading = false; } From a14f8d2ed5dd5348fc6d7a093f155f074e70029d Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Wed, 24 Jun 2026 13:38:25 +0200 Subject: [PATCH 3/3] fix test case --- tools/server/tests/unit/test_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/server/tests/unit/test_proxy.py b/tools/server/tests/unit/test_proxy.py index 3b86d80473e..0fed536e59a 100644 --- a/tools/server/tests/unit/test_proxy.py +++ b/tools/server/tests/unit/test_proxy.py @@ -16,7 +16,7 @@ def test_mcp_no_proxy(): server.start() res = server.make_request("GET", "/cors-proxy") - assert res.status_code == 404 + assert res.status_code == 403 def test_mcp_proxy():