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,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Test Command - Tests connectivity to a specific MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from code_puppy.messaging import emit_error, 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 TestCommand(MCPCommandBase):
|
|
18
|
+
"""
|
|
19
|
+
Command handler for testing MCP server connectivity.
|
|
20
|
+
|
|
21
|
+
Tests connectivity and basic functionality of a specific MCP server.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Test connectivity to 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 test <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
|
+
# Get managed server
|
|
50
|
+
managed_server = self.manager.get_server(server_id)
|
|
51
|
+
if not managed_server:
|
|
52
|
+
emit_info(
|
|
53
|
+
f"Server '{server_name}' not accessible", message_group=group_id
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
emit_info(
|
|
58
|
+
f"🔍 Testing connectivity to server: {server_name}",
|
|
59
|
+
message_group=group_id,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Basic connectivity test - try to get the pydantic server
|
|
63
|
+
try:
|
|
64
|
+
managed_server.get_pydantic_server() # Test server instantiation
|
|
65
|
+
emit_info(
|
|
66
|
+
"✓ Server instance created successfully", message_group=group_id
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Try to get server info if available
|
|
70
|
+
emit_info(
|
|
71
|
+
f" • Server type: {managed_server.config.type}",
|
|
72
|
+
message_group=group_id,
|
|
73
|
+
)
|
|
74
|
+
emit_info(
|
|
75
|
+
f" • Server enabled: {managed_server.is_enabled()}",
|
|
76
|
+
message_group=group_id,
|
|
77
|
+
)
|
|
78
|
+
emit_info(
|
|
79
|
+
f" • Server quarantined: {managed_server.is_quarantined()}",
|
|
80
|
+
message_group=group_id,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if not managed_server.is_enabled():
|
|
84
|
+
emit_info(
|
|
85
|
+
" • Server is disabled - enable it with '/mcp start'",
|
|
86
|
+
message_group=group_id,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if managed_server.is_quarantined():
|
|
90
|
+
emit_info(
|
|
91
|
+
" • Server is quarantined - may have recent errors",
|
|
92
|
+
message_group=group_id,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
emit_info(
|
|
96
|
+
f"✓ Connectivity test passed for: {server_name}",
|
|
97
|
+
message_group=group_id,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
except Exception as test_error:
|
|
101
|
+
emit_info(
|
|
102
|
+
f"✗ Connectivity test failed: {test_error}", message_group=group_id
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.error(f"Error testing server '{server_name}': {e}")
|
|
107
|
+
emit_error(f"Error testing server: {e}", message_group=group_id)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Command Utilities - Shared helper functions for MCP command handlers.
|
|
3
|
+
|
|
4
|
+
Provides common utility functions used across multiple MCP command modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
from code_puppy.mcp_.managed_server import ServerState
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def format_state_indicator(state: ServerState) -> Text:
|
|
15
|
+
"""
|
|
16
|
+
Format a server state with appropriate color and icon.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
state: Server state to format
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Rich Text object with colored state indicator
|
|
23
|
+
"""
|
|
24
|
+
state_map = {
|
|
25
|
+
ServerState.RUNNING: ("✓ Run", "green"),
|
|
26
|
+
ServerState.STOPPED: ("✗ Stop", "red"),
|
|
27
|
+
ServerState.STARTING: ("↗ Start", "yellow"),
|
|
28
|
+
ServerState.STOPPING: ("↙ Stop", "yellow"),
|
|
29
|
+
ServerState.ERROR: ("⚠ Err", "red"),
|
|
30
|
+
ServerState.QUARANTINED: ("⏸ Quar", "yellow"),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
display, color = state_map.get(state, ("? Unk", "dim"))
|
|
34
|
+
return Text(display, style=color)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def format_uptime(uptime_seconds: Optional[float]) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Format uptime in a human-readable format.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
uptime_seconds: Uptime in seconds, or None
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Formatted uptime string
|
|
46
|
+
"""
|
|
47
|
+
if uptime_seconds is None or uptime_seconds <= 0:
|
|
48
|
+
return "-"
|
|
49
|
+
|
|
50
|
+
# Convert to readable format
|
|
51
|
+
if uptime_seconds < 60:
|
|
52
|
+
return f"{int(uptime_seconds)}s"
|
|
53
|
+
elif uptime_seconds < 3600:
|
|
54
|
+
minutes = int(uptime_seconds // 60)
|
|
55
|
+
seconds = int(uptime_seconds % 60)
|
|
56
|
+
return f"{minutes}m {seconds}s"
|
|
57
|
+
else:
|
|
58
|
+
hours = int(uptime_seconds // 3600)
|
|
59
|
+
minutes = int((uptime_seconds % 3600) // 60)
|
|
60
|
+
return f"{hours}h {minutes}m"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def find_server_id_by_name(manager, server_name: str) -> Optional[str]:
|
|
64
|
+
"""
|
|
65
|
+
Find a server ID by its name.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
manager: MCP manager instance
|
|
69
|
+
server_name: Name of the server to find
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Server ID if found, None otherwise
|
|
73
|
+
"""
|
|
74
|
+
import logging
|
|
75
|
+
|
|
76
|
+
logger = logging.getLogger(__name__)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
servers = manager.list_servers()
|
|
80
|
+
for server in servers:
|
|
81
|
+
if server.name.lower() == server_name.lower():
|
|
82
|
+
return server.id
|
|
83
|
+
return None
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.error(f"Error finding server by name '{server_name}': {e}")
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def suggest_similar_servers(
|
|
90
|
+
manager, server_name: str, group_id: Optional[str] = None
|
|
91
|
+
) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Suggest similar server names when a server is not found.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
manager: MCP manager instance
|
|
97
|
+
server_name: The server name that was not found
|
|
98
|
+
group_id: Optional message group ID for grouping related messages
|
|
99
|
+
"""
|
|
100
|
+
import logging
|
|
101
|
+
|
|
102
|
+
from code_puppy.messaging import emit_info
|
|
103
|
+
|
|
104
|
+
logger = logging.getLogger(__name__)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
servers = manager.list_servers()
|
|
108
|
+
if not servers:
|
|
109
|
+
emit_info("No servers are registered", message_group=group_id)
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# Simple suggestion based on partial matching
|
|
113
|
+
suggestions = []
|
|
114
|
+
server_name_lower = server_name.lower()
|
|
115
|
+
|
|
116
|
+
for server in servers:
|
|
117
|
+
if server_name_lower in server.name.lower():
|
|
118
|
+
suggestions.append(server.name)
|
|
119
|
+
|
|
120
|
+
if suggestions:
|
|
121
|
+
emit_info(f"Did you mean: {', '.join(suggestions)}", message_group=group_id)
|
|
122
|
+
else:
|
|
123
|
+
server_names = [s.name for s in servers]
|
|
124
|
+
emit_info(
|
|
125
|
+
f"Available servers: {', '.join(server_names)}", message_group=group_id
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.error(f"Error suggesting similar servers: {e}")
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Interactive Wizard Utilities - Shared interactive installation wizard functions.
|
|
3
|
+
|
|
4
|
+
Provides interactive functionality for installing and configuring MCP servers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
12
|
+
from code_puppy.messaging import emit_error, emit_info, emit_prompt
|
|
13
|
+
|
|
14
|
+
# Configure logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def run_interactive_install_wizard(manager, group_id: str) -> bool:
|
|
19
|
+
"""
|
|
20
|
+
Run the interactive MCP server installation wizard.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
manager: MCP manager instance
|
|
24
|
+
group_id: Message group ID for grouping related messages
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
True if installation was successful, False otherwise
|
|
28
|
+
"""
|
|
29
|
+
try:
|
|
30
|
+
# Show welcome message
|
|
31
|
+
emit_info("🚀 MCP Server Installation Wizard", message_group=group_id)
|
|
32
|
+
emit_info(
|
|
33
|
+
"This wizard will help you install pre-configured MCP servers",
|
|
34
|
+
message_group=group_id,
|
|
35
|
+
)
|
|
36
|
+
emit_info("", message_group=group_id)
|
|
37
|
+
|
|
38
|
+
# Let user select a server
|
|
39
|
+
selected_server = interactive_server_selection(group_id)
|
|
40
|
+
if not selected_server:
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
# Get custom name
|
|
44
|
+
server_name = interactive_get_server_name(selected_server, group_id)
|
|
45
|
+
if not server_name:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
# Collect environment variables and command line arguments
|
|
49
|
+
env_vars = {}
|
|
50
|
+
cmd_args = {}
|
|
51
|
+
|
|
52
|
+
# Get environment variables
|
|
53
|
+
required_env_vars = selected_server.get_environment_vars()
|
|
54
|
+
if required_env_vars:
|
|
55
|
+
emit_info(
|
|
56
|
+
Text.from_markup("\n[yellow]Required Environment Variables:[/yellow]"),
|
|
57
|
+
message_group=group_id,
|
|
58
|
+
)
|
|
59
|
+
for var in required_env_vars:
|
|
60
|
+
# Check if already set in environment
|
|
61
|
+
import os
|
|
62
|
+
|
|
63
|
+
current_value = os.environ.get(var, "")
|
|
64
|
+
if current_value:
|
|
65
|
+
emit_info(
|
|
66
|
+
Text.from_markup(f" {var}: [green]Already set[/green]"),
|
|
67
|
+
message_group=group_id,
|
|
68
|
+
)
|
|
69
|
+
env_vars[var] = current_value
|
|
70
|
+
else:
|
|
71
|
+
value = emit_prompt(f" Enter value for {var}: ").strip()
|
|
72
|
+
if value:
|
|
73
|
+
env_vars[var] = value
|
|
74
|
+
|
|
75
|
+
# Get command line arguments
|
|
76
|
+
required_cmd_args = selected_server.get_command_line_args()
|
|
77
|
+
if required_cmd_args:
|
|
78
|
+
emit_info(
|
|
79
|
+
Text.from_markup("\n[yellow]Command Line Arguments:[/yellow]"),
|
|
80
|
+
message_group=group_id,
|
|
81
|
+
)
|
|
82
|
+
for arg_config in required_cmd_args:
|
|
83
|
+
name = arg_config.get("name", "")
|
|
84
|
+
prompt = arg_config.get("prompt", name)
|
|
85
|
+
default = arg_config.get("default", "")
|
|
86
|
+
required = arg_config.get("required", True)
|
|
87
|
+
|
|
88
|
+
# If required or has default, prompt user
|
|
89
|
+
if required or default:
|
|
90
|
+
arg_prompt = f" {prompt}"
|
|
91
|
+
if default:
|
|
92
|
+
arg_prompt += f" [{default}]"
|
|
93
|
+
if not required:
|
|
94
|
+
arg_prompt += " (optional)"
|
|
95
|
+
|
|
96
|
+
value = emit_prompt(f"{arg_prompt}: ").strip()
|
|
97
|
+
if value:
|
|
98
|
+
cmd_args[name] = value
|
|
99
|
+
elif default:
|
|
100
|
+
cmd_args[name] = default
|
|
101
|
+
|
|
102
|
+
# Configure the server
|
|
103
|
+
return interactive_configure_server(
|
|
104
|
+
manager, selected_server, server_name, group_id, env_vars, cmd_args
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
except ImportError:
|
|
108
|
+
emit_error("Server catalog not available", message_group=group_id)
|
|
109
|
+
return False
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error(f"Error in interactive wizard: {e}")
|
|
112
|
+
emit_error(f"Wizard error: {e}", message_group=group_id)
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def interactive_server_selection(group_id: str):
|
|
117
|
+
"""
|
|
118
|
+
Interactive server selection from catalog.
|
|
119
|
+
|
|
120
|
+
Returns selected server or None if cancelled.
|
|
121
|
+
"""
|
|
122
|
+
# This is a simplified version - the full implementation would have
|
|
123
|
+
# category browsing, search, etc. For now, we'll just show popular servers
|
|
124
|
+
try:
|
|
125
|
+
from code_puppy.mcp_.server_registry_catalog import catalog
|
|
126
|
+
|
|
127
|
+
servers = catalog.get_popular(10)
|
|
128
|
+
if not servers:
|
|
129
|
+
emit_info("No servers available in catalog", message_group=group_id)
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
emit_info("Popular MCP Servers:", message_group=group_id)
|
|
133
|
+
for i, server in enumerate(servers, 1):
|
|
134
|
+
indicators = []
|
|
135
|
+
if server.verified:
|
|
136
|
+
indicators.append("✓")
|
|
137
|
+
if server.popular:
|
|
138
|
+
indicators.append("⭐")
|
|
139
|
+
|
|
140
|
+
indicator_str = ""
|
|
141
|
+
if indicators:
|
|
142
|
+
indicator_str = " " + "".join(indicators)
|
|
143
|
+
|
|
144
|
+
emit_info(
|
|
145
|
+
f"{i:2}. {server.display_name}{indicator_str}", message_group=group_id
|
|
146
|
+
)
|
|
147
|
+
emit_info(f" {server.description[:80]}...", message_group=group_id)
|
|
148
|
+
|
|
149
|
+
choice = emit_prompt(
|
|
150
|
+
"Enter number (1-{}) or 'q' to quit: ".format(len(servers))
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if choice.lower() == "q":
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
index = int(choice) - 1
|
|
158
|
+
if 0 <= index < len(servers):
|
|
159
|
+
return servers[index]
|
|
160
|
+
else:
|
|
161
|
+
emit_error("Invalid selection", message_group=group_id)
|
|
162
|
+
return None
|
|
163
|
+
except ValueError:
|
|
164
|
+
emit_error("Invalid input", message_group=group_id)
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
except Exception as e:
|
|
168
|
+
logger.error(f"Error in server selection: {e}")
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def interactive_get_server_name(selected_server, group_id: str) -> Optional[str]:
|
|
173
|
+
"""
|
|
174
|
+
Get custom server name from user.
|
|
175
|
+
|
|
176
|
+
Returns server name or None if cancelled.
|
|
177
|
+
"""
|
|
178
|
+
default_name = selected_server.name
|
|
179
|
+
server_name = emit_prompt(f"Enter name for this server [{default_name}]: ").strip()
|
|
180
|
+
|
|
181
|
+
if not server_name:
|
|
182
|
+
server_name = default_name
|
|
183
|
+
|
|
184
|
+
return server_name
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def interactive_configure_server(
|
|
188
|
+
manager,
|
|
189
|
+
selected_server,
|
|
190
|
+
server_name: str,
|
|
191
|
+
group_id: str,
|
|
192
|
+
env_vars: Dict[str, Any],
|
|
193
|
+
cmd_args: Dict[str, Any],
|
|
194
|
+
) -> bool:
|
|
195
|
+
"""
|
|
196
|
+
Configure and install the selected server.
|
|
197
|
+
|
|
198
|
+
Returns True if successful, False otherwise.
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
# Check if server already exists
|
|
202
|
+
from .utils import find_server_id_by_name
|
|
203
|
+
|
|
204
|
+
existing_server = find_server_id_by_name(manager, server_name)
|
|
205
|
+
if existing_server:
|
|
206
|
+
override = emit_prompt(
|
|
207
|
+
f"Server '{server_name}' already exists. Override? [y/N]: "
|
|
208
|
+
)
|
|
209
|
+
if not override.lower().startswith("y"):
|
|
210
|
+
emit_info("Installation cancelled", message_group=group_id)
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
# Show confirmation
|
|
214
|
+
emit_info(f"Installing: {selected_server.display_name}", message_group=group_id)
|
|
215
|
+
emit_info(f"Name: {server_name}", message_group=group_id)
|
|
216
|
+
|
|
217
|
+
if env_vars:
|
|
218
|
+
emit_info("Environment Variables:", message_group=group_id)
|
|
219
|
+
for var, _value in env_vars.items():
|
|
220
|
+
emit_info(f" {var}: ***", message_group=group_id)
|
|
221
|
+
|
|
222
|
+
if cmd_args:
|
|
223
|
+
emit_info("Command Line Arguments:", message_group=group_id)
|
|
224
|
+
for arg, value in cmd_args.items():
|
|
225
|
+
emit_info(f" {arg}: {value}", message_group=group_id)
|
|
226
|
+
|
|
227
|
+
confirm = emit_prompt("Proceed with installation? [Y/n]: ")
|
|
228
|
+
if confirm.lower().startswith("n"):
|
|
229
|
+
emit_info("Installation cancelled", message_group=group_id)
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
# Install the server (simplified version)
|
|
233
|
+
return install_server_from_catalog(
|
|
234
|
+
manager, selected_server, server_name, env_vars, cmd_args, group_id
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
logger.error(f"Error configuring server: {e}")
|
|
239
|
+
emit_error(f"Configuration error: {e}", message_group=group_id)
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def install_server_from_catalog(
|
|
244
|
+
manager,
|
|
245
|
+
selected_server,
|
|
246
|
+
server_name: str,
|
|
247
|
+
env_vars: Dict[str, Any],
|
|
248
|
+
cmd_args: Dict[str, Any],
|
|
249
|
+
group_id: str,
|
|
250
|
+
) -> bool:
|
|
251
|
+
"""
|
|
252
|
+
Install a server from the catalog with the given configuration.
|
|
253
|
+
|
|
254
|
+
Returns True if successful, False otherwise.
|
|
255
|
+
"""
|
|
256
|
+
try:
|
|
257
|
+
import json
|
|
258
|
+
import os
|
|
259
|
+
|
|
260
|
+
from code_puppy.config import MCP_SERVERS_FILE
|
|
261
|
+
from code_puppy.mcp_.managed_server import ServerConfig
|
|
262
|
+
|
|
263
|
+
# Set environment variables in the current environment
|
|
264
|
+
for var, value in env_vars.items():
|
|
265
|
+
os.environ[var] = value
|
|
266
|
+
|
|
267
|
+
# Get server config with command line argument overrides
|
|
268
|
+
config_dict = selected_server.to_server_config(server_name, **cmd_args)
|
|
269
|
+
|
|
270
|
+
# Update the config with actual environment variable values
|
|
271
|
+
if "env" in config_dict:
|
|
272
|
+
for env_key, env_value in config_dict["env"].items():
|
|
273
|
+
# If it's a placeholder like $GITHUB_TOKEN, replace with actual value
|
|
274
|
+
if env_value.startswith("$"):
|
|
275
|
+
var_name = env_value[1:] # Remove the $
|
|
276
|
+
if var_name in env_vars:
|
|
277
|
+
config_dict["env"][env_key] = env_vars[var_name]
|
|
278
|
+
|
|
279
|
+
# Create ServerConfig
|
|
280
|
+
server_config = ServerConfig(
|
|
281
|
+
id=server_name,
|
|
282
|
+
name=server_name,
|
|
283
|
+
type=selected_server.type,
|
|
284
|
+
enabled=True,
|
|
285
|
+
config=config_dict,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Register with manager
|
|
289
|
+
server_id = manager.register_server(server_config)
|
|
290
|
+
|
|
291
|
+
if not server_id:
|
|
292
|
+
emit_info(
|
|
293
|
+
"Failed to register server with manager",
|
|
294
|
+
message_group=group_id,
|
|
295
|
+
)
|
|
296
|
+
return False
|
|
297
|
+
|
|
298
|
+
# Save to mcp_servers.json for persistence
|
|
299
|
+
if os.path.exists(MCP_SERVERS_FILE):
|
|
300
|
+
with open(MCP_SERVERS_FILE, "r") as f:
|
|
301
|
+
data = json.load(f)
|
|
302
|
+
servers = data.get("mcp_servers", {})
|
|
303
|
+
else:
|
|
304
|
+
servers = {}
|
|
305
|
+
data = {"mcp_servers": servers}
|
|
306
|
+
|
|
307
|
+
# Add new server
|
|
308
|
+
# Copy the config dict and add type before saving
|
|
309
|
+
save_config = config_dict.copy()
|
|
310
|
+
save_config["type"] = selected_server.type
|
|
311
|
+
servers[server_name] = save_config
|
|
312
|
+
|
|
313
|
+
# Save back
|
|
314
|
+
os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
|
|
315
|
+
with open(MCP_SERVERS_FILE, "w") as f:
|
|
316
|
+
json.dump(data, f, indent=2)
|
|
317
|
+
|
|
318
|
+
emit_info(
|
|
319
|
+
Text.from_markup(
|
|
320
|
+
f"[green]✓ Successfully installed server: {server_name}[/green]"
|
|
321
|
+
),
|
|
322
|
+
message_group=group_id,
|
|
323
|
+
)
|
|
324
|
+
emit_info(
|
|
325
|
+
"Use '/mcp start {}' to start the server".format(server_name),
|
|
326
|
+
message_group=group_id,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
return True
|
|
330
|
+
|
|
331
|
+
except Exception as e:
|
|
332
|
+
logger.error(f"Error installing server: {e}")
|
|
333
|
+
emit_error(f"Installation failed: {e}", message_group=group_id)
|
|
334
|
+
return False
|