mseep-lightfast-mcp 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- common/__init__.py +21 -0
- common/types.py +182 -0
- lightfast_mcp/__init__.py +50 -0
- lightfast_mcp/core/__init__.py +14 -0
- lightfast_mcp/core/base_server.py +205 -0
- lightfast_mcp/exceptions.py +55 -0
- lightfast_mcp/servers/__init__.py +1 -0
- lightfast_mcp/servers/blender/__init__.py +5 -0
- lightfast_mcp/servers/blender/server.py +358 -0
- lightfast_mcp/servers/blender_mcp_server.py +82 -0
- lightfast_mcp/servers/mock/__init__.py +5 -0
- lightfast_mcp/servers/mock/server.py +101 -0
- lightfast_mcp/servers/mock/tools.py +161 -0
- lightfast_mcp/servers/mock_server.py +78 -0
- lightfast_mcp/utils/__init__.py +1 -0
- lightfast_mcp/utils/logging_utils.py +69 -0
- mseep_lightfast_mcp-0.0.1.dist-info/METADATA +36 -0
- mseep_lightfast_mcp-0.0.1.dist-info/RECORD +43 -0
- mseep_lightfast_mcp-0.0.1.dist-info/WHEEL +5 -0
- mseep_lightfast_mcp-0.0.1.dist-info/entry_points.txt +7 -0
- mseep_lightfast_mcp-0.0.1.dist-info/licenses/LICENSE +21 -0
- mseep_lightfast_mcp-0.0.1.dist-info/top_level.txt +3 -0
- tools/__init__.py +46 -0
- tools/ai/__init__.py +8 -0
- tools/ai/conversation_cli.py +345 -0
- tools/ai/conversation_client.py +399 -0
- tools/ai/conversation_session.py +342 -0
- tools/ai/providers/__init__.py +11 -0
- tools/ai/providers/base_provider.py +64 -0
- tools/ai/providers/claude_provider.py +200 -0
- tools/ai/providers/openai_provider.py +204 -0
- tools/ai/tool_executor.py +257 -0
- tools/common/__init__.py +99 -0
- tools/common/async_utils.py +419 -0
- tools/common/errors.py +222 -0
- tools/common/logging.py +252 -0
- tools/common/types.py +130 -0
- tools/orchestration/__init__.py +15 -0
- tools/orchestration/cli.py +320 -0
- tools/orchestration/config_loader.py +348 -0
- tools/orchestration/server_orchestrator.py +466 -0
- tools/orchestration/server_registry.py +187 -0
- tools/orchestration/server_selector.py +242 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Server selector for choosing which MCP servers to start."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from lightfast_mcp.core.base_server import ServerConfig
|
|
6
|
+
from lightfast_mcp.utils.logging_utils import get_logger
|
|
7
|
+
|
|
8
|
+
from .config_loader import ConfigLoader
|
|
9
|
+
from .server_registry import get_registry
|
|
10
|
+
|
|
11
|
+
logger = get_logger("ServerSelector")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ServerSelector:
|
|
15
|
+
"""Interactive server selector for choosing which servers to start."""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.registry = get_registry()
|
|
19
|
+
self.config_loader = ConfigLoader()
|
|
20
|
+
self.available_configs: list[ServerConfig] = []
|
|
21
|
+
self.selected_configs: list[ServerConfig] = []
|
|
22
|
+
|
|
23
|
+
def load_available_servers(
|
|
24
|
+
self, config_file: str | None = None
|
|
25
|
+
) -> list[ServerConfig]:
|
|
26
|
+
"""Load available server configurations."""
|
|
27
|
+
self.available_configs = self.config_loader.load_servers_config(config_file)
|
|
28
|
+
logger.info(f"Loaded {len(self.available_configs)} server configurations")
|
|
29
|
+
return self.available_configs
|
|
30
|
+
|
|
31
|
+
def get_server_info(self) -> dict[str, dict[str, Any]]:
|
|
32
|
+
"""Get detailed information about available server types."""
|
|
33
|
+
return self.registry.get_server_info()
|
|
34
|
+
|
|
35
|
+
def select_servers_interactive(self) -> list[ServerConfig]:
|
|
36
|
+
"""Interactive server selection via console."""
|
|
37
|
+
if not self.available_configs:
|
|
38
|
+
print("[ERROR] No server configurations available.")
|
|
39
|
+
print(
|
|
40
|
+
" Create a configuration file first or check your config directory."
|
|
41
|
+
)
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
print("[SELECT] Lightfast MCP Server Selection")
|
|
45
|
+
print("=" * 50)
|
|
46
|
+
print("Available servers:")
|
|
47
|
+
|
|
48
|
+
# Display available servers
|
|
49
|
+
for i, config in enumerate(self.available_configs, 1):
|
|
50
|
+
server_type = config.config.get("type", "unknown")
|
|
51
|
+
status = "[OK]" if self._check_server_requirements(config) else "[WARN]"
|
|
52
|
+
print(
|
|
53
|
+
f" {i}. {status} {config.name} ({server_type}) - {config.description}"
|
|
54
|
+
)
|
|
55
|
+
if not self._check_server_requirements(config):
|
|
56
|
+
issues = self._get_requirement_issues(config)
|
|
57
|
+
for issue in issues:
|
|
58
|
+
print(f" • {issue}")
|
|
59
|
+
|
|
60
|
+
print()
|
|
61
|
+
print(
|
|
62
|
+
"Enter server numbers to start (comma-separated), 'all' for all servers, or 'none' to cancel:"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
selection = input("Selection: ").strip()
|
|
67
|
+
|
|
68
|
+
if selection.lower() == "none":
|
|
69
|
+
return []
|
|
70
|
+
|
|
71
|
+
if selection.lower() == "all":
|
|
72
|
+
self.selected_configs = self.available_configs.copy()
|
|
73
|
+
else:
|
|
74
|
+
# Parse comma-separated numbers
|
|
75
|
+
selected_indices = []
|
|
76
|
+
for part in selection.split(","):
|
|
77
|
+
try:
|
|
78
|
+
index = int(part.strip()) - 1 # Convert to 0-based
|
|
79
|
+
if 0 <= index < len(self.available_configs):
|
|
80
|
+
selected_indices.append(index)
|
|
81
|
+
else:
|
|
82
|
+
print(f"[WARN] Invalid selection: {part.strip()}")
|
|
83
|
+
except ValueError:
|
|
84
|
+
print(f"[WARN] Invalid number: {part.strip()}")
|
|
85
|
+
|
|
86
|
+
self.selected_configs = [
|
|
87
|
+
self.available_configs[i] for i in selected_indices
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
print(f"\n[OK] Selected {len(self.selected_configs)} servers:")
|
|
91
|
+
for config in self.selected_configs:
|
|
92
|
+
print(f" • {config.name} ({config.config.get('type', 'unknown')})")
|
|
93
|
+
|
|
94
|
+
return self.selected_configs
|
|
95
|
+
|
|
96
|
+
except KeyboardInterrupt:
|
|
97
|
+
print("\n[ERROR] Selection cancelled.")
|
|
98
|
+
return []
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"[ERROR] Error during selection: {e}")
|
|
101
|
+
return []
|
|
102
|
+
|
|
103
|
+
def select_servers_by_names(self, server_names: list[str]) -> list[ServerConfig]:
|
|
104
|
+
"""Select servers by their names."""
|
|
105
|
+
selected = []
|
|
106
|
+
for name in server_names:
|
|
107
|
+
config = self.find_server_config(name)
|
|
108
|
+
if config:
|
|
109
|
+
selected.append(config)
|
|
110
|
+
else:
|
|
111
|
+
logger.warning(f"Server configuration not found: {name}")
|
|
112
|
+
|
|
113
|
+
self.selected_configs = selected
|
|
114
|
+
return selected
|
|
115
|
+
|
|
116
|
+
def select_servers_by_type(self, server_types: list[str]) -> list[ServerConfig]:
|
|
117
|
+
"""Select servers by their types."""
|
|
118
|
+
selected = []
|
|
119
|
+
for config in self.available_configs:
|
|
120
|
+
server_type = config.config.get("type", "unknown")
|
|
121
|
+
if server_type in server_types:
|
|
122
|
+
selected.append(config)
|
|
123
|
+
|
|
124
|
+
self.selected_configs = selected
|
|
125
|
+
return selected
|
|
126
|
+
|
|
127
|
+
def find_server_config(self, name: str) -> ServerConfig | None:
|
|
128
|
+
"""Find a server configuration by name."""
|
|
129
|
+
for config in self.available_configs:
|
|
130
|
+
if config.name == name:
|
|
131
|
+
return config
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
def _check_server_requirements(self, config: ServerConfig) -> bool:
|
|
135
|
+
"""Check if server requirements are met."""
|
|
136
|
+
server_type = config.config.get("type", "unknown")
|
|
137
|
+
|
|
138
|
+
# Check if server type is available
|
|
139
|
+
server_class = self.registry.get_server_class(server_type)
|
|
140
|
+
if not server_class:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
# Basic validation
|
|
144
|
+
is_valid, _ = self.registry.validate_server_config(server_type, config)
|
|
145
|
+
return is_valid
|
|
146
|
+
|
|
147
|
+
def _get_requirement_issues(self, config: ServerConfig) -> list[str]:
|
|
148
|
+
"""Get list of requirement issues for a server."""
|
|
149
|
+
issues = []
|
|
150
|
+
server_type = config.config.get("type", "unknown")
|
|
151
|
+
|
|
152
|
+
# Check if server type exists
|
|
153
|
+
server_class = self.registry.get_server_class(server_type)
|
|
154
|
+
if not server_class:
|
|
155
|
+
issues.append(f"Server type '{server_type}' not found")
|
|
156
|
+
return issues
|
|
157
|
+
|
|
158
|
+
# Check validation
|
|
159
|
+
is_valid, error_msg = self.registry.validate_server_config(server_type, config)
|
|
160
|
+
if not is_valid:
|
|
161
|
+
issues.append(error_msg)
|
|
162
|
+
|
|
163
|
+
# Check dependencies and required apps
|
|
164
|
+
server_info = self.registry.get_server_info().get(server_type, {})
|
|
165
|
+
|
|
166
|
+
required_deps = server_info.get("required_dependencies", [])
|
|
167
|
+
for dep in required_deps:
|
|
168
|
+
try:
|
|
169
|
+
__import__(dep)
|
|
170
|
+
except ImportError:
|
|
171
|
+
issues.append(f"Missing dependency: {dep}")
|
|
172
|
+
|
|
173
|
+
required_apps = server_info.get("required_apps", [])
|
|
174
|
+
for app in required_apps:
|
|
175
|
+
issues.append(f"Requires {app} to be running")
|
|
176
|
+
|
|
177
|
+
return issues
|
|
178
|
+
|
|
179
|
+
def display_selection_summary(self):
|
|
180
|
+
"""Display a summary of the current selection."""
|
|
181
|
+
if not self.selected_configs:
|
|
182
|
+
print("No servers selected.")
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
print(f"\n[SUMMARY] Selected Servers ({len(self.selected_configs)}):")
|
|
186
|
+
print("-" * 40)
|
|
187
|
+
|
|
188
|
+
for config in self.selected_configs:
|
|
189
|
+
server_type = config.config.get("type", "unknown")
|
|
190
|
+
transport = config.transport
|
|
191
|
+
|
|
192
|
+
if transport in ["http", "streamable-http"]:
|
|
193
|
+
url = f"http://{config.host}:{config.port}{config.path}"
|
|
194
|
+
print(f"• {config.name} ({server_type}) - {url}")
|
|
195
|
+
else:
|
|
196
|
+
print(f"• {config.name} ({server_type}) - {transport}")
|
|
197
|
+
|
|
198
|
+
print()
|
|
199
|
+
|
|
200
|
+
def get_selected_servers(self) -> list[ServerConfig]:
|
|
201
|
+
"""Get the currently selected server configurations."""
|
|
202
|
+
return self.selected_configs.copy()
|
|
203
|
+
|
|
204
|
+
def clear_selection(self):
|
|
205
|
+
"""Clear the current selection."""
|
|
206
|
+
self.selected_configs = []
|
|
207
|
+
|
|
208
|
+
def create_sample_configuration(self, filename: str = "servers.yaml") -> bool:
|
|
209
|
+
"""Create a sample configuration file."""
|
|
210
|
+
success = self.config_loader.create_sample_config(filename)
|
|
211
|
+
if success:
|
|
212
|
+
print(f"[OK] Created sample configuration: {filename}")
|
|
213
|
+
print(" Edit this file to customize your server settings.")
|
|
214
|
+
else:
|
|
215
|
+
print(f"[ERROR] Failed to create sample configuration: {filename}")
|
|
216
|
+
return success
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def interactive_server_selection(config_file: str | None = None) -> list[ServerConfig]:
|
|
220
|
+
"""Convenience function for interactive server selection."""
|
|
221
|
+
selector = ServerSelector()
|
|
222
|
+
|
|
223
|
+
# Load available servers
|
|
224
|
+
configs = selector.load_available_servers(config_file)
|
|
225
|
+
|
|
226
|
+
if not configs:
|
|
227
|
+
print("[CONFIG] Would you like to create a sample configuration file? (y/n)")
|
|
228
|
+
try:
|
|
229
|
+
create_sample = input().strip().lower()
|
|
230
|
+
if create_sample in ["y", "yes"] and selector.create_sample_configuration():
|
|
231
|
+
print("Edit the configuration file and run the command again.")
|
|
232
|
+
except KeyboardInterrupt:
|
|
233
|
+
pass
|
|
234
|
+
return []
|
|
235
|
+
|
|
236
|
+
# Interactive selection
|
|
237
|
+
selected = selector.select_servers_interactive()
|
|
238
|
+
|
|
239
|
+
# Display summary
|
|
240
|
+
selector.display_selection_summary()
|
|
241
|
+
|
|
242
|
+
return selected
|