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.
Files changed (288) hide show
  1. code_puppy/__init__.py +10 -0
  2. code_puppy/__main__.py +10 -0
  3. code_puppy/agents/__init__.py +31 -0
  4. code_puppy/agents/agent_c_reviewer.py +155 -0
  5. code_puppy/agents/agent_code_puppy.py +117 -0
  6. code_puppy/agents/agent_code_reviewer.py +90 -0
  7. code_puppy/agents/agent_cpp_reviewer.py +132 -0
  8. code_puppy/agents/agent_creator_agent.py +638 -0
  9. code_puppy/agents/agent_golang_reviewer.py +151 -0
  10. code_puppy/agents/agent_helios.py +124 -0
  11. code_puppy/agents/agent_javascript_reviewer.py +160 -0
  12. code_puppy/agents/agent_manager.py +742 -0
  13. code_puppy/agents/agent_pack_leader.py +385 -0
  14. code_puppy/agents/agent_planning.py +165 -0
  15. code_puppy/agents/agent_python_programmer.py +169 -0
  16. code_puppy/agents/agent_python_reviewer.py +90 -0
  17. code_puppy/agents/agent_qa_expert.py +163 -0
  18. code_puppy/agents/agent_qa_kitten.py +208 -0
  19. code_puppy/agents/agent_scheduler.py +121 -0
  20. code_puppy/agents/agent_security_auditor.py +181 -0
  21. code_puppy/agents/agent_terminal_qa.py +323 -0
  22. code_puppy/agents/agent_typescript_reviewer.py +166 -0
  23. code_puppy/agents/base_agent.py +2156 -0
  24. code_puppy/agents/event_stream_handler.py +348 -0
  25. code_puppy/agents/json_agent.py +202 -0
  26. code_puppy/agents/pack/__init__.py +34 -0
  27. code_puppy/agents/pack/bloodhound.py +304 -0
  28. code_puppy/agents/pack/husky.py +327 -0
  29. code_puppy/agents/pack/retriever.py +393 -0
  30. code_puppy/agents/pack/shepherd.py +348 -0
  31. code_puppy/agents/pack/terrier.py +287 -0
  32. code_puppy/agents/pack/watchdog.py +367 -0
  33. code_puppy/agents/prompt_reviewer.py +145 -0
  34. code_puppy/agents/subagent_stream_handler.py +276 -0
  35. code_puppy/api/__init__.py +13 -0
  36. code_puppy/api/app.py +169 -0
  37. code_puppy/api/main.py +21 -0
  38. code_puppy/api/pty_manager.py +453 -0
  39. code_puppy/api/routers/__init__.py +12 -0
  40. code_puppy/api/routers/agents.py +36 -0
  41. code_puppy/api/routers/commands.py +217 -0
  42. code_puppy/api/routers/config.py +75 -0
  43. code_puppy/api/routers/sessions.py +234 -0
  44. code_puppy/api/templates/terminal.html +361 -0
  45. code_puppy/api/websocket.py +154 -0
  46. code_puppy/callbacks.py +692 -0
  47. code_puppy/chatgpt_codex_client.py +338 -0
  48. code_puppy/claude_cache_client.py +672 -0
  49. code_puppy/cli_runner.py +1073 -0
  50. code_puppy/command_line/__init__.py +1 -0
  51. code_puppy/command_line/add_model_menu.py +1092 -0
  52. code_puppy/command_line/agent_menu.py +662 -0
  53. code_puppy/command_line/attachments.py +395 -0
  54. code_puppy/command_line/autosave_menu.py +704 -0
  55. code_puppy/command_line/clipboard.py +527 -0
  56. code_puppy/command_line/colors_menu.py +532 -0
  57. code_puppy/command_line/command_handler.py +293 -0
  58. code_puppy/command_line/command_registry.py +150 -0
  59. code_puppy/command_line/config_commands.py +719 -0
  60. code_puppy/command_line/core_commands.py +867 -0
  61. code_puppy/command_line/diff_menu.py +865 -0
  62. code_puppy/command_line/file_path_completion.py +73 -0
  63. code_puppy/command_line/load_context_completion.py +52 -0
  64. code_puppy/command_line/mcp/__init__.py +10 -0
  65. code_puppy/command_line/mcp/base.py +32 -0
  66. code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
  67. code_puppy/command_line/mcp/custom_server_form.py +688 -0
  68. code_puppy/command_line/mcp/custom_server_installer.py +195 -0
  69. code_puppy/command_line/mcp/edit_command.py +148 -0
  70. code_puppy/command_line/mcp/handler.py +138 -0
  71. code_puppy/command_line/mcp/help_command.py +147 -0
  72. code_puppy/command_line/mcp/install_command.py +214 -0
  73. code_puppy/command_line/mcp/install_menu.py +705 -0
  74. code_puppy/command_line/mcp/list_command.py +94 -0
  75. code_puppy/command_line/mcp/logs_command.py +235 -0
  76. code_puppy/command_line/mcp/remove_command.py +82 -0
  77. code_puppy/command_line/mcp/restart_command.py +100 -0
  78. code_puppy/command_line/mcp/search_command.py +123 -0
  79. code_puppy/command_line/mcp/start_all_command.py +135 -0
  80. code_puppy/command_line/mcp/start_command.py +117 -0
  81. code_puppy/command_line/mcp/status_command.py +184 -0
  82. code_puppy/command_line/mcp/stop_all_command.py +112 -0
  83. code_puppy/command_line/mcp/stop_command.py +80 -0
  84. code_puppy/command_line/mcp/test_command.py +107 -0
  85. code_puppy/command_line/mcp/utils.py +129 -0
  86. code_puppy/command_line/mcp/wizard_utils.py +334 -0
  87. code_puppy/command_line/mcp_completion.py +174 -0
  88. code_puppy/command_line/model_picker_completion.py +197 -0
  89. code_puppy/command_line/model_settings_menu.py +932 -0
  90. code_puppy/command_line/motd.py +96 -0
  91. code_puppy/command_line/onboarding_slides.py +179 -0
  92. code_puppy/command_line/onboarding_wizard.py +342 -0
  93. code_puppy/command_line/pin_command_completion.py +329 -0
  94. code_puppy/command_line/prompt_toolkit_completion.py +846 -0
  95. code_puppy/command_line/session_commands.py +302 -0
  96. code_puppy/command_line/shell_passthrough.py +145 -0
  97. code_puppy/command_line/skills_completion.py +160 -0
  98. code_puppy/command_line/uc_menu.py +893 -0
  99. code_puppy/command_line/utils.py +93 -0
  100. code_puppy/command_line/wiggum_state.py +78 -0
  101. code_puppy/config.py +1770 -0
  102. code_puppy/error_logging.py +134 -0
  103. code_puppy/gemini_code_assist.py +385 -0
  104. code_puppy/gemini_model.py +754 -0
  105. code_puppy/hook_engine/README.md +105 -0
  106. code_puppy/hook_engine/__init__.py +21 -0
  107. code_puppy/hook_engine/aliases.py +155 -0
  108. code_puppy/hook_engine/engine.py +221 -0
  109. code_puppy/hook_engine/executor.py +296 -0
  110. code_puppy/hook_engine/matcher.py +156 -0
  111. code_puppy/hook_engine/models.py +240 -0
  112. code_puppy/hook_engine/registry.py +106 -0
  113. code_puppy/hook_engine/validator.py +144 -0
  114. code_puppy/http_utils.py +361 -0
  115. code_puppy/keymap.py +128 -0
  116. code_puppy/main.py +10 -0
  117. code_puppy/mcp_/__init__.py +66 -0
  118. code_puppy/mcp_/async_lifecycle.py +286 -0
  119. code_puppy/mcp_/blocking_startup.py +469 -0
  120. code_puppy/mcp_/captured_stdio_server.py +275 -0
  121. code_puppy/mcp_/circuit_breaker.py +290 -0
  122. code_puppy/mcp_/config_wizard.py +507 -0
  123. code_puppy/mcp_/dashboard.py +308 -0
  124. code_puppy/mcp_/error_isolation.py +407 -0
  125. code_puppy/mcp_/examples/retry_example.py +226 -0
  126. code_puppy/mcp_/health_monitor.py +589 -0
  127. code_puppy/mcp_/managed_server.py +428 -0
  128. code_puppy/mcp_/manager.py +807 -0
  129. code_puppy/mcp_/mcp_logs.py +224 -0
  130. code_puppy/mcp_/registry.py +451 -0
  131. code_puppy/mcp_/retry_manager.py +337 -0
  132. code_puppy/mcp_/server_registry_catalog.py +1126 -0
  133. code_puppy/mcp_/status_tracker.py +355 -0
  134. code_puppy/mcp_/system_tools.py +209 -0
  135. code_puppy/mcp_prompts/__init__.py +1 -0
  136. code_puppy/mcp_prompts/hook_creator.py +103 -0
  137. code_puppy/messaging/__init__.py +255 -0
  138. code_puppy/messaging/bus.py +613 -0
  139. code_puppy/messaging/commands.py +167 -0
  140. code_puppy/messaging/markdown_patches.py +57 -0
  141. code_puppy/messaging/message_queue.py +361 -0
  142. code_puppy/messaging/messages.py +569 -0
  143. code_puppy/messaging/queue_console.py +271 -0
  144. code_puppy/messaging/renderers.py +311 -0
  145. code_puppy/messaging/rich_renderer.py +1158 -0
  146. code_puppy/messaging/spinner/__init__.py +83 -0
  147. code_puppy/messaging/spinner/console_spinner.py +240 -0
  148. code_puppy/messaging/spinner/spinner_base.py +95 -0
  149. code_puppy/messaging/subagent_console.py +460 -0
  150. code_puppy/model_factory.py +848 -0
  151. code_puppy/model_switching.py +63 -0
  152. code_puppy/model_utils.py +168 -0
  153. code_puppy/models.json +174 -0
  154. code_puppy/models_dev_api.json +1 -0
  155. code_puppy/models_dev_parser.py +592 -0
  156. code_puppy/plugins/__init__.py +186 -0
  157. code_puppy/plugins/agent_skills/__init__.py +22 -0
  158. code_puppy/plugins/agent_skills/config.py +175 -0
  159. code_puppy/plugins/agent_skills/discovery.py +136 -0
  160. code_puppy/plugins/agent_skills/downloader.py +392 -0
  161. code_puppy/plugins/agent_skills/installer.py +22 -0
  162. code_puppy/plugins/agent_skills/metadata.py +219 -0
  163. code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
  164. code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
  165. code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
  166. code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
  167. code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
  168. code_puppy/plugins/agent_skills/skills_menu.py +781 -0
  169. code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  170. code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  171. code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
  172. code_puppy/plugins/antigravity_oauth/config.py +42 -0
  173. code_puppy/plugins/antigravity_oauth/constants.py +133 -0
  174. code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  175. code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
  176. code_puppy/plugins/antigravity_oauth/storage.py +288 -0
  177. code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  178. code_puppy/plugins/antigravity_oauth/token.py +167 -0
  179. code_puppy/plugins/antigravity_oauth/transport.py +863 -0
  180. code_puppy/plugins/antigravity_oauth/utils.py +168 -0
  181. code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
  182. code_puppy/plugins/chatgpt_oauth/config.py +52 -0
  183. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +329 -0
  184. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
  185. code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
  186. code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
  187. code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
  188. code_puppy/plugins/claude_code_hooks/config.py +137 -0
  189. code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -0
  190. code_puppy/plugins/claude_code_oauth/README.md +167 -0
  191. code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
  192. code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
  193. code_puppy/plugins/claude_code_oauth/config.py +52 -0
  194. code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
  195. code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
  196. code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
  197. code_puppy/plugins/claude_code_oauth/utils.py +640 -0
  198. code_puppy/plugins/customizable_commands/__init__.py +0 -0
  199. code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
  200. code_puppy/plugins/example_custom_command/README.md +280 -0
  201. code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
  202. code_puppy/plugins/file_permission_handler/__init__.py +4 -0
  203. code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -0
  204. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  205. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  206. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  207. code_puppy/plugins/hook_creator/__init__.py +1 -0
  208. code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
  209. code_puppy/plugins/hook_manager/__init__.py +1 -0
  210. code_puppy/plugins/hook_manager/config.py +290 -0
  211. code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
  212. code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
  213. code_puppy/plugins/oauth_puppy_html.py +228 -0
  214. code_puppy/plugins/scheduler/__init__.py +1 -0
  215. code_puppy/plugins/scheduler/register_callbacks.py +88 -0
  216. code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
  217. code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
  218. code_puppy/plugins/shell_safety/__init__.py +6 -0
  219. code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
  220. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  221. code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
  222. code_puppy/plugins/synthetic_status/__init__.py +1 -0
  223. code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
  224. code_puppy/plugins/synthetic_status/status_api.py +147 -0
  225. code_puppy/plugins/universal_constructor/__init__.py +13 -0
  226. code_puppy/plugins/universal_constructor/models.py +138 -0
  227. code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
  228. code_puppy/plugins/universal_constructor/registry.py +302 -0
  229. code_puppy/plugins/universal_constructor/sandbox.py +584 -0
  230. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  231. code_puppy/pydantic_patches.py +356 -0
  232. code_puppy/reopenable_async_client.py +232 -0
  233. code_puppy/round_robin_model.py +150 -0
  234. code_puppy/scheduler/__init__.py +41 -0
  235. code_puppy/scheduler/__main__.py +9 -0
  236. code_puppy/scheduler/cli.py +118 -0
  237. code_puppy/scheduler/config.py +126 -0
  238. code_puppy/scheduler/daemon.py +280 -0
  239. code_puppy/scheduler/executor.py +155 -0
  240. code_puppy/scheduler/platform.py +19 -0
  241. code_puppy/scheduler/platform_unix.py +22 -0
  242. code_puppy/scheduler/platform_win.py +32 -0
  243. code_puppy/session_storage.py +338 -0
  244. code_puppy/status_display.py +257 -0
  245. code_puppy/summarization_agent.py +176 -0
  246. code_puppy/terminal_utils.py +418 -0
  247. code_puppy/tools/__init__.py +501 -0
  248. code_puppy/tools/agent_tools.py +603 -0
  249. code_puppy/tools/ask_user_question/__init__.py +26 -0
  250. code_puppy/tools/ask_user_question/constants.py +73 -0
  251. code_puppy/tools/ask_user_question/demo_tui.py +55 -0
  252. code_puppy/tools/ask_user_question/handler.py +232 -0
  253. code_puppy/tools/ask_user_question/models.py +304 -0
  254. code_puppy/tools/ask_user_question/registration.py +26 -0
  255. code_puppy/tools/ask_user_question/renderers.py +309 -0
  256. code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
  257. code_puppy/tools/ask_user_question/theme.py +155 -0
  258. code_puppy/tools/ask_user_question/tui_loop.py +423 -0
  259. code_puppy/tools/browser/__init__.py +37 -0
  260. code_puppy/tools/browser/browser_control.py +289 -0
  261. code_puppy/tools/browser/browser_interactions.py +545 -0
  262. code_puppy/tools/browser/browser_locators.py +640 -0
  263. code_puppy/tools/browser/browser_manager.py +378 -0
  264. code_puppy/tools/browser/browser_navigation.py +251 -0
  265. code_puppy/tools/browser/browser_screenshot.py +179 -0
  266. code_puppy/tools/browser/browser_scripts.py +462 -0
  267. code_puppy/tools/browser/browser_workflows.py +221 -0
  268. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  269. code_puppy/tools/browser/terminal_command_tools.py +534 -0
  270. code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
  271. code_puppy/tools/browser/terminal_tools.py +525 -0
  272. code_puppy/tools/command_runner.py +1346 -0
  273. code_puppy/tools/common.py +1409 -0
  274. code_puppy/tools/display.py +84 -0
  275. code_puppy/tools/file_modifications.py +886 -0
  276. code_puppy/tools/file_operations.py +802 -0
  277. code_puppy/tools/scheduler_tools.py +412 -0
  278. code_puppy/tools/skills_tools.py +244 -0
  279. code_puppy/tools/subagent_context.py +158 -0
  280. code_puppy/tools/tools_content.py +51 -0
  281. code_puppy/tools/universal_constructor.py +889 -0
  282. code_puppy/uvx_detection.py +242 -0
  283. code_puppy/version_checker.py +82 -0
  284. codepp-0.0.437.dist-info/METADATA +766 -0
  285. codepp-0.0.437.dist-info/RECORD +288 -0
  286. codepp-0.0.437.dist-info/WHEEL +4 -0
  287. codepp-0.0.437.dist-info/entry_points.txt +3 -0
  288. 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)