Spaces:
Sleeping
Sleeping
Create server_manager.py
Browse files- server_manager.py +159 -0
server_manager.py
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Server management utilities for Universal MCP Client
|
3 |
+
"""
|
4 |
+
import asyncio
|
5 |
+
import re
|
6 |
+
import logging
|
7 |
+
import traceback
|
8 |
+
from typing import Tuple
|
9 |
+
|
10 |
+
from config import MCPServerConfig
|
11 |
+
from mcp_client import UniversalMCPClient
|
12 |
+
|
13 |
+
logger = logging.getLogger(__name__)
|
14 |
+
|
15 |
+
class ServerManager:
|
16 |
+
"""Manages MCP server connections and status"""
|
17 |
+
|
18 |
+
def __init__(self, mcp_client: UniversalMCPClient):
|
19 |
+
self.mcp_client = mcp_client
|
20 |
+
|
21 |
+
def convert_hf_space_to_url(self, space_name: str) -> str:
|
22 |
+
"""
|
23 |
+
Convert HuggingFace space name to proper URL format.
|
24 |
+
|
25 |
+
HuggingFace URL rules:
|
26 |
+
- Replace "/" with "-"
|
27 |
+
- Convert to lowercase
|
28 |
+
- Replace dots and other special chars with "-"
|
29 |
+
- Remove consecutive hyphens
|
30 |
+
"""
|
31 |
+
if "/" not in space_name:
|
32 |
+
raise ValueError("Space name should be in format: username/space-name")
|
33 |
+
|
34 |
+
# Replace "/" with "-"
|
35 |
+
url_name = space_name.replace("/", "-")
|
36 |
+
|
37 |
+
# Convert to lowercase
|
38 |
+
url_name = url_name.lower()
|
39 |
+
|
40 |
+
# Replace dots and other special characters with hyphens
|
41 |
+
url_name = re.sub(r'[^a-z0-9\-]', '-', url_name)
|
42 |
+
|
43 |
+
# Remove consecutive hyphens
|
44 |
+
url_name = re.sub(r'-+', '-', url_name)
|
45 |
+
|
46 |
+
# Remove leading/trailing hyphens
|
47 |
+
url_name = url_name.strip('-')
|
48 |
+
|
49 |
+
return f"https://{url_name}.hf.space"
|
50 |
+
|
51 |
+
def add_custom_server(self, name: str, space_name: str) -> Tuple[str, str]:
|
52 |
+
"""Add a custom MCP server from HuggingFace space name"""
|
53 |
+
logger.info(f"β Adding MCP server: {name} from space: {space_name}")
|
54 |
+
|
55 |
+
if not name or not space_name:
|
56 |
+
return "β Please provide both server name and space name", ""
|
57 |
+
|
58 |
+
space_name = space_name.strip()
|
59 |
+
|
60 |
+
try:
|
61 |
+
# Use the improved URL conversion
|
62 |
+
mcp_url = self.convert_hf_space_to_url(space_name)
|
63 |
+
logger.info(f"π Converted {space_name} β {mcp_url}")
|
64 |
+
|
65 |
+
except ValueError as e:
|
66 |
+
return f"β {str(e)}", ""
|
67 |
+
|
68 |
+
config = MCPServerConfig(
|
69 |
+
name=name.strip(),
|
70 |
+
url=mcp_url,
|
71 |
+
description=f"MCP server from HuggingFace space: {space_name}",
|
72 |
+
space_id=space_name
|
73 |
+
)
|
74 |
+
|
75 |
+
try:
|
76 |
+
# Run async function
|
77 |
+
def run_async():
|
78 |
+
loop = asyncio.new_event_loop()
|
79 |
+
asyncio.set_event_loop(loop)
|
80 |
+
try:
|
81 |
+
return loop.run_until_complete(self.mcp_client.add_server_async(config))
|
82 |
+
finally:
|
83 |
+
loop.close()
|
84 |
+
|
85 |
+
success, message = run_async()
|
86 |
+
logger.info(f"Server addition result: {success} - {message}")
|
87 |
+
|
88 |
+
if success:
|
89 |
+
# Format success message for accordion display
|
90 |
+
tools_info = ""
|
91 |
+
if 'Found' in message and 'tools:' in message:
|
92 |
+
tools_section = message.split('Found')[1]
|
93 |
+
tools_info = f"**Available Tools:**\n{tools_section}"
|
94 |
+
|
95 |
+
details_html = f"""
|
96 |
+
<details style="margin-top: 10px;">
|
97 |
+
<summary style="cursor: pointer; padding: 8px; background: #f0f0f0; border-radius: 4px;"><strong>β
{name} - Connection Details</strong></summary>
|
98 |
+
<div style="padding: 10px; border-left: 3px solid #28a745; margin-left: 10px; margin-top: 5px;">
|
99 |
+
<p><strong>Space:</strong> {space_name}</p>
|
100 |
+
<p><strong>Base URL:</strong> {mcp_url}</p>
|
101 |
+
<p><strong>Status:</strong> Connected successfully!</p>
|
102 |
+
<div style="margin-top: 10px;">
|
103 |
+
{tools_info.replace('**', '<strong>').replace('**', '</strong>').replace(chr(10), '<br>')}
|
104 |
+
</div>
|
105 |
+
</div>
|
106 |
+
</details>
|
107 |
+
"""
|
108 |
+
return "β
Server added successfully!", details_html
|
109 |
+
else:
|
110 |
+
error_html = f"""
|
111 |
+
<details style="margin-top: 10px;">
|
112 |
+
<summary style="cursor: pointer; padding: 8px; background: #f8d7da; border-radius: 4px;"><strong>β {name} - Connection Failed</strong></summary>
|
113 |
+
<div style="padding: 10px; border-left: 3px solid #dc3545; margin-left: 10px; margin-top: 5px;">
|
114 |
+
<p>{message}</p>
|
115 |
+
</div>
|
116 |
+
</details>
|
117 |
+
"""
|
118 |
+
return f"β Failed to add server: {name}", error_html
|
119 |
+
|
120 |
+
except Exception as e:
|
121 |
+
error_msg = f"β Failed to add server: {str(e)}"
|
122 |
+
logger.error(error_msg)
|
123 |
+
logger.error(traceback.format_exc())
|
124 |
+
return error_msg, ""
|
125 |
+
|
126 |
+
def get_server_status(self) -> Tuple[str, str]:
|
127 |
+
"""Get status of all servers in accordion format"""
|
128 |
+
try:
|
129 |
+
status = self.mcp_client.get_server_status()
|
130 |
+
server_count = f"**Total MCP Servers**: {len(status)}"
|
131 |
+
|
132 |
+
if not status:
|
133 |
+
return server_count, "<p><em>No MCP servers configured yet.</em></p>"
|
134 |
+
|
135 |
+
accordion_html = ""
|
136 |
+
|
137 |
+
for name, state in status.items():
|
138 |
+
server_config = self.mcp_client.servers[name]
|
139 |
+
base_url = server_config.url.replace("/gradio_api/mcp/sse", "")
|
140 |
+
|
141 |
+
# Determine health status
|
142 |
+
health = "π’ Healthy" if "β
Connected" in state else "π΄ Unhealthy"
|
143 |
+
|
144 |
+
accordion_html += f"""
|
145 |
+
<details style="margin-bottom: 10px;">
|
146 |
+
<summary style="cursor: pointer; padding: 8px; background: #e9ecef; border-radius: 4px;"><strong>π§ {name}</strong></summary>
|
147 |
+
<div style="padding: 10px; border-left: 3px solid #007bff; margin-left: 10px; margin-top: 5px;">
|
148 |
+
<p><strong>Title:</strong> {name}</p>
|
149 |
+
<p><strong>Status:</strong> Connected (MCP Protocol)</p>
|
150 |
+
<p><strong>Health:</strong> {health}</p>
|
151 |
+
<p><strong>Base URL:</strong> {base_url}</p>
|
152 |
+
</div>
|
153 |
+
</details>
|
154 |
+
"""
|
155 |
+
|
156 |
+
return server_count, accordion_html
|
157 |
+
|
158 |
+
except Exception as e:
|
159 |
+
return "**Total MCP Servers**: 0", f"<p style='color: red;'>β Error getting status: {str(e)}</p>"
|