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.
Files changed (43) hide show
  1. common/__init__.py +21 -0
  2. common/types.py +182 -0
  3. lightfast_mcp/__init__.py +50 -0
  4. lightfast_mcp/core/__init__.py +14 -0
  5. lightfast_mcp/core/base_server.py +205 -0
  6. lightfast_mcp/exceptions.py +55 -0
  7. lightfast_mcp/servers/__init__.py +1 -0
  8. lightfast_mcp/servers/blender/__init__.py +5 -0
  9. lightfast_mcp/servers/blender/server.py +358 -0
  10. lightfast_mcp/servers/blender_mcp_server.py +82 -0
  11. lightfast_mcp/servers/mock/__init__.py +5 -0
  12. lightfast_mcp/servers/mock/server.py +101 -0
  13. lightfast_mcp/servers/mock/tools.py +161 -0
  14. lightfast_mcp/servers/mock_server.py +78 -0
  15. lightfast_mcp/utils/__init__.py +1 -0
  16. lightfast_mcp/utils/logging_utils.py +69 -0
  17. mseep_lightfast_mcp-0.0.1.dist-info/METADATA +36 -0
  18. mseep_lightfast_mcp-0.0.1.dist-info/RECORD +43 -0
  19. mseep_lightfast_mcp-0.0.1.dist-info/WHEEL +5 -0
  20. mseep_lightfast_mcp-0.0.1.dist-info/entry_points.txt +7 -0
  21. mseep_lightfast_mcp-0.0.1.dist-info/licenses/LICENSE +21 -0
  22. mseep_lightfast_mcp-0.0.1.dist-info/top_level.txt +3 -0
  23. tools/__init__.py +46 -0
  24. tools/ai/__init__.py +8 -0
  25. tools/ai/conversation_cli.py +345 -0
  26. tools/ai/conversation_client.py +399 -0
  27. tools/ai/conversation_session.py +342 -0
  28. tools/ai/providers/__init__.py +11 -0
  29. tools/ai/providers/base_provider.py +64 -0
  30. tools/ai/providers/claude_provider.py +200 -0
  31. tools/ai/providers/openai_provider.py +204 -0
  32. tools/ai/tool_executor.py +257 -0
  33. tools/common/__init__.py +99 -0
  34. tools/common/async_utils.py +419 -0
  35. tools/common/errors.py +222 -0
  36. tools/common/logging.py +252 -0
  37. tools/common/types.py +130 -0
  38. tools/orchestration/__init__.py +15 -0
  39. tools/orchestration/cli.py +320 -0
  40. tools/orchestration/config_loader.py +348 -0
  41. tools/orchestration/server_orchestrator.py +466 -0
  42. tools/orchestration/server_registry.py +187 -0
  43. 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