code-puppy 0.0.135__py3-none-any.whl → 0.0.137__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.
- code_puppy/agent.py +15 -17
- code_puppy/agents/agent_manager.py +320 -9
- code_puppy/agents/base_agent.py +58 -2
- code_puppy/agents/runtime_manager.py +68 -42
- code_puppy/command_line/command_handler.py +82 -33
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/add_command.py +183 -0
- code_puppy/command_line/mcp/base.py +35 -0
- code_puppy/command_line/mcp/handler.py +133 -0
- code_puppy/command_line/mcp/help_command.py +146 -0
- code_puppy/command_line/mcp/install_command.py +176 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +126 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +92 -0
- code_puppy/command_line/mcp/search_command.py +117 -0
- code_puppy/command_line/mcp/start_all_command.py +126 -0
- code_puppy/command_line/mcp/start_command.py +98 -0
- code_puppy/command_line/mcp/status_command.py +185 -0
- code_puppy/command_line/mcp/stop_all_command.py +109 -0
- code_puppy/command_line/mcp/stop_command.py +79 -0
- code_puppy/command_line/mcp/test_command.py +107 -0
- code_puppy/command_line/mcp/utils.py +129 -0
- code_puppy/command_line/mcp/wizard_utils.py +259 -0
- code_puppy/command_line/model_picker_completion.py +21 -4
- code_puppy/command_line/prompt_toolkit_completion.py +9 -0
- code_puppy/config.py +5 -5
- code_puppy/main.py +23 -17
- code_puppy/mcp/__init__.py +42 -16
- code_puppy/mcp/async_lifecycle.py +51 -49
- code_puppy/mcp/blocking_startup.py +125 -113
- code_puppy/mcp/captured_stdio_server.py +63 -70
- code_puppy/mcp/circuit_breaker.py +63 -47
- code_puppy/mcp/config_wizard.py +169 -136
- code_puppy/mcp/dashboard.py +79 -71
- code_puppy/mcp/error_isolation.py +147 -100
- code_puppy/mcp/examples/retry_example.py +55 -42
- code_puppy/mcp/health_monitor.py +152 -141
- code_puppy/mcp/managed_server.py +100 -93
- code_puppy/mcp/manager.py +168 -156
- code_puppy/mcp/registry.py +148 -110
- code_puppy/mcp/retry_manager.py +63 -61
- code_puppy/mcp/server_registry_catalog.py +271 -225
- code_puppy/mcp/status_tracker.py +80 -80
- code_puppy/mcp/system_tools.py +47 -52
- code_puppy/messaging/message_queue.py +20 -13
- code_puppy/messaging/renderers.py +30 -15
- code_puppy/state_management.py +103 -0
- code_puppy/tui/app.py +64 -7
- code_puppy/tui/components/chat_view.py +3 -3
- code_puppy/tui/components/human_input_modal.py +12 -8
- code_puppy/tui/screens/__init__.py +2 -2
- code_puppy/tui/screens/mcp_install_wizard.py +208 -179
- code_puppy/tui/tests/test_agent_command.py +3 -3
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/METADATA +1 -1
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/RECORD +60 -42
- code_puppy/command_line/mcp_commands.py +0 -1789
- {code_puppy-0.0.135.data → code_puppy-0.0.137.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Remove Command - Removes an MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
from code_puppy.messaging import emit_info
|
|
11
|
+
|
|
12
|
+
from .base import MCPCommandBase
|
|
13
|
+
from .utils import find_server_id_by_name, suggest_similar_servers
|
|
14
|
+
|
|
15
|
+
# Configure logging
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RemoveCommand(MCPCommandBase):
|
|
20
|
+
"""
|
|
21
|
+
Command handler for removing MCP servers.
|
|
22
|
+
|
|
23
|
+
Removes a specific MCP server from the manager and configuration.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Remove an MCP server.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
args: Command arguments, expects [server_name]
|
|
32
|
+
group_id: Optional message group ID for grouping related messages
|
|
33
|
+
"""
|
|
34
|
+
if group_id is None:
|
|
35
|
+
group_id = self.generate_group_id()
|
|
36
|
+
|
|
37
|
+
if not args:
|
|
38
|
+
emit_info("Usage: /mcp remove <server_name>", message_group=group_id)
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
server_name = args[0]
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
# Find server by name
|
|
45
|
+
server_id = find_server_id_by_name(self.manager, server_name)
|
|
46
|
+
if not server_id:
|
|
47
|
+
emit_info(f"Server '{server_name}' not found", message_group=group_id)
|
|
48
|
+
suggest_similar_servers(self.manager, server_name, group_id=group_id)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
# Actually remove the server
|
|
52
|
+
success = self.manager.remove_server(server_id)
|
|
53
|
+
|
|
54
|
+
if success:
|
|
55
|
+
emit_info(f"✓ Removed server: {server_name}", message_group=group_id)
|
|
56
|
+
|
|
57
|
+
# Also remove from mcp_servers.json
|
|
58
|
+
from code_puppy.config import MCP_SERVERS_FILE
|
|
59
|
+
|
|
60
|
+
if os.path.exists(MCP_SERVERS_FILE):
|
|
61
|
+
try:
|
|
62
|
+
with open(MCP_SERVERS_FILE, "r") as f:
|
|
63
|
+
data = json.load(f)
|
|
64
|
+
servers = data.get("mcp_servers", {})
|
|
65
|
+
|
|
66
|
+
# Remove the server if it exists
|
|
67
|
+
if server_name in servers:
|
|
68
|
+
del servers[server_name]
|
|
69
|
+
|
|
70
|
+
# Save back
|
|
71
|
+
with open(MCP_SERVERS_FILE, "w") as f:
|
|
72
|
+
json.dump(data, f, indent=2)
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.warning(f"Could not update mcp_servers.json: {e}")
|
|
75
|
+
else:
|
|
76
|
+
emit_info(
|
|
77
|
+
f"✗ Failed to remove server: {server_name}", message_group=group_id
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"Error removing server '{server_name}': {e}")
|
|
82
|
+
emit_info(f"[red]Error removing server: {e}[/red]", message_group=group_id)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Restart Command - Restarts a specific MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from code_puppy.messaging import emit_info
|
|
9
|
+
|
|
10
|
+
from .base import MCPCommandBase
|
|
11
|
+
from .utils import find_server_id_by_name, suggest_similar_servers
|
|
12
|
+
|
|
13
|
+
# Configure logging
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RestartCommand(MCPCommandBase):
|
|
18
|
+
"""
|
|
19
|
+
Command handler for restarting MCP servers.
|
|
20
|
+
|
|
21
|
+
Stops, reloads configuration, and starts a specific MCP server.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Restart a specific MCP server.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
args: Command arguments, expects [server_name]
|
|
30
|
+
group_id: Optional message group ID for grouping related messages
|
|
31
|
+
"""
|
|
32
|
+
if group_id is None:
|
|
33
|
+
group_id = self.generate_group_id()
|
|
34
|
+
|
|
35
|
+
if not args:
|
|
36
|
+
emit_info("Usage: /mcp restart <server_name>", message_group=group_id)
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
server_name = args[0]
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
# Find server by name
|
|
43
|
+
server_id = find_server_id_by_name(self.manager, server_name)
|
|
44
|
+
if not server_id:
|
|
45
|
+
emit_info(f"Server '{server_name}' not found", message_group=group_id)
|
|
46
|
+
suggest_similar_servers(self.manager, server_name, group_id=group_id)
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
# Stop the server first
|
|
50
|
+
emit_info(f"Stopping server: {server_name}", message_group=group_id)
|
|
51
|
+
self.manager.stop_server_sync(server_id)
|
|
52
|
+
|
|
53
|
+
# Then reload and start it
|
|
54
|
+
emit_info("Reloading configuration...", message_group=group_id)
|
|
55
|
+
reload_success = self.manager.reload_server(server_id)
|
|
56
|
+
|
|
57
|
+
if reload_success:
|
|
58
|
+
emit_info(f"Starting server: {server_name}", message_group=group_id)
|
|
59
|
+
start_success = self.manager.start_server_sync(server_id)
|
|
60
|
+
|
|
61
|
+
if start_success:
|
|
62
|
+
emit_info(
|
|
63
|
+
f"✓ Restarted server: {server_name}", message_group=group_id
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Reload the agent to pick up the server changes
|
|
67
|
+
try:
|
|
68
|
+
from code_puppy.agent import get_code_generation_agent
|
|
69
|
+
|
|
70
|
+
get_code_generation_agent(force_reload=True)
|
|
71
|
+
emit_info(
|
|
72
|
+
"[dim]Agent reloaded with updated servers[/dim]",
|
|
73
|
+
message_group=group_id,
|
|
74
|
+
)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
77
|
+
else:
|
|
78
|
+
emit_info(
|
|
79
|
+
f"✗ Failed to start server after reload: {server_name}",
|
|
80
|
+
message_group=group_id,
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
emit_info(
|
|
84
|
+
f"✗ Failed to reload server configuration: {server_name}",
|
|
85
|
+
message_group=group_id,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.error(f"Error restarting server '{server_name}': {e}")
|
|
90
|
+
emit_info(
|
|
91
|
+
f"[red]Failed to restart server: {e}[/red]", message_group=group_id
|
|
92
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Search Command - Searches for pre-configured MCP servers in the registry.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from code_puppy.messaging import emit_info, emit_system_message
|
|
11
|
+
|
|
12
|
+
from .base import MCPCommandBase
|
|
13
|
+
|
|
14
|
+
# Configure logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SearchCommand(MCPCommandBase):
|
|
19
|
+
"""
|
|
20
|
+
Command handler for searching MCP server registry.
|
|
21
|
+
|
|
22
|
+
Searches for pre-configured MCP servers with optional query terms.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Search for pre-configured MCP servers in the registry.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
args: Search query terms
|
|
31
|
+
group_id: Optional message group ID for grouping related messages
|
|
32
|
+
"""
|
|
33
|
+
if group_id is None:
|
|
34
|
+
group_id = self.generate_group_id()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
from code_puppy.mcp.server_registry_catalog import catalog
|
|
38
|
+
|
|
39
|
+
if not args:
|
|
40
|
+
# Show popular servers if no query
|
|
41
|
+
emit_info(
|
|
42
|
+
"[bold cyan]Popular MCP Servers:[/bold cyan]\n",
|
|
43
|
+
message_group=group_id,
|
|
44
|
+
)
|
|
45
|
+
servers = catalog.get_popular(15)
|
|
46
|
+
else:
|
|
47
|
+
query = " ".join(args)
|
|
48
|
+
emit_info(
|
|
49
|
+
f"[bold cyan]Searching for: {query}[/bold cyan]\n",
|
|
50
|
+
message_group=group_id,
|
|
51
|
+
)
|
|
52
|
+
servers = catalog.search(query)
|
|
53
|
+
|
|
54
|
+
if not servers:
|
|
55
|
+
emit_info(
|
|
56
|
+
"[yellow]No servers found matching your search[/yellow]",
|
|
57
|
+
message_group=group_id,
|
|
58
|
+
)
|
|
59
|
+
emit_info(
|
|
60
|
+
"Try: /mcp search database, /mcp search file, /mcp search git",
|
|
61
|
+
message_group=group_id,
|
|
62
|
+
)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# Create results table
|
|
66
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
67
|
+
table.add_column("ID", style="cyan", width=20)
|
|
68
|
+
table.add_column("Name", style="green")
|
|
69
|
+
table.add_column("Category", style="yellow")
|
|
70
|
+
table.add_column("Description", style="white")
|
|
71
|
+
table.add_column("Tags", style="dim")
|
|
72
|
+
|
|
73
|
+
for server in servers[:20]: # Limit to 20 results
|
|
74
|
+
tags = ", ".join(server.tags[:3]) # Show first 3 tags
|
|
75
|
+
if len(server.tags) > 3:
|
|
76
|
+
tags += "..."
|
|
77
|
+
|
|
78
|
+
# Add verified/popular indicators
|
|
79
|
+
indicators = []
|
|
80
|
+
if server.verified:
|
|
81
|
+
indicators.append("✓")
|
|
82
|
+
if server.popular:
|
|
83
|
+
indicators.append("⭐")
|
|
84
|
+
name_display = server.display_name
|
|
85
|
+
if indicators:
|
|
86
|
+
name_display += f" {''.join(indicators)}"
|
|
87
|
+
|
|
88
|
+
table.add_row(
|
|
89
|
+
server.id,
|
|
90
|
+
name_display,
|
|
91
|
+
server.category,
|
|
92
|
+
server.description[:50] + "..."
|
|
93
|
+
if len(server.description) > 50
|
|
94
|
+
else server.description,
|
|
95
|
+
tags,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# The first message established the group, subsequent messages will auto-group
|
|
99
|
+
emit_system_message(table, message_group=group_id)
|
|
100
|
+
emit_info("\n[dim]✓ = Verified ⭐ = Popular[/dim]", message_group=group_id)
|
|
101
|
+
emit_info(
|
|
102
|
+
"[yellow]To install:[/yellow] /mcp install <id>", message_group=group_id
|
|
103
|
+
)
|
|
104
|
+
emit_info(
|
|
105
|
+
"[yellow]For details:[/yellow] /mcp search <specific-term>",
|
|
106
|
+
message_group=group_id,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
except ImportError:
|
|
110
|
+
emit_info(
|
|
111
|
+
"[red]Server registry not available[/red]", message_group=group_id
|
|
112
|
+
)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"Error searching server registry: {e}")
|
|
115
|
+
emit_info(
|
|
116
|
+
f"[red]Error searching servers: {e}[/red]", message_group=group_id
|
|
117
|
+
)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Start All Command - Starts all registered MCP servers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from code_puppy.mcp.managed_server import ServerState
|
|
10
|
+
from code_puppy.messaging import emit_info
|
|
11
|
+
|
|
12
|
+
from .base import MCPCommandBase
|
|
13
|
+
|
|
14
|
+
# Configure logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StartAllCommand(MCPCommandBase):
|
|
19
|
+
"""
|
|
20
|
+
Command handler for starting all MCP servers.
|
|
21
|
+
|
|
22
|
+
Starts all registered MCP servers and provides a summary of results.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Start all registered MCP servers.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
args: Command arguments (unused)
|
|
31
|
+
group_id: Optional message group ID for grouping related messages
|
|
32
|
+
"""
|
|
33
|
+
if group_id is None:
|
|
34
|
+
group_id = self.generate_group_id()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
servers = self.manager.list_servers()
|
|
38
|
+
|
|
39
|
+
if not servers:
|
|
40
|
+
emit_info(
|
|
41
|
+
"[yellow]No servers registered[/yellow]", message_group=group_id
|
|
42
|
+
)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
started_count = 0
|
|
46
|
+
failed_count = 0
|
|
47
|
+
already_running = 0
|
|
48
|
+
|
|
49
|
+
emit_info(f"Starting {len(servers)} servers...", message_group=group_id)
|
|
50
|
+
|
|
51
|
+
for server_info in servers:
|
|
52
|
+
server_id = server_info.id
|
|
53
|
+
server_name = server_info.name
|
|
54
|
+
|
|
55
|
+
# Skip if already running
|
|
56
|
+
if server_info.state == ServerState.RUNNING:
|
|
57
|
+
already_running += 1
|
|
58
|
+
emit_info(
|
|
59
|
+
f" • {server_name}: already running", message_group=group_id
|
|
60
|
+
)
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
# Try to start the server
|
|
64
|
+
success = self.manager.start_server_sync(server_id)
|
|
65
|
+
|
|
66
|
+
if success:
|
|
67
|
+
started_count += 1
|
|
68
|
+
emit_info(
|
|
69
|
+
f" [green]✓ Started: {server_name}[/green]",
|
|
70
|
+
message_group=group_id,
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
failed_count += 1
|
|
74
|
+
emit_info(
|
|
75
|
+
f" [red]✗ Failed: {server_name}[/red]", message_group=group_id
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Summary
|
|
79
|
+
emit_info("", message_group=group_id)
|
|
80
|
+
if started_count > 0:
|
|
81
|
+
emit_info(
|
|
82
|
+
f"[green]Started {started_count} server(s)[/green]",
|
|
83
|
+
message_group=group_id,
|
|
84
|
+
)
|
|
85
|
+
if already_running > 0:
|
|
86
|
+
emit_info(
|
|
87
|
+
f"{already_running} server(s) already running",
|
|
88
|
+
message_group=group_id,
|
|
89
|
+
)
|
|
90
|
+
if failed_count > 0:
|
|
91
|
+
emit_info(
|
|
92
|
+
f"[yellow]Failed to start {failed_count} server(s)[/yellow]",
|
|
93
|
+
message_group=group_id,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Reload agent if any servers were started
|
|
97
|
+
if started_count > 0:
|
|
98
|
+
# Give async tasks a moment to complete before reloading agent
|
|
99
|
+
try:
|
|
100
|
+
import asyncio
|
|
101
|
+
|
|
102
|
+
asyncio.get_running_loop() # Check if in async context
|
|
103
|
+
# If we're in async context, wait a bit for servers to start
|
|
104
|
+
time.sleep(0.5) # Small delay to let async tasks progress
|
|
105
|
+
except RuntimeError:
|
|
106
|
+
pass # No async loop, servers will start when agent uses them
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
from code_puppy.agents.runtime_manager import (
|
|
110
|
+
get_runtime_agent_manager,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
manager = get_runtime_agent_manager()
|
|
114
|
+
manager.reload_agent()
|
|
115
|
+
emit_info(
|
|
116
|
+
"[dim]Agent reloaded with updated servers[/dim]",
|
|
117
|
+
message_group=group_id,
|
|
118
|
+
)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Error starting all servers: {e}")
|
|
124
|
+
emit_info(
|
|
125
|
+
f"[red]Failed to start servers: {e}[/red]", message_group=group_id
|
|
126
|
+
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Start Command - Starts a specific MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from code_puppy.messaging import emit_info
|
|
10
|
+
|
|
11
|
+
from .base import MCPCommandBase
|
|
12
|
+
from .utils import find_server_id_by_name, suggest_similar_servers
|
|
13
|
+
|
|
14
|
+
# Configure logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StartCommand(MCPCommandBase):
|
|
19
|
+
"""
|
|
20
|
+
Command handler for starting MCP servers.
|
|
21
|
+
|
|
22
|
+
Starts a specific MCP server by name and reloads the agent.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Start a specific MCP server.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
args: Command arguments, expects [server_name]
|
|
31
|
+
group_id: Optional message group ID for grouping related messages
|
|
32
|
+
"""
|
|
33
|
+
if group_id is None:
|
|
34
|
+
group_id = self.generate_group_id()
|
|
35
|
+
|
|
36
|
+
if not args:
|
|
37
|
+
emit_info(
|
|
38
|
+
"[yellow]Usage: /mcp start <server_name>[/yellow]",
|
|
39
|
+
message_group=group_id,
|
|
40
|
+
)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
server_name = args[0]
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# Find server by name
|
|
47
|
+
server_id = find_server_id_by_name(self.manager, server_name)
|
|
48
|
+
if not server_id:
|
|
49
|
+
emit_info(
|
|
50
|
+
f"[red]Server '{server_name}' not found[/red]",
|
|
51
|
+
message_group=group_id,
|
|
52
|
+
)
|
|
53
|
+
suggest_similar_servers(self.manager, server_name, group_id=group_id)
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
# Start the server (enable and start process)
|
|
57
|
+
success = self.manager.start_server_sync(server_id)
|
|
58
|
+
|
|
59
|
+
if success:
|
|
60
|
+
# This and subsequent messages will auto-group with the first message
|
|
61
|
+
emit_info(
|
|
62
|
+
f"[green]✓ Started server: {server_name}[/green]",
|
|
63
|
+
message_group=group_id,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Give async tasks a moment to complete
|
|
67
|
+
try:
|
|
68
|
+
import asyncio
|
|
69
|
+
|
|
70
|
+
asyncio.get_running_loop() # Check if in async context
|
|
71
|
+
# If we're in async context, wait a bit for server to start
|
|
72
|
+
time.sleep(0.5) # Small delay to let async tasks progress
|
|
73
|
+
except RuntimeError:
|
|
74
|
+
pass # No async loop, server will start when agent uses it
|
|
75
|
+
|
|
76
|
+
# Reload the agent to pick up the newly enabled server
|
|
77
|
+
try:
|
|
78
|
+
from code_puppy.agents.runtime_manager import (
|
|
79
|
+
get_runtime_agent_manager,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
manager = get_runtime_agent_manager()
|
|
83
|
+
manager.reload_agent()
|
|
84
|
+
emit_info(
|
|
85
|
+
"[dim]Agent reloaded with updated servers[/dim]",
|
|
86
|
+
message_group=group_id,
|
|
87
|
+
)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
90
|
+
else:
|
|
91
|
+
emit_info(
|
|
92
|
+
f"[red]✗ Failed to start server: {server_name}[/red]",
|
|
93
|
+
message_group=group_id,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Error starting server '{server_name}': {e}")
|
|
98
|
+
emit_info(f"[red]Failed to start server: {e}[/red]", message_group=group_id)
|