codepp 0.0.437__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/__init__.py +10 -0
- code_puppy/__main__.py +10 -0
- code_puppy/agents/__init__.py +31 -0
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +117 -0
- code_puppy/agents/agent_code_reviewer.py +90 -0
- code_puppy/agents/agent_cpp_reviewer.py +132 -0
- code_puppy/agents/agent_creator_agent.py +638 -0
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_helios.py +124 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +742 -0
- code_puppy/agents/agent_pack_leader.py +385 -0
- code_puppy/agents/agent_planning.py +165 -0
- code_puppy/agents/agent_python_programmer.py +169 -0
- code_puppy/agents/agent_python_reviewer.py +90 -0
- code_puppy/agents/agent_qa_expert.py +163 -0
- code_puppy/agents/agent_qa_kitten.py +208 -0
- code_puppy/agents/agent_scheduler.py +121 -0
- code_puppy/agents/agent_security_auditor.py +181 -0
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +166 -0
- code_puppy/agents/base_agent.py +2156 -0
- code_puppy/agents/event_stream_handler.py +348 -0
- code_puppy/agents/json_agent.py +202 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +327 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +453 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +75 -0
- code_puppy/api/routers/sessions.py +234 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +692 -0
- code_puppy/chatgpt_codex_client.py +338 -0
- code_puppy/claude_cache_client.py +672 -0
- code_puppy/cli_runner.py +1073 -0
- code_puppy/command_line/__init__.py +1 -0
- code_puppy/command_line/add_model_menu.py +1092 -0
- code_puppy/command_line/agent_menu.py +662 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +704 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +532 -0
- code_puppy/command_line/command_handler.py +293 -0
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +719 -0
- code_puppy/command_line/core_commands.py +867 -0
- code_puppy/command_line/diff_menu.py +865 -0
- code_puppy/command_line/file_path_completion.py +73 -0
- code_puppy/command_line/load_context_completion.py +52 -0
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/base.py +32 -0
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +138 -0
- code_puppy/command_line/mcp/help_command.py +147 -0
- code_puppy/command_line/mcp/install_command.py +214 -0
- code_puppy/command_line/mcp/install_menu.py +705 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +235 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +100 -0
- code_puppy/command_line/mcp/search_command.py +123 -0
- code_puppy/command_line/mcp/start_all_command.py +135 -0
- code_puppy/command_line/mcp/start_command.py +117 -0
- code_puppy/command_line/mcp/status_command.py +184 -0
- code_puppy/command_line/mcp/stop_all_command.py +112 -0
- code_puppy/command_line/mcp/stop_command.py +80 -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 +334 -0
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +197 -0
- code_puppy/command_line/model_settings_menu.py +932 -0
- code_puppy/command_line/motd.py +96 -0
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +342 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +846 -0
- code_puppy/command_line/session_commands.py +302 -0
- code_puppy/command_line/shell_passthrough.py +145 -0
- code_puppy/command_line/skills_completion.py +160 -0
- code_puppy/command_line/uc_menu.py +893 -0
- code_puppy/command_line/utils.py +93 -0
- code_puppy/command_line/wiggum_state.py +78 -0
- code_puppy/config.py +1770 -0
- code_puppy/error_logging.py +134 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +754 -0
- code_puppy/hook_engine/README.md +105 -0
- code_puppy/hook_engine/__init__.py +21 -0
- code_puppy/hook_engine/aliases.py +155 -0
- code_puppy/hook_engine/engine.py +221 -0
- code_puppy/hook_engine/executor.py +296 -0
- code_puppy/hook_engine/matcher.py +156 -0
- code_puppy/hook_engine/models.py +240 -0
- code_puppy/hook_engine/registry.py +106 -0
- code_puppy/hook_engine/validator.py +144 -0
- code_puppy/http_utils.py +361 -0
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +10 -0
- code_puppy/mcp_/__init__.py +66 -0
- code_puppy/mcp_/async_lifecycle.py +286 -0
- code_puppy/mcp_/blocking_startup.py +469 -0
- code_puppy/mcp_/captured_stdio_server.py +275 -0
- code_puppy/mcp_/circuit_breaker.py +290 -0
- code_puppy/mcp_/config_wizard.py +507 -0
- code_puppy/mcp_/dashboard.py +308 -0
- code_puppy/mcp_/error_isolation.py +407 -0
- code_puppy/mcp_/examples/retry_example.py +226 -0
- code_puppy/mcp_/health_monitor.py +589 -0
- code_puppy/mcp_/managed_server.py +428 -0
- code_puppy/mcp_/manager.py +807 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +451 -0
- code_puppy/mcp_/retry_manager.py +337 -0
- code_puppy/mcp_/server_registry_catalog.py +1126 -0
- code_puppy/mcp_/status_tracker.py +355 -0
- code_puppy/mcp_/system_tools.py +209 -0
- code_puppy/mcp_prompts/__init__.py +1 -0
- code_puppy/mcp_prompts/hook_creator.py +103 -0
- code_puppy/messaging/__init__.py +255 -0
- code_puppy/messaging/bus.py +613 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +361 -0
- code_puppy/messaging/messages.py +569 -0
- code_puppy/messaging/queue_console.py +271 -0
- code_puppy/messaging/renderers.py +311 -0
- code_puppy/messaging/rich_renderer.py +1158 -0
- code_puppy/messaging/spinner/__init__.py +83 -0
- code_puppy/messaging/spinner/console_spinner.py +240 -0
- code_puppy/messaging/spinner/spinner_base.py +95 -0
- code_puppy/messaging/subagent_console.py +460 -0
- code_puppy/model_factory.py +848 -0
- code_puppy/model_switching.py +63 -0
- code_puppy/model_utils.py +168 -0
- code_puppy/models.json +174 -0
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +186 -0
- code_puppy/plugins/agent_skills/__init__.py +22 -0
- code_puppy/plugins/agent_skills/config.py +175 -0
- code_puppy/plugins/agent_skills/discovery.py +136 -0
- code_puppy/plugins/agent_skills/downloader.py +392 -0
- code_puppy/plugins/agent_skills/installer.py +22 -0
- code_puppy/plugins/agent_skills/metadata.py +219 -0
- code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
- code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
- code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
- code_puppy/plugins/agent_skills/skills_menu.py +781 -0
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +133 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
- code_puppy/plugins/antigravity_oauth/storage.py +288 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +863 -0
- code_puppy/plugins/antigravity_oauth/utils.py +168 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +329 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
- code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy/plugins/claude_code_hooks/config.py +137 -0
- code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
- code_puppy/plugins/claude_code_oauth/config.py +52 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
- code_puppy/plugins/claude_code_oauth/utils.py +640 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/hook_creator/__init__.py +1 -0
- code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy/plugins/hook_manager/__init__.py +1 -0
- code_puppy/plugins/hook_manager/config.py +290 -0
- code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
- code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/scheduler/__init__.py +1 -0
- code_puppy/plugins/scheduler/register_callbacks.py +88 -0
- code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
- code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/plugins/synthetic_status/__init__.py +1 -0
- code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
- code_puppy/plugins/synthetic_status/status_api.py +147 -0
- code_puppy/plugins/universal_constructor/__init__.py +13 -0
- code_puppy/plugins/universal_constructor/models.py +138 -0
- code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
- code_puppy/plugins/universal_constructor/registry.py +302 -0
- code_puppy/plugins/universal_constructor/sandbox.py +584 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/pydantic_patches.py +356 -0
- code_puppy/reopenable_async_client.py +232 -0
- code_puppy/round_robin_model.py +150 -0
- code_puppy/scheduler/__init__.py +41 -0
- code_puppy/scheduler/__main__.py +9 -0
- code_puppy/scheduler/cli.py +118 -0
- code_puppy/scheduler/config.py +126 -0
- code_puppy/scheduler/daemon.py +280 -0
- code_puppy/scheduler/executor.py +155 -0
- code_puppy/scheduler/platform.py +19 -0
- code_puppy/scheduler/platform_unix.py +22 -0
- code_puppy/scheduler/platform_win.py +32 -0
- code_puppy/session_storage.py +338 -0
- code_puppy/status_display.py +257 -0
- code_puppy/summarization_agent.py +176 -0
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +501 -0
- code_puppy/tools/agent_tools.py +603 -0
- code_puppy/tools/ask_user_question/__init__.py +26 -0
- code_puppy/tools/ask_user_question/constants.py +73 -0
- code_puppy/tools/ask_user_question/demo_tui.py +55 -0
- code_puppy/tools/ask_user_question/handler.py +232 -0
- code_puppy/tools/ask_user_question/models.py +304 -0
- code_puppy/tools/ask_user_question/registration.py +26 -0
- code_puppy/tools/ask_user_question/renderers.py +309 -0
- code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
- code_puppy/tools/ask_user_question/theme.py +155 -0
- code_puppy/tools/ask_user_question/tui_loop.py +423 -0
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +378 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +534 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +1346 -0
- code_puppy/tools/common.py +1409 -0
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +886 -0
- code_puppy/tools/file_operations.py +802 -0
- code_puppy/tools/scheduler_tools.py +412 -0
- code_puppy/tools/skills_tools.py +244 -0
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/tools/tools_content.py +51 -0
- code_puppy/tools/universal_constructor.py +889 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +82 -0
- codepp-0.0.437.dist-info/METADATA +766 -0
- codepp-0.0.437.dist-info/RECORD +288 -0
- codepp-0.0.437.dist-info/WHEEL +4 -0
- codepp-0.0.437.dist-info/entry_points.txt +3 -0
- codepp-0.0.437.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,135 @@
|
|
|
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 rich.text import Text
|
|
10
|
+
|
|
11
|
+
from code_puppy.mcp_.managed_server import ServerState
|
|
12
|
+
from code_puppy.messaging import emit_info
|
|
13
|
+
|
|
14
|
+
from ...agents import get_current_agent
|
|
15
|
+
from .base import MCPCommandBase
|
|
16
|
+
|
|
17
|
+
# Configure logging
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StartAllCommand(MCPCommandBase):
|
|
22
|
+
"""
|
|
23
|
+
Command handler for starting all MCP servers.
|
|
24
|
+
|
|
25
|
+
Starts all registered MCP servers and provides a summary of results.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Start all registered MCP servers.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
args: Command arguments (unused)
|
|
34
|
+
group_id: Optional message group ID for grouping related messages
|
|
35
|
+
"""
|
|
36
|
+
if group_id is None:
|
|
37
|
+
group_id = self.generate_group_id()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
servers = self.manager.list_servers()
|
|
41
|
+
|
|
42
|
+
if not servers:
|
|
43
|
+
emit_info(
|
|
44
|
+
"[yellow]No servers registered[/yellow]", message_group=group_id
|
|
45
|
+
)
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
started_count = 0
|
|
49
|
+
failed_count = 0
|
|
50
|
+
already_running = 0
|
|
51
|
+
|
|
52
|
+
emit_info(f"Starting {len(servers)} servers...", message_group=group_id)
|
|
53
|
+
|
|
54
|
+
for server_info in servers:
|
|
55
|
+
server_id = server_info.id
|
|
56
|
+
server_name = server_info.name
|
|
57
|
+
|
|
58
|
+
# Skip if already running
|
|
59
|
+
if server_info.state == ServerState.RUNNING:
|
|
60
|
+
already_running += 1
|
|
61
|
+
emit_info(
|
|
62
|
+
f" • {server_name}: already running", message_group=group_id
|
|
63
|
+
)
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
# Try to start the server
|
|
67
|
+
success = self.manager.start_server_sync(server_id)
|
|
68
|
+
|
|
69
|
+
if success:
|
|
70
|
+
started_count += 1
|
|
71
|
+
emit_info(
|
|
72
|
+
Text.from_markup(f" [green]✓ Started: {server_name}[/green]"),
|
|
73
|
+
message_group=group_id,
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
failed_count += 1
|
|
77
|
+
emit_info(
|
|
78
|
+
Text.from_markup(f" [red]✗ Failed: {server_name}[/red]"),
|
|
79
|
+
message_group=group_id,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Summary
|
|
83
|
+
emit_info("", message_group=group_id)
|
|
84
|
+
if started_count > 0:
|
|
85
|
+
emit_info(
|
|
86
|
+
Text.from_markup(
|
|
87
|
+
f"[green]Started {started_count} server(s)[/green]"
|
|
88
|
+
),
|
|
89
|
+
message_group=group_id,
|
|
90
|
+
)
|
|
91
|
+
if already_running > 0:
|
|
92
|
+
emit_info(
|
|
93
|
+
f"{already_running} server(s) already running",
|
|
94
|
+
message_group=group_id,
|
|
95
|
+
)
|
|
96
|
+
if failed_count > 0:
|
|
97
|
+
emit_info(
|
|
98
|
+
Text.from_markup(
|
|
99
|
+
f"[yellow]Failed to start {failed_count} server(s)[/yellow]"
|
|
100
|
+
),
|
|
101
|
+
message_group=group_id,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Reload agent if any servers were started
|
|
105
|
+
if started_count > 0:
|
|
106
|
+
# Give async tasks a moment to complete before reloading agent
|
|
107
|
+
try:
|
|
108
|
+
import asyncio
|
|
109
|
+
|
|
110
|
+
asyncio.get_running_loop() # Check if in async context
|
|
111
|
+
# If we're in async context, wait a bit for servers to start
|
|
112
|
+
time.sleep(0.5) # Small delay to let async tasks progress
|
|
113
|
+
except RuntimeError:
|
|
114
|
+
pass # No async loop, servers will start when agent uses them
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
agent = get_current_agent()
|
|
118
|
+
agent.reload_code_generation_agent()
|
|
119
|
+
# Update MCP tool cache immediately so token counts reflect the change
|
|
120
|
+
agent.update_mcp_tool_cache_sync()
|
|
121
|
+
emit_info(
|
|
122
|
+
Text.from_markup(
|
|
123
|
+
"[dim]Agent reloaded with updated servers[/dim]"
|
|
124
|
+
),
|
|
125
|
+
message_group=group_id,
|
|
126
|
+
)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logger.error(f"Error starting all servers: {e}")
|
|
132
|
+
emit_info(
|
|
133
|
+
Text.from_markup(f"[red]Failed to start servers: {e}[/red]"),
|
|
134
|
+
message_group=group_id,
|
|
135
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Start Command - Starts a specific MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success
|
|
11
|
+
|
|
12
|
+
from ...agents import get_current_agent
|
|
13
|
+
from .base import MCPCommandBase
|
|
14
|
+
from .utils import find_server_id_by_name, suggest_similar_servers
|
|
15
|
+
|
|
16
|
+
# Configure logging
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StartCommand(MCPCommandBase):
|
|
21
|
+
"""
|
|
22
|
+
Command handler for starting MCP servers.
|
|
23
|
+
|
|
24
|
+
Starts a specific MCP server by name and reloads the agent.
|
|
25
|
+
The server subprocess starts asynchronously in the background.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Start a specific MCP server.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
args: Command arguments, expects [server_name]
|
|
34
|
+
group_id: Optional message group ID for grouping related messages
|
|
35
|
+
"""
|
|
36
|
+
if group_id is None:
|
|
37
|
+
group_id = self.generate_group_id()
|
|
38
|
+
|
|
39
|
+
if not args:
|
|
40
|
+
emit_info(
|
|
41
|
+
Text.from_markup("[yellow]Usage: /mcp start <server_name>[/yellow]"),
|
|
42
|
+
message_group=group_id,
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
server_name = args[0]
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
# Find server by name
|
|
50
|
+
server_id = find_server_id_by_name(self.manager, server_name)
|
|
51
|
+
if not server_id:
|
|
52
|
+
emit_error(
|
|
53
|
+
f"Server '{server_name}' not found",
|
|
54
|
+
message_group=group_id,
|
|
55
|
+
)
|
|
56
|
+
suggest_similar_servers(self.manager, server_name, group_id=group_id)
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
# Get server info for better messaging (safely handle missing method)
|
|
60
|
+
server_type = "unknown"
|
|
61
|
+
try:
|
|
62
|
+
if hasattr(self.manager, "get_server_by_name"):
|
|
63
|
+
server_config = self.manager.get_server_by_name(server_name)
|
|
64
|
+
server_type = (
|
|
65
|
+
getattr(server_config, "type", "unknown")
|
|
66
|
+
if server_config
|
|
67
|
+
else "unknown"
|
|
68
|
+
)
|
|
69
|
+
except Exception:
|
|
70
|
+
pass # Default to unknown type if we can't determine it
|
|
71
|
+
|
|
72
|
+
# Start the server (schedules async start in background)
|
|
73
|
+
success = self.manager.start_server_sync(server_id)
|
|
74
|
+
|
|
75
|
+
if success:
|
|
76
|
+
if server_type == "stdio":
|
|
77
|
+
# Stdio servers start subprocess asynchronously
|
|
78
|
+
emit_success(
|
|
79
|
+
f"🚀 Starting server: {server_name} (subprocess starting in background)",
|
|
80
|
+
message_group=group_id,
|
|
81
|
+
)
|
|
82
|
+
emit_info(
|
|
83
|
+
Text.from_markup(
|
|
84
|
+
"[dim]Tip: Use /mcp status to check if the server is fully initialized[/dim]"
|
|
85
|
+
),
|
|
86
|
+
message_group=group_id,
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
# SSE/HTTP servers connect on first use
|
|
90
|
+
emit_success(
|
|
91
|
+
f"✅ Enabled server: {server_name}",
|
|
92
|
+
message_group=group_id,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Reload the agent to pick up the newly enabled server
|
|
96
|
+
# NOTE: We don't block or wait - the server will be ready
|
|
97
|
+
# when the next prompt runs (pydantic-ai handles connection)
|
|
98
|
+
try:
|
|
99
|
+
agent = get_current_agent()
|
|
100
|
+
agent.reload_code_generation_agent()
|
|
101
|
+
# Clear MCP tool cache - it will be repopulated on next run
|
|
102
|
+
agent.update_mcp_tool_cache_sync()
|
|
103
|
+
emit_info(
|
|
104
|
+
"Agent reloaded with updated servers",
|
|
105
|
+
message_group=group_id,
|
|
106
|
+
)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
109
|
+
else:
|
|
110
|
+
emit_error(
|
|
111
|
+
f"Failed to start server: {server_name}",
|
|
112
|
+
message_group=group_id,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Error starting server '{server_name}': {e}")
|
|
117
|
+
emit_error(f"Failed to start server: {e}", message_group=group_id)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Status Command - Shows detailed status for MCP servers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
12
|
+
from code_puppy.mcp_.managed_server import ServerState
|
|
13
|
+
from code_puppy.messaging import emit_error, emit_info
|
|
14
|
+
|
|
15
|
+
from .base import MCPCommandBase
|
|
16
|
+
from .list_command import ListCommand
|
|
17
|
+
from .utils import (
|
|
18
|
+
find_server_id_by_name,
|
|
19
|
+
format_state_indicator,
|
|
20
|
+
format_uptime,
|
|
21
|
+
suggest_similar_servers,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Configure logging
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class StatusCommand(MCPCommandBase):
|
|
29
|
+
"""
|
|
30
|
+
Command handler for showing MCP server status.
|
|
31
|
+
|
|
32
|
+
Shows detailed status for a specific server or brief status for all servers.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Show detailed status for a specific server or all servers.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
args: Command arguments, expects [server_name] (optional)
|
|
41
|
+
group_id: Optional message group ID for grouping related messages
|
|
42
|
+
"""
|
|
43
|
+
if group_id is None:
|
|
44
|
+
group_id = self.generate_group_id()
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
if args:
|
|
48
|
+
# Show detailed status for specific server
|
|
49
|
+
server_name = args[0]
|
|
50
|
+
server_id = find_server_id_by_name(self.manager, server_name)
|
|
51
|
+
|
|
52
|
+
if not server_id:
|
|
53
|
+
emit_info(
|
|
54
|
+
f"Server '{server_name}' not found", message_group=group_id
|
|
55
|
+
)
|
|
56
|
+
suggest_similar_servers(
|
|
57
|
+
self.manager, server_name, group_id=group_id
|
|
58
|
+
)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
self._show_detailed_server_status(server_id, server_name, group_id)
|
|
62
|
+
else:
|
|
63
|
+
# Show brief status for all servers
|
|
64
|
+
list_command = ListCommand()
|
|
65
|
+
list_command.execute([], group_id=group_id)
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"Error showing server status: {e}")
|
|
69
|
+
emit_info(f"Failed to get server status: {e}", message_group=group_id)
|
|
70
|
+
|
|
71
|
+
def _show_detailed_server_status(
|
|
72
|
+
self, server_id: str, server_name: str, group_id: Optional[str] = None
|
|
73
|
+
) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Show comprehensive status information for a specific server.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
server_id: ID of the server
|
|
79
|
+
server_name: Name of the server
|
|
80
|
+
group_id: Optional message group ID
|
|
81
|
+
"""
|
|
82
|
+
if group_id is None:
|
|
83
|
+
group_id = self.generate_group_id()
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
status = self.manager.get_server_status(server_id)
|
|
87
|
+
|
|
88
|
+
if not status.get("exists", True):
|
|
89
|
+
emit_info(
|
|
90
|
+
f"Server '{server_name}' not found or not accessible",
|
|
91
|
+
message_group=group_id,
|
|
92
|
+
)
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
# Create detailed status panel
|
|
96
|
+
status_lines = []
|
|
97
|
+
|
|
98
|
+
# Basic information
|
|
99
|
+
status_lines.append(f"[bold]Server:[/bold] {server_name}")
|
|
100
|
+
status_lines.append(f"[bold]ID:[/bold] {server_id}")
|
|
101
|
+
status_lines.append(
|
|
102
|
+
f"[bold]Type:[/bold] {status.get('type', 'unknown').upper()}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# State and status
|
|
106
|
+
state = status.get("state", "unknown")
|
|
107
|
+
state_display = format_state_indicator(
|
|
108
|
+
ServerState(state)
|
|
109
|
+
if state in [s.value for s in ServerState]
|
|
110
|
+
else ServerState.STOPPED
|
|
111
|
+
)
|
|
112
|
+
status_lines.append(f"[bold]State:[/bold] {state_display}")
|
|
113
|
+
|
|
114
|
+
enabled = status.get("enabled", False)
|
|
115
|
+
status_lines.append(
|
|
116
|
+
f"[bold]Enabled:[/bold] {'✓ Yes' if enabled else '✗ No'}"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Check async lifecycle manager status if available
|
|
120
|
+
try:
|
|
121
|
+
from code_puppy.mcp_.async_lifecycle import get_lifecycle_manager
|
|
122
|
+
|
|
123
|
+
lifecycle_mgr = get_lifecycle_manager()
|
|
124
|
+
if lifecycle_mgr.is_running(server_id):
|
|
125
|
+
status_lines.append(
|
|
126
|
+
"[bold]Process:[/bold] [green]✓ Active (subprocess/connection running)[/green]"
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
status_lines.append("[bold]Process:[/bold] [dim]Not active[/dim]")
|
|
130
|
+
except Exception:
|
|
131
|
+
pass # Lifecycle manager not available
|
|
132
|
+
|
|
133
|
+
quarantined = status.get("quarantined", False)
|
|
134
|
+
if quarantined:
|
|
135
|
+
status_lines.append("[bold]Quarantined:[/bold] [yellow]⚠ Yes[/yellow]")
|
|
136
|
+
|
|
137
|
+
# Timing information
|
|
138
|
+
uptime = status.get("tracker_uptime")
|
|
139
|
+
if uptime:
|
|
140
|
+
uptime_str = format_uptime(
|
|
141
|
+
uptime.total_seconds()
|
|
142
|
+
if hasattr(uptime, "total_seconds")
|
|
143
|
+
else uptime
|
|
144
|
+
)
|
|
145
|
+
status_lines.append(f"[bold]Uptime:[/bold] {uptime_str}")
|
|
146
|
+
|
|
147
|
+
# Error information
|
|
148
|
+
error_msg = status.get("error_message")
|
|
149
|
+
if error_msg:
|
|
150
|
+
status_lines.append(f"[bold]Error:[/bold] [red]{error_msg}[/red]")
|
|
151
|
+
|
|
152
|
+
# Event information
|
|
153
|
+
event_count = status.get("recent_events_count", 0)
|
|
154
|
+
status_lines.append(f"[bold]Recent Events:[/bold] {event_count}")
|
|
155
|
+
|
|
156
|
+
# Metadata
|
|
157
|
+
metadata = status.get("tracker_metadata", {})
|
|
158
|
+
if metadata:
|
|
159
|
+
status_lines.append(f"[bold]Metadata:[/bold] {len(metadata)} keys")
|
|
160
|
+
|
|
161
|
+
# Create and show the panel
|
|
162
|
+
panel_content = Text.from_markup("\n".join(status_lines))
|
|
163
|
+
panel = Panel(
|
|
164
|
+
panel_content, title=f"🔌 {server_name} Status", border_style="cyan"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
emit_info(panel, message_group=group_id)
|
|
168
|
+
|
|
169
|
+
# Show recent events if available
|
|
170
|
+
recent_events = status.get("recent_events", [])
|
|
171
|
+
if recent_events:
|
|
172
|
+
emit_info("\n📋 Recent Events:", message_group=group_id)
|
|
173
|
+
for event in recent_events[-5:]: # Show last 5 events
|
|
174
|
+
timestamp = datetime.fromisoformat(event["timestamp"])
|
|
175
|
+
time_str = timestamp.strftime("%H:%M:%S")
|
|
176
|
+
emit_info(
|
|
177
|
+
f" {time_str}: {event['message']}", message_group=group_id
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(
|
|
182
|
+
f"Error getting detailed status for server '{server_name}': {e}"
|
|
183
|
+
)
|
|
184
|
+
emit_error(f"Error getting server status: {e}", message_group=group_id)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Stop All Command - Stops all running MCP servers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
from code_puppy.mcp_.managed_server import ServerState
|
|
12
|
+
from code_puppy.messaging import emit_info
|
|
13
|
+
|
|
14
|
+
from ...agents import get_current_agent
|
|
15
|
+
from .base import MCPCommandBase
|
|
16
|
+
|
|
17
|
+
# Configure logging
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StopAllCommand(MCPCommandBase):
|
|
22
|
+
"""
|
|
23
|
+
Command handler for stopping all MCP servers.
|
|
24
|
+
|
|
25
|
+
Stops all running MCP servers and provides a summary of results.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Stop all running MCP servers.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
args: Command arguments (unused)
|
|
34
|
+
group_id: Optional message group ID for grouping related messages
|
|
35
|
+
"""
|
|
36
|
+
if group_id is None:
|
|
37
|
+
group_id = self.generate_group_id()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
servers = self.manager.list_servers()
|
|
41
|
+
|
|
42
|
+
if not servers:
|
|
43
|
+
emit_info("No servers registered", message_group=group_id)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
stopped_count = 0
|
|
47
|
+
failed_count = 0
|
|
48
|
+
|
|
49
|
+
# Count running servers
|
|
50
|
+
running_servers = [s for s in servers if s.state == ServerState.RUNNING]
|
|
51
|
+
|
|
52
|
+
if not running_servers:
|
|
53
|
+
emit_info("No servers are currently running", message_group=group_id)
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
emit_info(
|
|
57
|
+
f"Stopping {len(running_servers)} running server(s)...",
|
|
58
|
+
message_group=group_id,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
for server_info in running_servers:
|
|
62
|
+
server_id = server_info.id
|
|
63
|
+
server_name = server_info.name
|
|
64
|
+
|
|
65
|
+
# Try to stop the server
|
|
66
|
+
success = self.manager.stop_server_sync(server_id)
|
|
67
|
+
|
|
68
|
+
if success:
|
|
69
|
+
stopped_count += 1
|
|
70
|
+
emit_info(f" ✓ Stopped: {server_name}", message_group=group_id)
|
|
71
|
+
else:
|
|
72
|
+
failed_count += 1
|
|
73
|
+
emit_info(f" ✗ Failed: {server_name}", message_group=group_id)
|
|
74
|
+
|
|
75
|
+
# Summary
|
|
76
|
+
emit_info("", message_group=group_id)
|
|
77
|
+
if stopped_count > 0:
|
|
78
|
+
emit_info(f"Stopped {stopped_count} server(s)", message_group=group_id)
|
|
79
|
+
if failed_count > 0:
|
|
80
|
+
emit_info(
|
|
81
|
+
f"Failed to stop {failed_count} server(s)", message_group=group_id
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Reload agent if any servers were stopped
|
|
85
|
+
if stopped_count > 0:
|
|
86
|
+
# Give async tasks a moment to complete before reloading agent
|
|
87
|
+
try:
|
|
88
|
+
import asyncio
|
|
89
|
+
|
|
90
|
+
asyncio.get_running_loop() # Check if in async context
|
|
91
|
+
# If we're in async context, wait a bit for servers to stop
|
|
92
|
+
time.sleep(0.5) # Small delay to let async tasks progress
|
|
93
|
+
except RuntimeError:
|
|
94
|
+
pass # No async loop, servers will stop when needed
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
agent = get_current_agent()
|
|
98
|
+
agent.reload_code_generation_agent()
|
|
99
|
+
# Update MCP tool cache immediately so token counts reflect the change
|
|
100
|
+
agent.update_mcp_tool_cache_sync()
|
|
101
|
+
emit_info(
|
|
102
|
+
Text.from_markup(
|
|
103
|
+
"[dim]Agent reloaded with updated servers[/dim]"
|
|
104
|
+
),
|
|
105
|
+
message_group=group_id,
|
|
106
|
+
)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
109
|
+
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error(f"Error stopping all servers: {e}")
|
|
112
|
+
emit_info(f"Failed to stop servers: {e}", message_group=group_id)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Stop Command - Stops a specific MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from code_puppy.messaging import emit_error, emit_info
|
|
11
|
+
|
|
12
|
+
from ...agents import get_current_agent
|
|
13
|
+
from .base import MCPCommandBase
|
|
14
|
+
from .utils import find_server_id_by_name, suggest_similar_servers
|
|
15
|
+
|
|
16
|
+
# Configure logging
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StopCommand(MCPCommandBase):
|
|
21
|
+
"""
|
|
22
|
+
Command handler for stopping MCP servers.
|
|
23
|
+
|
|
24
|
+
Stops a specific MCP server by name and reloads the agent.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Stop a specific MCP server.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
args: Command arguments, expects [server_name]
|
|
33
|
+
group_id: Optional message group ID for grouping related messages
|
|
34
|
+
"""
|
|
35
|
+
if group_id is None:
|
|
36
|
+
group_id = self.generate_group_id()
|
|
37
|
+
|
|
38
|
+
if not args:
|
|
39
|
+
emit_info(
|
|
40
|
+
Text.from_markup("[yellow]Usage: /mcp stop <server_name>[/yellow]"),
|
|
41
|
+
message_group=group_id,
|
|
42
|
+
)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
server_name = args[0]
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Find server by name
|
|
49
|
+
server_id = find_server_id_by_name(self.manager, server_name)
|
|
50
|
+
if not server_id:
|
|
51
|
+
emit_info(f"Server '{server_name}' not found", message_group=group_id)
|
|
52
|
+
suggest_similar_servers(self.manager, server_name, group_id=group_id)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
# Stop the server (disable and stop process)
|
|
56
|
+
success = self.manager.stop_server_sync(server_id)
|
|
57
|
+
|
|
58
|
+
if success:
|
|
59
|
+
emit_info(f"✓ Stopped server: {server_name}", message_group=group_id)
|
|
60
|
+
|
|
61
|
+
# Reload the agent to remove the disabled server
|
|
62
|
+
try:
|
|
63
|
+
agent = get_current_agent()
|
|
64
|
+
agent.reload_code_generation_agent()
|
|
65
|
+
# Update MCP tool cache immediately so token counts reflect the change
|
|
66
|
+
agent.update_mcp_tool_cache_sync()
|
|
67
|
+
emit_info(
|
|
68
|
+
"Agent reloaded with updated servers",
|
|
69
|
+
message_group=group_id,
|
|
70
|
+
)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.warning(f"Could not reload agent: {e}")
|
|
73
|
+
else:
|
|
74
|
+
emit_info(
|
|
75
|
+
f"✗ Failed to stop server: {server_name}", message_group=group_id
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.error(f"Error stopping server '{server_name}': {e}")
|
|
80
|
+
emit_error(f"Failed to stop server: {e}", message_group=group_id)
|