From 89ed5f71070f5e59b5ae06f040d7240da6734662 Mon Sep 17 00:00:00 2001 From: "mula.liu" Date: Sun, 15 Mar 2026 17:04:48 +0800 Subject: [PATCH] v0.1.4-p2 --- backend/main.py | 156 +++--------------------------------------------- 1 file changed, 7 insertions(+), 149 deletions(-) diff --git a/backend/main.py b/backend/main.py index 4940397..d5d2684 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1139,17 +1139,6 @@ def _probe_mcp_server(cfg: Dict[str, Any], bot_id: Optional[str] = None) -> Dict "probe_from": "validation", } - probe_payload = { - "jsonrpc": "2.0", - "id": "dashboard-probe", - "method": "initialize", - "params": { - "protocolVersion": "2025-03-26", - "capabilities": {}, - "clientInfo": {"name": "dashboard-nanobot", "version": "0.1.4"}, - }, - } - def _with_body_preview(message: str, preview: Any) -> str: text = str(message or "").strip() body = " ".join(str(preview or "").strip().split()) @@ -1187,14 +1176,13 @@ def _probe_mcp_server(cfg: Dict[str, Any], bot_id: Optional[str] = None) -> Dict return {"ok": True, "transport": transport_type, "status_code": status_code, "message": "MCP SSE endpoint is reachable", "content_type": content_type, "probe_from": "bot-container"} probe_headers = dict(headers) - probe_headers.setdefault("Content-Type", "application/json") probe_headers.setdefault("Accept", "application/json, text/event-stream") probe = docker_manager.probe_http_from_container( bot_id=bot_id, url=url, - method="POST", + method="GET", headers=probe_headers, - body_json=probe_payload, + body_json=None, timeout_seconds=timeout_s, ) status_code = probe.get("status_code") @@ -1206,8 +1194,8 @@ def _probe_mcp_server(cfg: Dict[str, Any], bot_id: Optional[str] = None) -> Dict return {"ok": False, "transport": transport_type, "status_code": status_code, "message": "MCP endpoint not found", "probe_from": "bot-container"} if isinstance(status_code, int) and status_code >= 500: return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview("MCP endpoint server error", body_preview), "probe_from": "bot-container"} - if probe.get("ok") and status_code in {200, 201, 202, 204, 400, 405, 415, 422}: - reachability_msg = "MCP endpoint is reachable" if status_code in {200, 201, 202, 204} else "MCP endpoint is reachable (request format not fully accepted by probe)" + if probe.get("ok") and status_code in {200, 201, 202, 204, 400, 401, 403, 405, 406, 415, 422}: + reachability_msg = "MCP endpoint is reachable" if status_code in {200, 201, 202, 204} else "MCP endpoint is reachable (HTTP endpoint responded as expected)" return {"ok": True, "transport": transport_type, "status_code": status_code, "message": reachability_msg, "probe_from": "bot-container"} return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview(message or "Unexpected response from MCP endpoint", body_preview), "probe_from": "bot-container"} @@ -1265,9 +1253,8 @@ def _probe_mcp_server(cfg: Dict[str, Any], bot_id: Optional[str] = None) -> Dict } req_headers = dict(headers) - req_headers.setdefault("Content-Type", "application/json") req_headers.setdefault("Accept", "application/json, text/event-stream") - resp = client.post(url, headers=req_headers, json=probe_payload) + resp = client.get(url, headers=req_headers) body_preview = resp.text[:512] if resp.status_code in {401, 403}: return { @@ -1301,12 +1288,12 @@ def _probe_mcp_server(cfg: Dict[str, Any], bot_id: Optional[str] = None) -> Dict "message": "MCP endpoint is reachable", "probe_from": "backend-host", } - if resp.status_code in {400, 405, 415, 422}: + if resp.status_code in {400, 401, 403, 405, 406, 415, 422}: return { "ok": True, "transport": transport_type, "status_code": resp.status_code, - "message": "MCP endpoint is reachable (request format not fully accepted by probe)", + "message": "MCP endpoint is reachable (HTTP endpoint responded as expected)", "probe_from": "backend-host", } return { @@ -1334,124 +1321,6 @@ def _probe_mcp_server(cfg: Dict[str, Any], bot_id: Optional[str] = None) -> Dict } -def _probe_mcp_server_for_start(cfg: Dict[str, Any], image_tag: str) -> Dict[str, Any]: - transport_type = str(cfg.get("type") or "streamableHttp").strip() - if transport_type not in {"streamableHttp", "sse"}: - transport_type = "streamableHttp" - url = str(cfg.get("url") or "").strip() - headers_raw = cfg.get("headers") - headers: Dict[str, str] = {} - if isinstance(headers_raw, dict): - for k, v in headers_raw.items(): - key = str(k or "").strip() - if key: - headers[key] = str(v or "").strip() - timeout_raw = cfg.get("toolTimeout", 10) - try: - timeout_s = max(1, min(int(timeout_raw), 30)) - except Exception: - timeout_s = 10 - - if not url: - return { - "ok": False, - "transport": transport_type, - "status_code": None, - "message": "MCP url is required", - "probe_from": "validation", - } - - probe_payload = { - "jsonrpc": "2.0", - "id": "dashboard-start-probe", - "method": "initialize", - "params": { - "protocolVersion": "2025-03-26", - "capabilities": {}, - "clientInfo": {"name": "dashboard-nanobot", "version": "0.1.4"}, - }, - } - - def _with_body_preview(message: str, preview: Any) -> str: - text = str(message or "").strip() - body = " ".join(str(preview or "").strip().split()) - if not body: - return text - body = body[:240] - return f"{text}: {body}" if text else body - - if transport_type == "sse": - probe_headers = dict(headers) - probe_headers.setdefault("Accept", "text/event-stream") - probe = docker_manager.probe_http_via_temporary_container( - image_tag=image_tag, - url=url, - method="GET", - headers=probe_headers, - body_json=None, - timeout_seconds=timeout_s, - ) - status_code = probe.get("status_code") - content_type = str(probe.get("content_type") or "") - message = str(probe.get("message") or "").strip() - body_preview = probe.get("body_preview") - if status_code in {401, 403}: - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": "Auth failed for MCP SSE endpoint", "content_type": content_type, "probe_from": "temp-container"} - if status_code == 404: - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": "MCP SSE endpoint not found", "content_type": content_type, "probe_from": "temp-container"} - if isinstance(status_code, int) and status_code >= 500: - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview("MCP SSE endpoint server error", body_preview), "content_type": content_type, "probe_from": "temp-container"} - if not probe.get("ok"): - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview(message or "Failed to connect MCP SSE endpoint from temporary probe container", body_preview), "content_type": content_type, "probe_from": "temp-container"} - if "text/event-stream" not in content_type.lower(): - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview("Endpoint reachable, but content-type is not text/event-stream", body_preview), "content_type": content_type, "probe_from": "temp-container"} - return {"ok": True, "transport": transport_type, "status_code": status_code, "message": "MCP SSE endpoint is reachable", "content_type": content_type, "probe_from": "temp-container"} - - probe_headers = dict(headers) - probe_headers.setdefault("Content-Type", "application/json") - probe_headers.setdefault("Accept", "application/json, text/event-stream") - probe = docker_manager.probe_http_via_temporary_container( - image_tag=image_tag, - url=url, - method="POST", - headers=probe_headers, - body_json=probe_payload, - timeout_seconds=timeout_s, - ) - status_code = probe.get("status_code") - message = str(probe.get("message") or "").strip() - body_preview = probe.get("body_preview") - if status_code in {401, 403}: - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": "Auth failed for MCP endpoint", "probe_from": "temp-container"} - if status_code == 404: - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": "MCP endpoint not found", "probe_from": "temp-container"} - if isinstance(status_code, int) and status_code >= 500: - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview("MCP endpoint server error", body_preview), "probe_from": "temp-container"} - if probe.get("ok") and status_code in {200, 201, 202, 204, 400, 405, 415, 422}: - reachability_msg = "MCP endpoint is reachable" if status_code in {200, 201, 202, 204} else "MCP endpoint is reachable (request format not fully accepted by probe)" - return {"ok": True, "transport": transport_type, "status_code": status_code, "message": reachability_msg, "probe_from": "temp-container"} - return {"ok": False, "transport": transport_type, "status_code": status_code, "message": _with_body_preview(message or "Unexpected response from MCP endpoint", body_preview), "probe_from": "temp-container"} - - -def _preflight_mcp_servers_for_start(bot_id: str, image_tag: str) -> List[str]: - config_data = _read_bot_config(bot_id) - if not isinstance(config_data, dict): - return [] - tools_cfg = config_data.get("tools") - if not isinstance(tools_cfg, dict): - return [] - mcp_servers = _normalize_mcp_servers(tools_cfg.get("mcpServers")) - failures: List[str] = [] - for server_name, cfg in mcp_servers.items(): - result = _probe_mcp_server_for_start(cfg, image_tag=image_tag) - if result.get("ok"): - continue - message = str(result.get("message") or "MCP precheck failed").strip() - probe_from = str(result.get("probe_from") or "temp-container").strip() - failures.append(f"{server_name}: {message} [{probe_from}]") - return failures - - def _parse_env_params(raw: Any) -> Dict[str, str]: return _normalize_env_params(raw) @@ -2441,17 +2310,6 @@ async def start_bot(bot_id: str, session: Session = Depends(get_session)): if not bot: raise HTTPException(status_code=404, detail="Bot not found") _sync_workspace_channels(session, bot_id) - mcp_preflight_failures = _preflight_mcp_servers_for_start(bot_id, image_tag=bot.image_tag) - if mcp_preflight_failures: - bot.docker_status = "STOPPED" - session.add(bot) - session.commit() - _invalidate_bot_detail_cache(bot_id) - raise HTTPException( - status_code=400, - detail="MCP precheck failed: " + " | ".join(mcp_preflight_failures[:5]), - ) - runtime_snapshot = _read_bot_runtime_snapshot(bot) env_params = _read_env_store(bot_id) success = docker_manager.start_bot(