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,195 @@
|
|
|
1
|
+
"""Custom MCP server installation logic.
|
|
2
|
+
|
|
3
|
+
Handles prompting users for custom server configuration and installing
|
|
4
|
+
custom MCP servers with JSON configuration.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from code_puppy.command_line.utils import safe_input
|
|
11
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
12
|
+
|
|
13
|
+
# Example configurations for each server type
|
|
14
|
+
CUSTOM_SERVER_EXAMPLES = {
|
|
15
|
+
"stdio": """{
|
|
16
|
+
"type": "stdio",
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
|
|
19
|
+
"env": {
|
|
20
|
+
"NODE_ENV": "production"
|
|
21
|
+
},
|
|
22
|
+
"timeout": 30
|
|
23
|
+
}""",
|
|
24
|
+
"http": """{
|
|
25
|
+
"type": "http",
|
|
26
|
+
"url": "http://localhost:8080/mcp",
|
|
27
|
+
"headers": {
|
|
28
|
+
"Authorization": "Bearer $MY_API_KEY",
|
|
29
|
+
"Content-Type": "application/json"
|
|
30
|
+
},
|
|
31
|
+
"timeout": 30
|
|
32
|
+
}""",
|
|
33
|
+
"sse": """{
|
|
34
|
+
"type": "sse",
|
|
35
|
+
"url": "http://localhost:8080/sse",
|
|
36
|
+
"headers": {
|
|
37
|
+
"Authorization": "Bearer $MY_API_KEY"
|
|
38
|
+
}
|
|
39
|
+
}""",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def prompt_and_install_custom_server(manager) -> bool:
|
|
44
|
+
"""Prompt for custom server configuration and install it.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
manager: MCP manager instance
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
True if successful, False otherwise
|
|
51
|
+
"""
|
|
52
|
+
from code_puppy.config import MCP_SERVERS_FILE
|
|
53
|
+
from code_puppy.mcp_.managed_server import ServerConfig
|
|
54
|
+
|
|
55
|
+
from .utils import find_server_id_by_name
|
|
56
|
+
|
|
57
|
+
emit_info("\n➕ Add Custom MCP Server\n")
|
|
58
|
+
emit_info(" Configure your own MCP server using JSON.\n")
|
|
59
|
+
|
|
60
|
+
# Get server name
|
|
61
|
+
try:
|
|
62
|
+
server_name = safe_input(" Server name: ")
|
|
63
|
+
if not server_name:
|
|
64
|
+
emit_warning("Server name is required")
|
|
65
|
+
return False
|
|
66
|
+
except (KeyboardInterrupt, EOFError):
|
|
67
|
+
emit_info("")
|
|
68
|
+
emit_warning("Cancelled")
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
# Check if server already exists
|
|
72
|
+
existing = find_server_id_by_name(manager, server_name)
|
|
73
|
+
if existing:
|
|
74
|
+
try:
|
|
75
|
+
override = safe_input(f" Server '{server_name}' exists. Override? [y/N]: ")
|
|
76
|
+
if not override.lower().startswith("y"):
|
|
77
|
+
emit_warning("Cancelled")
|
|
78
|
+
return False
|
|
79
|
+
except (KeyboardInterrupt, EOFError):
|
|
80
|
+
emit_info("")
|
|
81
|
+
emit_warning("Cancelled")
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
# Select server type
|
|
85
|
+
emit_info("\n Select server type:\n")
|
|
86
|
+
emit_info(" 1. 📟 stdio - Local command (npx, python, uvx, etc.)")
|
|
87
|
+
emit_info(" 2. 🌐 http - HTTP endpoint")
|
|
88
|
+
emit_info(" 3. 📡 sse - Server-Sent Events\n")
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
type_choice = safe_input(" Enter choice [1-3]: ")
|
|
92
|
+
except (KeyboardInterrupt, EOFError):
|
|
93
|
+
emit_info("")
|
|
94
|
+
emit_warning("Cancelled")
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
type_map = {"1": "stdio", "2": "http", "3": "sse"}
|
|
98
|
+
server_type = type_map.get(type_choice)
|
|
99
|
+
if not server_type:
|
|
100
|
+
emit_warning("Invalid choice")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
# Show example for selected type
|
|
104
|
+
example = CUSTOM_SERVER_EXAMPLES.get(server_type, "{}")
|
|
105
|
+
emit_info(f"\n Example {server_type} configuration:\n")
|
|
106
|
+
for line in example.split("\n"):
|
|
107
|
+
emit_info(f" {line}")
|
|
108
|
+
emit_info("")
|
|
109
|
+
|
|
110
|
+
# Get JSON configuration
|
|
111
|
+
emit_info(" Enter your JSON configuration (paste and press Enter twice):\n")
|
|
112
|
+
|
|
113
|
+
json_lines = []
|
|
114
|
+
empty_count = 0
|
|
115
|
+
try:
|
|
116
|
+
while True:
|
|
117
|
+
line = safe_input("")
|
|
118
|
+
if line == "":
|
|
119
|
+
empty_count += 1
|
|
120
|
+
if empty_count >= 2:
|
|
121
|
+
break
|
|
122
|
+
json_lines.append(line)
|
|
123
|
+
else:
|
|
124
|
+
empty_count = 0
|
|
125
|
+
json_lines.append(line)
|
|
126
|
+
except (KeyboardInterrupt, EOFError):
|
|
127
|
+
emit_info("")
|
|
128
|
+
emit_warning("Cancelled")
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
json_str = "\n".join(json_lines).strip()
|
|
132
|
+
if not json_str:
|
|
133
|
+
emit_warning("No configuration provided")
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
# Parse JSON
|
|
137
|
+
try:
|
|
138
|
+
config_dict = json.loads(json_str)
|
|
139
|
+
except json.JSONDecodeError as e:
|
|
140
|
+
emit_error(f"Invalid JSON: {e}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
# Validate required fields based on type
|
|
144
|
+
if server_type == "stdio":
|
|
145
|
+
if "command" not in config_dict:
|
|
146
|
+
emit_error("stdio servers require a 'command' field")
|
|
147
|
+
return False
|
|
148
|
+
elif server_type in ("http", "sse"):
|
|
149
|
+
if "url" not in config_dict:
|
|
150
|
+
emit_error(f"{server_type} servers require a 'url' field")
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
# Create server config
|
|
154
|
+
try:
|
|
155
|
+
server_config = ServerConfig(
|
|
156
|
+
id=server_name,
|
|
157
|
+
name=server_name,
|
|
158
|
+
type=server_type,
|
|
159
|
+
enabled=True,
|
|
160
|
+
config=config_dict,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Register with manager
|
|
164
|
+
server_id = manager.register_server(server_config)
|
|
165
|
+
|
|
166
|
+
if not server_id:
|
|
167
|
+
emit_error("Failed to register server")
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
# Save to mcp_servers.json for persistence
|
|
171
|
+
if os.path.exists(MCP_SERVERS_FILE):
|
|
172
|
+
with open(MCP_SERVERS_FILE, "r") as f:
|
|
173
|
+
data = json.load(f)
|
|
174
|
+
servers = data.get("mcp_servers", {})
|
|
175
|
+
else:
|
|
176
|
+
servers = {}
|
|
177
|
+
data = {"mcp_servers": servers}
|
|
178
|
+
|
|
179
|
+
# Add new server with type
|
|
180
|
+
save_config = config_dict.copy()
|
|
181
|
+
save_config["type"] = server_type
|
|
182
|
+
servers[server_name] = save_config
|
|
183
|
+
|
|
184
|
+
# Save back
|
|
185
|
+
os.makedirs(os.path.dirname(MCP_SERVERS_FILE), exist_ok=True)
|
|
186
|
+
with open(MCP_SERVERS_FILE, "w") as f:
|
|
187
|
+
json.dump(data, f, indent=2)
|
|
188
|
+
|
|
189
|
+
emit_success(f"\n ✅ Successfully added custom server '{server_name}'!")
|
|
190
|
+
emit_info(f" Use '/mcp start {server_name}' to start the server.\n")
|
|
191
|
+
return True
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
emit_error(f"Failed to add server: {e}")
|
|
195
|
+
return False
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""MCP Edit Command - Edit existing MCP server configurations.
|
|
2
|
+
|
|
3
|
+
Provides a TUI for editing custom MCP server configurations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from code_puppy.config import MCP_SERVERS_FILE
|
|
14
|
+
from code_puppy.messaging import emit_error, emit_info, emit_warning
|
|
15
|
+
|
|
16
|
+
from .base import MCPCommandBase
|
|
17
|
+
from .custom_server_form import run_custom_server_form
|
|
18
|
+
|
|
19
|
+
# Configure logging
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class EditCommand(MCPCommandBase):
|
|
24
|
+
"""Command handler for editing existing MCP servers.
|
|
25
|
+
|
|
26
|
+
Opens the same TUI form as /mcp install custom, but pre-populated
|
|
27
|
+
with the existing server's configuration.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
31
|
+
"""Edit an existing MCP server configuration.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
args: Server name to edit
|
|
35
|
+
group_id: Optional message group ID for grouping related messages
|
|
36
|
+
"""
|
|
37
|
+
if group_id is None:
|
|
38
|
+
group_id = self.generate_group_id()
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
# Need a server name
|
|
42
|
+
if not args:
|
|
43
|
+
emit_info(
|
|
44
|
+
Text.from_markup("[yellow]Usage: /mcp edit <server_name>[/yellow]"),
|
|
45
|
+
message_group=group_id,
|
|
46
|
+
)
|
|
47
|
+
emit_info(
|
|
48
|
+
"Use '/mcp list' to see available servers.",
|
|
49
|
+
message_group=group_id,
|
|
50
|
+
)
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
server_name = args[0]
|
|
54
|
+
|
|
55
|
+
# Load existing server config
|
|
56
|
+
server_config = self._load_server_config(server_name, group_id)
|
|
57
|
+
if server_config is None:
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
server_type, config_dict = server_config
|
|
61
|
+
|
|
62
|
+
# Run the form in edit mode
|
|
63
|
+
success = run_custom_server_form(
|
|
64
|
+
self.manager,
|
|
65
|
+
edit_mode=True,
|
|
66
|
+
existing_name=server_name,
|
|
67
|
+
existing_type=server_type,
|
|
68
|
+
existing_config=config_dict,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if success:
|
|
72
|
+
# Reload MCP servers to pick up changes
|
|
73
|
+
try:
|
|
74
|
+
from code_puppy.agent import reload_mcp_servers
|
|
75
|
+
|
|
76
|
+
reload_mcp_servers()
|
|
77
|
+
except ImportError:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"Error editing server: {e}")
|
|
82
|
+
emit_error(f"Error: {e}", message_group=group_id)
|
|
83
|
+
|
|
84
|
+
def _load_server_config(
|
|
85
|
+
self, server_name: str, group_id: str
|
|
86
|
+
) -> Optional[tuple[str, dict]]:
|
|
87
|
+
"""Load an existing server configuration from mcp_servers.json.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
server_name: Name of the server to load
|
|
91
|
+
group_id: Message group ID for output
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Tuple of (server_type, config_dict) or None if not found
|
|
95
|
+
"""
|
|
96
|
+
if not os.path.exists(MCP_SERVERS_FILE):
|
|
97
|
+
emit_error(
|
|
98
|
+
"No MCP servers configured yet.",
|
|
99
|
+
message_group=group_id,
|
|
100
|
+
)
|
|
101
|
+
emit_info(
|
|
102
|
+
"Use '/mcp install' to add a server first.",
|
|
103
|
+
message_group=group_id,
|
|
104
|
+
)
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
with open(MCP_SERVERS_FILE, "r") as f:
|
|
109
|
+
data = json.load(f)
|
|
110
|
+
|
|
111
|
+
servers = data.get("mcp_servers", {})
|
|
112
|
+
|
|
113
|
+
if server_name not in servers:
|
|
114
|
+
emit_error(
|
|
115
|
+
f"Server '{server_name}' not found.",
|
|
116
|
+
message_group=group_id,
|
|
117
|
+
)
|
|
118
|
+
# Show available servers
|
|
119
|
+
if servers:
|
|
120
|
+
emit_warning(
|
|
121
|
+
"\nAvailable servers:",
|
|
122
|
+
message_group=group_id,
|
|
123
|
+
)
|
|
124
|
+
for name in sorted(servers.keys()):
|
|
125
|
+
emit_info(f" • {name}", message_group=group_id)
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
config = servers[
|
|
129
|
+
server_name
|
|
130
|
+
].copy() # Make a copy to avoid modifying original
|
|
131
|
+
|
|
132
|
+
# Extract type from config (default to stdio)
|
|
133
|
+
server_type = config.pop("type", "stdio")
|
|
134
|
+
|
|
135
|
+
return (server_type, config)
|
|
136
|
+
|
|
137
|
+
except json.JSONDecodeError as e:
|
|
138
|
+
emit_error(
|
|
139
|
+
f"Error reading config file: {e}",
|
|
140
|
+
message_group=group_id,
|
|
141
|
+
)
|
|
142
|
+
return None
|
|
143
|
+
except Exception as e:
|
|
144
|
+
emit_error(
|
|
145
|
+
f"Error loading server config: {e}",
|
|
146
|
+
message_group=group_id,
|
|
147
|
+
)
|
|
148
|
+
return None
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Command Handler - Main router for MCP server management commands.
|
|
3
|
+
|
|
4
|
+
This module provides the MCPCommandHandler class that routes MCP commands
|
|
5
|
+
to their respective command modules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import shlex
|
|
10
|
+
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from code_puppy.messaging import emit_info
|
|
14
|
+
|
|
15
|
+
from .base import MCPCommandBase
|
|
16
|
+
from .edit_command import EditCommand
|
|
17
|
+
from .help_command import HelpCommand
|
|
18
|
+
from .install_command import InstallCommand
|
|
19
|
+
|
|
20
|
+
# Import all command modules
|
|
21
|
+
from .list_command import ListCommand
|
|
22
|
+
from .logs_command import LogsCommand
|
|
23
|
+
from .remove_command import RemoveCommand
|
|
24
|
+
from .restart_command import RestartCommand
|
|
25
|
+
from .search_command import SearchCommand
|
|
26
|
+
from .start_all_command import StartAllCommand
|
|
27
|
+
from .start_command import StartCommand
|
|
28
|
+
from .status_command import StatusCommand
|
|
29
|
+
from .stop_all_command import StopAllCommand
|
|
30
|
+
from .stop_command import StopCommand
|
|
31
|
+
from .test_command import TestCommand
|
|
32
|
+
|
|
33
|
+
# Configure logging
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MCPCommandHandler(MCPCommandBase):
|
|
38
|
+
"""
|
|
39
|
+
Main command handler for MCP server management operations.
|
|
40
|
+
|
|
41
|
+
Routes MCP commands to their respective command modules.
|
|
42
|
+
Each command is implemented in its own module for better maintainability.
|
|
43
|
+
|
|
44
|
+
Example usage:
|
|
45
|
+
handler = MCPCommandHandler()
|
|
46
|
+
handler.handle_mcp_command("/mcp list")
|
|
47
|
+
handler.handle_mcp_command("/mcp start filesystem")
|
|
48
|
+
handler.handle_mcp_command("/mcp status filesystem")
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self):
|
|
52
|
+
"""Initialize the MCP command handler."""
|
|
53
|
+
super().__init__()
|
|
54
|
+
|
|
55
|
+
# Initialize command handlers
|
|
56
|
+
self._commands = {
|
|
57
|
+
"list": ListCommand(),
|
|
58
|
+
"start": StartCommand(),
|
|
59
|
+
"start-all": StartAllCommand(),
|
|
60
|
+
"stop": StopCommand(),
|
|
61
|
+
"stop-all": StopAllCommand(),
|
|
62
|
+
"restart": RestartCommand(),
|
|
63
|
+
"status": StatusCommand(),
|
|
64
|
+
"test": TestCommand(),
|
|
65
|
+
"edit": EditCommand(),
|
|
66
|
+
"remove": RemoveCommand(),
|
|
67
|
+
"logs": LogsCommand(),
|
|
68
|
+
"search": SearchCommand(),
|
|
69
|
+
"install": InstallCommand(),
|
|
70
|
+
"help": HelpCommand(),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
logger.info("MCPCommandHandler initialized with all command modules")
|
|
74
|
+
|
|
75
|
+
def handle_mcp_command(self, command: str) -> bool:
|
|
76
|
+
"""
|
|
77
|
+
Handle MCP commands and route to appropriate handler.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
command: The full command string (e.g., "/mcp list", "/mcp start server")
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
True if command was handled successfully, False otherwise
|
|
84
|
+
"""
|
|
85
|
+
group_id = self.generate_group_id()
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
# Remove /mcp prefix and parse arguments
|
|
89
|
+
command = command.strip()
|
|
90
|
+
if not command.startswith("/mcp"):
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
# Remove the /mcp prefix
|
|
94
|
+
args_str = command[4:].strip()
|
|
95
|
+
|
|
96
|
+
# If no subcommand, show status dashboard
|
|
97
|
+
if not args_str:
|
|
98
|
+
self._commands["list"].execute([], group_id=group_id)
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
# Parse arguments using shlex for proper handling of quoted strings
|
|
102
|
+
try:
|
|
103
|
+
args = shlex.split(args_str)
|
|
104
|
+
except ValueError as e:
|
|
105
|
+
emit_info(
|
|
106
|
+
Text.from_markup(f"[red]Invalid command syntax: {e}[/red]"),
|
|
107
|
+
message_group=group_id,
|
|
108
|
+
)
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
if not args:
|
|
112
|
+
self._commands["list"].execute([], group_id=group_id)
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
subcommand = args[0].lower()
|
|
116
|
+
sub_args = args[1:] if len(args) > 1 else []
|
|
117
|
+
|
|
118
|
+
# Route to appropriate command handler
|
|
119
|
+
command_handler = self._commands.get(subcommand)
|
|
120
|
+
if command_handler:
|
|
121
|
+
command_handler.execute(sub_args, group_id=group_id)
|
|
122
|
+
return True
|
|
123
|
+
else:
|
|
124
|
+
emit_info(
|
|
125
|
+
Text.from_markup(
|
|
126
|
+
f"[yellow]Unknown MCP subcommand: {subcommand}[/yellow]"
|
|
127
|
+
),
|
|
128
|
+
message_group=group_id,
|
|
129
|
+
)
|
|
130
|
+
emit_info(
|
|
131
|
+
"Type '/mcp help' for available commands", message_group=group_id
|
|
132
|
+
)
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(f"Error handling MCP command '{command}': {e}")
|
|
137
|
+
emit_info(f"Error executing MCP command: {e}", message_group=group_id)
|
|
138
|
+
return True
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Help Command - Shows help for all MCP commands.
|
|
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 .base import MCPCommandBase
|
|
13
|
+
|
|
14
|
+
# Configure logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HelpCommand(MCPCommandBase):
|
|
19
|
+
"""
|
|
20
|
+
Command handler for showing MCP command help.
|
|
21
|
+
|
|
22
|
+
Displays comprehensive help information for all available MCP commands.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def execute(self, args: List[str], group_id: Optional[str] = None) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Show help for MCP commands.
|
|
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
|
+
# Build help text programmatically to avoid markup conflicts
|
|
38
|
+
help_lines = []
|
|
39
|
+
|
|
40
|
+
# Title
|
|
41
|
+
help_lines.append(
|
|
42
|
+
Text("MCP Server Management Commands", style="bold magenta")
|
|
43
|
+
)
|
|
44
|
+
help_lines.append(Text(""))
|
|
45
|
+
|
|
46
|
+
# Registry Commands
|
|
47
|
+
help_lines.append(Text("Registry Commands:", style="bold cyan"))
|
|
48
|
+
help_lines.append(
|
|
49
|
+
Text("/mcp search", style="cyan")
|
|
50
|
+
+ Text(" [query] Search 30+ pre-configured servers")
|
|
51
|
+
)
|
|
52
|
+
help_lines.append(
|
|
53
|
+
Text("/mcp install", style="cyan")
|
|
54
|
+
+ Text(" <id> Install server from registry")
|
|
55
|
+
)
|
|
56
|
+
help_lines.append(Text(""))
|
|
57
|
+
|
|
58
|
+
# Core Commands
|
|
59
|
+
help_lines.append(Text("Core Commands:", style="bold cyan"))
|
|
60
|
+
help_lines.append(
|
|
61
|
+
Text("/mcp", style="cyan")
|
|
62
|
+
+ Text(" Show server status dashboard")
|
|
63
|
+
)
|
|
64
|
+
help_lines.append(
|
|
65
|
+
Text("/mcp list", style="cyan")
|
|
66
|
+
+ Text(" List all registered servers")
|
|
67
|
+
)
|
|
68
|
+
help_lines.append(
|
|
69
|
+
Text("/mcp start", style="cyan")
|
|
70
|
+
+ Text(" <name> Start a specific server")
|
|
71
|
+
)
|
|
72
|
+
help_lines.append(
|
|
73
|
+
Text("/mcp start-all", style="cyan")
|
|
74
|
+
+ Text(" Start all servers")
|
|
75
|
+
)
|
|
76
|
+
help_lines.append(
|
|
77
|
+
Text("/mcp stop", style="cyan")
|
|
78
|
+
+ Text(" <name> Stop a specific server")
|
|
79
|
+
)
|
|
80
|
+
help_lines.append(
|
|
81
|
+
Text("/mcp stop-all", style="cyan")
|
|
82
|
+
+ Text(" [group_id] Stop all running servers")
|
|
83
|
+
)
|
|
84
|
+
help_lines.append(
|
|
85
|
+
Text("/mcp restart", style="cyan")
|
|
86
|
+
+ Text(" <name> Restart a specific server")
|
|
87
|
+
)
|
|
88
|
+
help_lines.append(Text(""))
|
|
89
|
+
|
|
90
|
+
# Management Commands
|
|
91
|
+
help_lines.append(Text("Management Commands:", style="bold cyan"))
|
|
92
|
+
help_lines.append(
|
|
93
|
+
Text("/mcp status", style="cyan")
|
|
94
|
+
+ Text(" [name] Show detailed status (all servers or specific)")
|
|
95
|
+
)
|
|
96
|
+
help_lines.append(
|
|
97
|
+
Text("/mcp test", style="cyan")
|
|
98
|
+
+ Text(" <name> Test connectivity to a server")
|
|
99
|
+
)
|
|
100
|
+
help_lines.append(
|
|
101
|
+
Text("/mcp logs", style="cyan")
|
|
102
|
+
+ Text(" <name> [limit] Show recent events (default limit: 10)")
|
|
103
|
+
)
|
|
104
|
+
help_lines.append(
|
|
105
|
+
Text("/mcp edit", style="cyan")
|
|
106
|
+
+ Text(" <name> Edit existing server config")
|
|
107
|
+
)
|
|
108
|
+
help_lines.append(
|
|
109
|
+
Text("/mcp remove", style="cyan")
|
|
110
|
+
+ Text(" <name> Remove/disable a server")
|
|
111
|
+
)
|
|
112
|
+
help_lines.append(
|
|
113
|
+
Text("/mcp help", style="cyan")
|
|
114
|
+
+ Text(" Show this help message")
|
|
115
|
+
)
|
|
116
|
+
help_lines.append(Text(""))
|
|
117
|
+
|
|
118
|
+
# Status Indicators
|
|
119
|
+
help_lines.append(Text("Status Indicators:", style="bold"))
|
|
120
|
+
help_lines.append(
|
|
121
|
+
Text("✓ Running ✗ Stopped ⚠ Error ⏸ Quarantined ⭐ Popular")
|
|
122
|
+
)
|
|
123
|
+
help_lines.append(Text(""))
|
|
124
|
+
|
|
125
|
+
# Examples
|
|
126
|
+
help_lines.append(Text("Examples:", style="bold"))
|
|
127
|
+
examples_text = """/mcp search database # Find database servers
|
|
128
|
+
/mcp install postgres # Install PostgreSQL server
|
|
129
|
+
/mcp start filesystem # Start a specific server
|
|
130
|
+
/mcp start-all # Start all servers at once
|
|
131
|
+
/mcp stop-all # Stop all running servers
|
|
132
|
+
/mcp edit filesystem # Edit an existing server config
|
|
133
|
+
/mcp remove filesystem # Remove a server"""
|
|
134
|
+
help_lines.append(Text(examples_text, style="dim"))
|
|
135
|
+
|
|
136
|
+
# Combine all lines
|
|
137
|
+
final_text = Text()
|
|
138
|
+
for i, line in enumerate(help_lines):
|
|
139
|
+
if i > 0:
|
|
140
|
+
final_text.append("\n")
|
|
141
|
+
final_text.append_text(line)
|
|
142
|
+
|
|
143
|
+
emit_info(final_text, message_group=group_id)
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.error(f"Error showing help: {e}")
|
|
147
|
+
emit_error(f"Error showing help: {e}", message_group=group_id)
|