diff --git a/api/routes.py b/api/routes.py index edef7ed5..63739194 100644 --- a/api/routes.py +++ b/api/routes.py @@ -6229,6 +6229,9 @@ def handle_patch(handler, parsed) -> bool: if not _check_csrf(handler): return j(handler, {"error": _csrf_rejection_error(handler)}, status=403) body = read_body(handler) + if parsed.path.startswith("/api/mcp/servers/"): + name = parsed.path[len("/api/mcp/servers/"):] + return _handle_mcp_server_toggle(handler, name, body) if parsed.path.startswith("/api/kanban/"): from api.kanban_bridge import handle_kanban_patch @@ -6244,6 +6247,9 @@ def handle_delete(handler, parsed) -> bool: if not _check_csrf(handler): return j(handler, {"error": _csrf_rejection_error(handler)}, status=403) body = read_body(handler) + if parsed.path.startswith("/api/mcp/servers/"): + name = parsed.path[len("/api/mcp/servers/"):] + return _handle_mcp_server_delete(handler, name) if parsed.path.startswith("/api/kanban/"): from api.kanban_bridge import handle_kanban_delete @@ -6253,6 +6259,17 @@ def handle_delete(handler, parsed) -> bool: return True return False + +def handle_put(handler, parsed) -> bool: + """Handle all PUT routes. Returns True if handled, False for 404.""" + if not _check_csrf(handler): + return j(handler, {"error": "Cross-origin request rejected"}, status=403) + body = read_body(handler) + if parsed.path.startswith("/api/mcp/servers/"): + name = parsed.path[len("/api/mcp/servers/"):] + return _handle_mcp_server_update(handler, name, body) + return False + # ── GET route helpers ───────────────────────────────────────────────────────── # MIME types for static file serving. Hoisted to module scope to avoid @@ -11888,7 +11905,7 @@ def _handle_mcp_servers_list(handler): ] return j(handler, { "servers": result, - "toggle_supported": False, + "toggle_supported": True, "reload_required": True, }) @@ -11912,6 +11929,30 @@ def _handle_mcp_server_delete(handler, name): return j(handler, {"ok": True, "deleted": name}) +def _handle_mcp_server_toggle(handler, name, body): + """Toggle enabled state for an MCP server (PATCH /api/mcp/servers/{name}).""" + from urllib.parse import unquote + name = unquote(name) + if not name: + return bad(handler, "name is required") + if "enabled" not in body: + return bad(handler, "enabled field is required") + enabled = bool(body["enabled"]) + cfg = get_config() + servers = cfg.get("mcp_servers", {}) + if not isinstance(servers, dict): + servers = {} + if name not in servers: + return bad(handler, f"MCP server '{name}' not found", 404) + if not isinstance(servers[name], dict): + return bad(handler, f"MCP server '{name}' has invalid config", 400) + servers[name]["enabled"] = enabled + cfg["mcp_servers"] = servers + _save_yaml_config_file(_get_config_path(), cfg) + reload_config() + return j(handler, {"ok": True, "name": name, "enabled": enabled}) + + _MASKED_PLACEHOLDER = "••••••" diff --git a/server.py b/server.py index 3888afc5..7405d073 100644 --- a/server.py +++ b/server.py @@ -115,7 +115,7 @@ from api.auth import check_auth from api.config import HOST, PORT, STATE_DIR, SESSION_DIR, DEFAULT_WORKSPACE from api.helpers import j, get_profile_cookie from api.profiles import set_request_profile, clear_request_profile -from api.routes import handle_delete, handle_get, handle_patch, handle_post +from api.routes import handle_delete, handle_get, handle_patch, handle_post, handle_put from api.startup import auto_install_agent_deps, fix_credential_permissions from api.updates import WEBUI_VERSION @@ -286,6 +286,9 @@ class Handler(BaseHTTPRequestHandler): def do_POST(self) -> None: self._handle_write(handle_post) + def do_PUT(self) -> None: + self._handle_write(handle_put) + def do_PATCH(self) -> None: self._handle_write(handle_patch) diff --git a/static/i18n.js b/static/i18n.js index 0f1f8867..5e54d9d6 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -87,6 +87,11 @@ const LOCALES = { mcp_tool_count: '{0} tools', mcp_enabled_yes: 'Enabled', mcp_enabled_no: 'Disabled', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP Tools', mcp_tools_desc: 'Search known tools across active MCP servers.', mcp_tools_search_placeholder: 'Search tools by name, server, or description…', @@ -1325,6 +1330,11 @@ const LOCALES = { mcp_tool_count: '{0} strumenti', mcp_enabled_yes: 'Abilitato', mcp_enabled_no: 'Disabilitato', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'Strumenti MCP', mcp_tools_desc: 'Cerca strumenti noti tra i server MCP attivi.', mcp_tools_search_placeholder: 'Cerca strumenti per nome, server o descrizione…', @@ -2555,6 +2565,11 @@ const LOCALES = { mcp_tool_count: '{0} 個のツール', mcp_enabled_yes: '有効', mcp_enabled_no: '無効', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP ツール', mcp_tools_desc: 'アクティブな MCP サーバー全体から既知のツールを検索します。', mcp_tools_search_placeholder: '名前、サーバー、説明でツールを検索…', @@ -3787,6 +3802,11 @@ const LOCALES = { mcp_tool_count: '{0} tools', mcp_enabled_yes: 'Enabled', mcp_enabled_no: 'Disabled', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP Tools', mcp_tools_desc: 'Search known tools across active MCP servers.', mcp_tools_search_placeholder: 'Search tools by name, server, or description…', @@ -4951,6 +4971,11 @@ const LOCALES = { mcp_tool_count: '{0} tools', mcp_enabled_yes: 'Enabled', mcp_enabled_no: 'Disabled', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP Tools', mcp_tools_desc: 'Search known tools across active MCP servers.', mcp_tools_search_placeholder: 'Search tools by name, server, or description…', @@ -6118,6 +6143,11 @@ const LOCALES = { mcp_tool_count: '{0} tools', mcp_enabled_yes: 'Enabled', mcp_enabled_no: 'Disabled', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP Tools', mcp_tools_desc: 'Search known tools across active MCP servers.', mcp_tools_search_placeholder: 'Search tools by name, server, or description…', @@ -7289,6 +7319,11 @@ const LOCALES = { mcp_tool_count: '{0} 个工具', mcp_enabled_yes: '已启用', mcp_enabled_no: '已禁用', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP 工具', mcp_tools_desc: '搜索活跃 MCP 服务器中的已知工具。', mcp_tools_search_placeholder: '按名称、服务器或描述搜索工具…', @@ -8447,6 +8482,11 @@ const LOCALES = { mcp_tool_count: '{0} tools', mcp_enabled_yes: 'Enabled', mcp_enabled_no: 'Disabled', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP Tools', mcp_tools_desc: 'Search known tools across active MCP servers.', mcp_tools_search_placeholder: 'Search tools by name, server, or description…', @@ -10754,6 +10794,11 @@ const LOCALES = { mcp_tool_count: '{0} tools', mcp_enabled_yes: 'Enabled', mcp_enabled_no: 'Disabled', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'MCP Tools', mcp_tools_desc: 'Search known tools across active MCP servers.', mcp_tools_search_placeholder: 'Search tools by name, server, or description…', @@ -11981,6 +12026,11 @@ const LOCALES = { mcp_tool_count: '{0} outils', mcp_enabled_yes: 'Activé', mcp_enabled_no: 'Désactivé', + mcp_enable_server: 'Enable this MCP server', + mcp_disable_server: 'Disable this MCP server', + mcp_enabled_toast: (name) => `MCP server "${name}" enabled.`, + mcp_disabled_toast: (name) => `MCP server "${name}" disabled.`, + mcp_toggle_failed: 'Failed to update MCP server.', mcp_tools_title: 'Outils MCP', mcp_tools_desc: 'Recherchez des outils connus sur les serveurs MCP actifs.', mcp_tools_search_placeholder: 'Outils de recherche par nom, serveur ou description…', diff --git a/static/panels.js b/static/panels.js index fdeeb510..89ad0d20 100644 --- a/static/panels.js +++ b/static/panels.js @@ -7120,6 +7120,16 @@ function _mcpStatusLabel(status){ }[status]||'mcp_status_unknown'; return t(key); } +function toggleMcpServer(name, enabled){ + api('/api/mcp/servers/'+encodeURIComponent(name),{ + method:'PATCH', + body:JSON.stringify({enabled:enabled}), + }).then(r=>{ + if(r&&r.ok) showToast(t(enabled?'mcp_enabled_toast':'mcp_disabled_toast',name)); + else showToast(t('mcp_toggle_failed'),'error'); + loadMcpServers(); + }).catch(()=>{showToast(t('mcp_toggle_failed'),'error');loadMcpServers();}); +} function loadMcpServers(){ const list=$('mcpServerList'); if(!list) return; @@ -7130,7 +7140,6 @@ function loadMcpServers(){ list.innerHTML=`