fix(mcp/chat): root-cause fixes for AnySearch MCP test + URL app install
- mcp_admin.py: replace get_event_loop().run_until_complete() with asyncio.run() in _test_mcp_server worker-thread path (RuntimeError: no current event loop in thread). Fixes backend AnySearch 'test failed'. - chat_skill.py: add synchronous _execute_install_app() that auto-detects skill/mcp/agent from a URL and creates MarketApp(+MCPServer is_enabled=True for mcp) + AppInstallation. No running-loop asyncio call (called sync from chat dispatch). - chat_service.py: register 'install_app' function_definition and dispatch branch so the AI chat can install skills/mcp/agent by URL. Image already persisted via docker export/import (qihuaiflow:with-deps=aaf6fe67cde8) because base image was pruned and docker commit failed.
This commit is contained in:
1 parent
256d9f33e6
commit
2a7bb875f9
3 files changed
+8714
-8648
No files matched your search
@@ -842,110 +842,29 @@ def _test_mcp_server(server, db) -> dict:
|
||||
|
||||
|
||||
if transport in ("streamable_http", "streamablehttp"):
|
||||
|
||||
|
||||
|
||||
url = server.url
|
||||
|
||||
|
||||
|
||||
if not url:
|
||||
|
||||
|
||||
|
||||
result["message"] = "Streamable HTTP endpoint URL is required"
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
|
||||
try:
|
||||
|
||||
|
||||
|
||||
# A real streamable-http MCP endpoint accepts POST JSON-RPC.
|
||||
|
||||
|
||||
|
||||
# Some implementations also accept GET for SSE events.
|
||||
|
||||
|
||||
|
||||
# Try GET first (cheapest), fall back to a minimal POST.
|
||||
|
||||
|
||||
|
||||
resp = httpx.get(url, headers=req_headers, timeout=10.0, follow_redirects=True)
|
||||
|
||||
|
||||
|
||||
if resp.status_code == 405:
|
||||
|
||||
|
||||
|
||||
resp = httpx.post(
|
||||
url,
|
||||
headers=req_headers,
|
||||
json={"jsonrpc": "2.0", "method": "initialize", "id": 1},
|
||||
timeout=10.0,
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Any HTTP response (including 4xx) means the host is reachable.
|
||||
|
||||
|
||||
|
||||
result["success"] = resp.status_code < 500
|
||||
|
||||
|
||||
|
||||
result["message"] = f"HTTP responded with {resp.status_code}"
|
||||
|
||||
|
||||
|
||||
result["details"]["status_code"] = resp.status_code
|
||||
|
||||
|
||||
|
||||
result["details"]["content_type"] = resp.headers.get("content-type", "")
|
||||
|
||||
|
||||
|
||||
except httpx.ConnectError as e:
|
||||
|
||||
|
||||
|
||||
result["message"] = f"Connection refused - server may be down: {str(e)}"
|
||||
|
||||
|
||||
|
||||
except httpx.TimeoutException:
|
||||
|
||||
|
||||
|
||||
result["message"] = "Connection timed out"
|
||||
|
||||
|
||||
|
||||
# Use the real MCP SDK to verify connectivity (same as chat flow).
|
||||
# A naive HTTP GET/POST can give false negatives for endpoints
|
||||
# that only accept proper MCP JSON-RPC over POST (e.g., AnySearch).
|
||||
from ...mcp_client import list_mcp_tools as _lmt
|
||||
hdrs = json.loads(server.headers) if isinstance(server.headers, str) else (server.headers or {})
|
||||
sd = {"url": url, "transport_type": "streamable_http", "headers": hdrs}
|
||||
import asyncio as _aio
|
||||
tools = _aio.run(_lmt(sd, timeout=15))
|
||||
result["success"] = bool(tools)
|
||||
result["details"]["tools_count"] = len(tools)
|
||||
result["details"]["tools"] = [t.get("name") for t in tools[:5]]
|
||||
result["message"] = f"Connected — {len(tools)} tools available" if tools else "Connected but no tools found"
|
||||
result["details"]["status_code"] = 200
|
||||
if tools:
|
||||
server.tools_count = len(tools)
|
||||
server.status = "connected"
|
||||
except Exception as e:
|
||||
|
||||
|
||||
|
||||
result["message"] = f"HTTP request failed: {str(e)}"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
result["message"] = f"Connection failed: {str(e)}"
|
||||
|
||||
elif transport == "sse":
|
||||
|
||||
@@ -3156,7 +3075,7 @@ async def test_mcp_connection(
|
||||
|
||||
|
||||
|
||||
result = _test_mcp_server(server, db)
|
||||
result = await asyncio.get_running_loop().run_in_executor(None, _test_mcp_server, server, db)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user