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,345 @@
1
+ """CLI interface for the new ConversationClient."""
2
+
3
+ import asyncio
4
+ import os
5
+ import signal
6
+ import sys
7
+ from typing import Optional
8
+
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.markdown import Markdown
12
+ from rich.panel import Panel
13
+ from rich.progress import Progress, SpinnerColumn, TextColumn
14
+
15
+ from tools.common import get_logger
16
+ from tools.orchestration.config_loader import load_server_configs
17
+
18
+ from .conversation_client import ConversationClient, create_conversation_client
19
+
20
+ app = typer.Typer(help="Conversation client CLI using the new architecture")
21
+ console = Console()
22
+ logger = get_logger("ConversationCLI")
23
+
24
+ # Global client for signal handling
25
+ current_client: Optional[ConversationClient] = None
26
+
27
+
28
+ def handle_interrupt(signum, frame):
29
+ """Handle Ctrl+C gracefully."""
30
+ if current_client:
31
+ console.print("\n[yellow]Shutting down gracefully...[/yellow]")
32
+ asyncio.create_task(current_client.disconnect_from_servers())
33
+ sys.exit(0)
34
+
35
+
36
+ signal.signal(signal.SIGINT, handle_interrupt)
37
+
38
+
39
+ def print_step_info(step) -> None:
40
+ """Print information about a completed conversation step."""
41
+ step_title = f"Step {step.step_number + 1}"
42
+
43
+ if step.text and step.tool_calls:
44
+ step_title += " (Text + Tool Calls)"
45
+ elif step.tool_calls:
46
+ step_title += " (Tool Calls Only)"
47
+ elif step.text:
48
+ step_title += " (Text Only)"
49
+
50
+ # Print step header
51
+ console.print(f"\n[bold blue]{step_title}[/bold blue]")
52
+
53
+ # Print text if present
54
+ if step.text:
55
+ console.print(Panel(Markdown(step.text), title="Response", border_style="blue"))
56
+
57
+ # Print tool calls and results
58
+ if step.tool_calls:
59
+ for i, tool_call in enumerate(step.tool_calls):
60
+ tool_title = f"Tool Call {i + 1}: {tool_call.tool_name}"
61
+
62
+ # Find corresponding result
63
+ result = None
64
+ for tool_result in step.tool_results:
65
+ if tool_result.id == tool_call.id:
66
+ result = tool_result
67
+ break
68
+
69
+ # Format tool call info
70
+ info_parts = [f"**Tool:** {tool_call.tool_name}"]
71
+ if tool_call.server_name:
72
+ info_parts.append(f"**Server:** {tool_call.server_name}")
73
+ if tool_call.arguments:
74
+ info_parts.append(f"**Arguments:** `{tool_call.arguments}`")
75
+
76
+ if result:
77
+ if result.is_success:
78
+ info_parts.append(f"**Result:** {result.result}")
79
+ border_style = "green"
80
+ elif result.is_error:
81
+ info_parts.append(f"**Error:** {result.error}")
82
+ border_style = "red"
83
+ else:
84
+ info_parts.append("**Status:** Pending")
85
+ border_style = "yellow"
86
+ else:
87
+ info_parts.append("**Status:** No result")
88
+ border_style = "yellow"
89
+
90
+ console.print(
91
+ Panel(
92
+ "\n".join(info_parts), title=tool_title, border_style=border_style
93
+ )
94
+ )
95
+
96
+
97
+ @app.command()
98
+ def chat(
99
+ config_path: str = typer.Option(
100
+ "config/servers.yaml",
101
+ "--config",
102
+ "-c",
103
+ help="Path to server configuration file",
104
+ ),
105
+ ai_provider: str = typer.Option(
106
+ "claude", "--provider", "-p", help="AI provider (claude or openai)"
107
+ ),
108
+ max_steps: int = typer.Option(
109
+ None,
110
+ "--max-steps",
111
+ "-s",
112
+ help="Maximum number of steps (overrides environment variable)",
113
+ ),
114
+ api_key: Optional[str] = typer.Option(
115
+ None, "--api-key", "-k", help="API key (optional, uses environment variables)"
116
+ ),
117
+ ):
118
+ """Start an interactive chat session with AI and connected MCP servers."""
119
+ asyncio.run(async_chat(config_path, ai_provider, max_steps, api_key))
120
+
121
+
122
+ async def async_chat(
123
+ config_path: str, ai_provider: str, max_steps: Optional[int], api_key: Optional[str]
124
+ ):
125
+ """Async chat implementation."""
126
+ global current_client
127
+
128
+ try:
129
+ # Load server configuration
130
+ servers = load_server_configs(config_path)
131
+ if not servers:
132
+ console.print(
133
+ "[red]No servers configured. Please check your configuration file.[/red]"
134
+ )
135
+ return
136
+
137
+ # Get max_steps from environment if not provided
138
+ if max_steps is None:
139
+ max_steps = int(os.getenv("LIGHTFAST_MAX_STEPS", "5"))
140
+
141
+ console.print("[green]Loading servers and connecting...[/green]")
142
+
143
+ # Create and connect client
144
+ with Progress(
145
+ SpinnerColumn(),
146
+ TextColumn("[progress.description]{task.description}"),
147
+ console=console,
148
+ ) as progress:
149
+ task = progress.add_task("Connecting to servers...", total=None)
150
+
151
+ client_result = await create_conversation_client(
152
+ servers=servers,
153
+ ai_provider=ai_provider,
154
+ api_key=api_key,
155
+ max_steps=max_steps,
156
+ )
157
+
158
+ if not client_result.is_success:
159
+ console.print(
160
+ f"[red]Failed to create client: {client_result.error}[/red]"
161
+ )
162
+ return
163
+
164
+ current_client = client_result.data
165
+ progress.update(task, description="Connected!")
166
+
167
+ # Display connected servers and tools
168
+ if current_client is not None:
169
+ connected_servers = current_client.get_connected_servers()
170
+ if connected_servers:
171
+ console.print(
172
+ f"\n[green][OK] Connected to {len(connected_servers)} servers:[/green]"
173
+ )
174
+ tools_by_server = current_client.get_available_tools()
175
+ for server in connected_servers:
176
+ tools = tools_by_server.get(server, [])
177
+ console.print(f" • {server}: {len(tools)} tools")
178
+ else:
179
+ console.print(
180
+ "[yellow]Warning: No servers connected successfully[/yellow]"
181
+ )
182
+ else:
183
+ console.print("[red]Error: Client is not available[/red]")
184
+
185
+ console.print(f"\n[blue]AI Provider:[/blue] {ai_provider}")
186
+ console.print(f"[blue]Max Steps:[/blue] {max_steps}")
187
+ console.print(
188
+ "\n[green]Chat started! Type 'quit' or 'exit' to end the session.[/green]"
189
+ )
190
+ console.print("[dim]Use Ctrl+C to exit gracefully.[/dim]\n")
191
+
192
+ # Interactive chat loop
193
+ while True:
194
+ try:
195
+ # Get user input
196
+ user_input = console.input("[bold cyan]You:[/bold cyan] ").strip()
197
+
198
+ if user_input.lower() in ["quit", "exit", "q"]:
199
+ break
200
+
201
+ if not user_input:
202
+ continue
203
+
204
+ console.print("\n[yellow]🤖 Processing...[/yellow]")
205
+
206
+ # Send message and get result
207
+ if current_client is not None:
208
+ chat_result = await current_client.chat(user_input)
209
+
210
+ if not chat_result.is_success:
211
+ console.print(f"[red]Error: {chat_result.error}[/red]")
212
+ continue
213
+
214
+ conversation_result = chat_result.data
215
+
216
+ # Display the steps
217
+ for step in conversation_result.steps:
218
+ print_step_info(step)
219
+
220
+ console.print("\n" + "=" * 50 + "\n")
221
+ else:
222
+ console.print("[red]Error: Client is not available[/red]")
223
+
224
+ except KeyboardInterrupt:
225
+ break
226
+ except Exception as e:
227
+ console.print(f"[red]Error during chat: {e}[/red]")
228
+ logger.error(f"Chat error: {e}")
229
+
230
+ except Exception as e:
231
+ console.print(f"[red]Failed to start chat: {e}[/red]")
232
+ logger.error(f"Setup error: {e}")
233
+
234
+ finally:
235
+ if current_client:
236
+ console.print("[yellow]Disconnecting from servers...[/yellow]")
237
+ await current_client.disconnect_from_servers()
238
+ console.print("[green]Disconnected. Goodbye![/green]")
239
+
240
+
241
+ @app.command()
242
+ def test(
243
+ config_path: str = typer.Option(
244
+ "config/servers.yaml",
245
+ "--config",
246
+ "-c",
247
+ help="Path to server configuration file",
248
+ ),
249
+ ai_provider: str = typer.Option(
250
+ "claude", "--provider", "-p", help="AI provider (claude or openai)"
251
+ ),
252
+ max_steps: int = typer.Option(
253
+ 3, "--max-steps", "-s", help="Maximum number of steps for test"
254
+ ),
255
+ message: str = typer.Option(
256
+ "Hello! What tools do you have available?",
257
+ "--message",
258
+ "-m",
259
+ help="Test message to send",
260
+ ),
261
+ ):
262
+ """Test the conversation client with a single message."""
263
+ asyncio.run(async_test(config_path, ai_provider, max_steps, message))
264
+
265
+
266
+ async def async_test(config_path: str, ai_provider: str, max_steps: int, message: str):
267
+ """Async test implementation."""
268
+ global current_client
269
+
270
+ try:
271
+ # Load server configuration
272
+ servers = load_server_configs(config_path)
273
+ if not servers:
274
+ console.print("[red]No servers configured.[/red]")
275
+ return
276
+
277
+ console.print("[green]Testing conversation client...[/green]")
278
+
279
+ # Create and connect client
280
+ client_result = await create_conversation_client(
281
+ servers=servers,
282
+ ai_provider=ai_provider,
283
+ max_steps=max_steps,
284
+ )
285
+
286
+ if not client_result.is_success:
287
+ console.print(f"[red]Failed to create client: {client_result.error}[/red]")
288
+ return
289
+
290
+ current_client = client_result.data
291
+
292
+ # Display status
293
+ if current_client is not None:
294
+ connected_servers = current_client.get_connected_servers()
295
+ console.print(f"Connected servers: {connected_servers}")
296
+
297
+ tools_by_server = current_client.get_available_tools()
298
+ console.print(f"Available tools: {tools_by_server}")
299
+ else:
300
+ console.print("[red]Error: Client is not available[/red]")
301
+ return
302
+
303
+ # Send test message
304
+ console.print(f"\n[cyan]Sending message:[/cyan] {message}")
305
+
306
+ if current_client is not None:
307
+ chat_result = await current_client.chat(message)
308
+
309
+ if not chat_result.is_success:
310
+ console.print(f"[red]Chat failed: {chat_result.error}[/red]")
311
+ return
312
+
313
+ conversation_result = chat_result.data
314
+
315
+ # Display results
316
+ console.print(
317
+ f"\n[green]Completed {len(conversation_result.steps)} steps:[/green]"
318
+ )
319
+ for step in conversation_result.steps:
320
+ print_step_info(step)
321
+
322
+ # Display summary
323
+ console.print("\n[blue]Conversation Summary:[/blue]")
324
+ console.print(
325
+ f" • Total tool calls: {conversation_result.total_tool_calls}"
326
+ )
327
+ console.print(
328
+ f" • Successful: {conversation_result.successful_tool_calls}"
329
+ )
330
+ console.print(f" • Failed: {conversation_result.failed_tool_calls}")
331
+ console.print(f" • Success rate: {conversation_result.success_rate:.1%}")
332
+ else:
333
+ console.print("[red]Error: Client is not available[/red]")
334
+
335
+ except Exception as e:
336
+ console.print(f"[red]Test failed: {e}[/red]")
337
+ logger.error(f"Test error: {e}")
338
+
339
+ finally:
340
+ if current_client:
341
+ await current_client.disconnect_from_servers()
342
+
343
+
344
+ if __name__ == "__main__":
345
+ app()