code-puppy 0.0.130__py3-none-any.whl → 0.0.131__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/command_line/mcp_commands.py +659 -101
- code_puppy/mcp/config_wizard.py +151 -117
- code_puppy/mcp/server_registry_catalog.py +346 -46
- code_puppy/mcp/system_tools.py +214 -0
- code_puppy/messaging/__init__.py +4 -0
- code_puppy/messaging/message_queue.py +86 -0
- code_puppy/messaging/renderers.py +94 -0
- code_puppy/tui/app.py +24 -1
- code_puppy/tui/components/chat_view.py +33 -18
- code_puppy/tui/components/human_input_modal.py +171 -0
- code_puppy/tui/screens/__init__.py +3 -1
- code_puppy/tui/screens/mcp_install_wizard.py +593 -0
- {code_puppy-0.0.130.dist-info → code_puppy-0.0.131.dist-info}/METADATA +1 -1
- {code_puppy-0.0.130.dist-info → code_puppy-0.0.131.dist-info}/RECORD +18 -15
- {code_puppy-0.0.130.data → code_puppy-0.0.131.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.130.dist-info → code_puppy-0.0.131.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.130.dist-info → code_puppy-0.0.131.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.130.dist-info → code_puppy-0.0.131.dist-info}/licenses/LICENSE +0 -0
code_puppy/mcp/config_wizard.py
CHANGED
|
@@ -7,46 +7,101 @@ from typing import Dict, Optional
|
|
|
7
7
|
from urllib.parse import urlparse
|
|
8
8
|
|
|
9
9
|
from code_puppy.mcp import ServerConfig, get_mcp_manager
|
|
10
|
-
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
11
|
-
from rich.prompt import Prompt, Confirm
|
|
10
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning, emit_prompt
|
|
12
11
|
from rich.console import Console
|
|
13
12
|
|
|
14
13
|
console = Console()
|
|
15
14
|
|
|
16
15
|
|
|
16
|
+
def prompt_ask(prompt_text: str, default: Optional[str] = None, choices: Optional[list] = None) -> Optional[str]:
|
|
17
|
+
"""Helper function to replace rich.prompt.Prompt.ask with emit_prompt."""
|
|
18
|
+
try:
|
|
19
|
+
if default:
|
|
20
|
+
full_prompt = f"{prompt_text} [{default}]"
|
|
21
|
+
else:
|
|
22
|
+
full_prompt = prompt_text
|
|
23
|
+
|
|
24
|
+
if choices:
|
|
25
|
+
full_prompt += f" ({'/'.join(choices)})"
|
|
26
|
+
|
|
27
|
+
response = emit_prompt(full_prompt + ": ")
|
|
28
|
+
|
|
29
|
+
# Handle default value
|
|
30
|
+
if not response.strip() and default:
|
|
31
|
+
return default
|
|
32
|
+
|
|
33
|
+
# Handle choices validation
|
|
34
|
+
if choices and response.strip() and response.strip() not in choices:
|
|
35
|
+
emit_error(f"Invalid choice. Must be one of: {', '.join(choices)}")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
return response.strip() if response.strip() else None
|
|
39
|
+
except Exception as e:
|
|
40
|
+
emit_error(f"Input error: {e}")
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def confirm_ask(prompt_text: str, default: bool = True) -> bool:
|
|
45
|
+
"""Helper function to replace rich.prompt.Confirm.ask with emit_prompt."""
|
|
46
|
+
try:
|
|
47
|
+
default_text = "[Y/n]" if default else "[y/N]"
|
|
48
|
+
response = emit_prompt(f"{prompt_text} {default_text}: ")
|
|
49
|
+
|
|
50
|
+
if not response.strip():
|
|
51
|
+
return default
|
|
52
|
+
|
|
53
|
+
response_lower = response.strip().lower()
|
|
54
|
+
if response_lower in ['y', 'yes', 'true', '1']:
|
|
55
|
+
return True
|
|
56
|
+
elif response_lower in ['n', 'no', 'false', '0']:
|
|
57
|
+
return False
|
|
58
|
+
else:
|
|
59
|
+
return default
|
|
60
|
+
except Exception as e:
|
|
61
|
+
emit_error(f"Input error: {e}")
|
|
62
|
+
return default
|
|
63
|
+
|
|
64
|
+
|
|
17
65
|
class MCPConfigWizard:
|
|
18
66
|
"""Interactive wizard for configuring MCP servers."""
|
|
19
67
|
|
|
20
68
|
def __init__(self):
|
|
21
69
|
self.manager = get_mcp_manager()
|
|
22
70
|
|
|
23
|
-
def run_wizard(self) -> Optional[ServerConfig]:
|
|
71
|
+
def run_wizard(self, group_id: str = None) -> Optional[ServerConfig]:
|
|
24
72
|
"""
|
|
25
73
|
Run the interactive configuration wizard.
|
|
26
74
|
|
|
75
|
+
Args:
|
|
76
|
+
group_id: Optional message group ID for grouping related messages
|
|
77
|
+
|
|
27
78
|
Returns:
|
|
28
79
|
ServerConfig if successful, None if cancelled
|
|
29
80
|
"""
|
|
30
|
-
|
|
81
|
+
if group_id is None:
|
|
82
|
+
import uuid
|
|
83
|
+
group_id = str(uuid.uuid4())
|
|
84
|
+
|
|
85
|
+
emit_info("🧙 MCP Server Configuration Wizard", message_group=group_id)
|
|
31
86
|
|
|
32
87
|
# Step 1: Server name
|
|
33
|
-
name = self.prompt_server_name()
|
|
88
|
+
name = self.prompt_server_name(group_id)
|
|
34
89
|
if not name:
|
|
35
90
|
return None
|
|
36
91
|
|
|
37
92
|
# Step 2: Server type
|
|
38
|
-
server_type = self.prompt_server_type()
|
|
93
|
+
server_type = self.prompt_server_type(group_id)
|
|
39
94
|
if not server_type:
|
|
40
95
|
return None
|
|
41
96
|
|
|
42
97
|
# Step 3: Type-specific configuration
|
|
43
98
|
config = {}
|
|
44
99
|
if server_type == "sse":
|
|
45
|
-
config = self.prompt_sse_config()
|
|
100
|
+
config = self.prompt_sse_config(group_id)
|
|
46
101
|
elif server_type == "http":
|
|
47
|
-
config = self.prompt_http_config()
|
|
102
|
+
config = self.prompt_http_config(group_id)
|
|
48
103
|
elif server_type == "stdio":
|
|
49
|
-
config = self.prompt_stdio_config()
|
|
104
|
+
config = self.prompt_stdio_config(group_id)
|
|
50
105
|
|
|
51
106
|
if not config:
|
|
52
107
|
return None
|
|
@@ -61,62 +116,55 @@ class MCPConfigWizard:
|
|
|
61
116
|
)
|
|
62
117
|
|
|
63
118
|
# Step 5: Show summary and confirm
|
|
64
|
-
if self.prompt_confirmation(server_config):
|
|
119
|
+
if self.prompt_confirmation(server_config, group_id):
|
|
65
120
|
return server_config
|
|
66
121
|
|
|
67
122
|
return None
|
|
68
123
|
|
|
69
|
-
def prompt_server_name(self) -> Optional[str]:
|
|
124
|
+
def prompt_server_name(self, group_id: str = None) -> Optional[str]:
|
|
70
125
|
"""Prompt for server name with validation."""
|
|
71
126
|
while True:
|
|
72
|
-
name =
|
|
73
|
-
"[yellow]Enter server name[/yellow]",
|
|
74
|
-
default=None
|
|
75
|
-
)
|
|
127
|
+
name = prompt_ask("Enter server name", default=None)
|
|
76
128
|
|
|
77
129
|
if not name:
|
|
78
|
-
if not
|
|
130
|
+
if not confirm_ask("Cancel configuration?", default=False):
|
|
79
131
|
continue
|
|
80
132
|
return None
|
|
81
133
|
|
|
82
134
|
# Validate name
|
|
83
135
|
if not self.validate_name(name):
|
|
84
|
-
emit_error("Name must be alphanumeric with hyphens/underscores only")
|
|
136
|
+
emit_error("Name must be alphanumeric with hyphens/underscores only", message_group=group_id)
|
|
85
137
|
continue
|
|
86
138
|
|
|
87
139
|
# Check uniqueness
|
|
88
140
|
existing = self.manager.registry.get_by_name(name)
|
|
89
141
|
if existing:
|
|
90
|
-
emit_error(f"Server '{name}' already exists")
|
|
142
|
+
emit_error(f"Server '{name}' already exists", message_group=group_id)
|
|
91
143
|
continue
|
|
92
144
|
|
|
93
145
|
return name
|
|
94
146
|
|
|
95
|
-
def prompt_server_type(self) -> Optional[str]:
|
|
147
|
+
def prompt_server_type(self, group_id: str = None) -> Optional[str]:
|
|
96
148
|
"""Prompt for server type."""
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
149
|
+
emit_info("\nServer types:", message_group=group_id)
|
|
150
|
+
emit_info(" sse - Server-Sent Events (HTTP streaming)", message_group=group_id)
|
|
151
|
+
emit_info(" http - HTTP/REST API", message_group=group_id)
|
|
152
|
+
emit_info(" stdio - Local command (subprocess)", message_group=group_id)
|
|
101
153
|
|
|
102
154
|
while True:
|
|
103
|
-
server_type =
|
|
104
|
-
"\n[yellow]Select server type[/yellow]",
|
|
105
|
-
choices=["sse", "http", "stdio"],
|
|
106
|
-
default="stdio"
|
|
107
|
-
)
|
|
155
|
+
server_type = prompt_ask("Select server type", choices=["sse", "http", "stdio"], default="stdio")
|
|
108
156
|
|
|
109
157
|
if server_type in ["sse", "http", "stdio"]:
|
|
110
158
|
return server_type
|
|
111
159
|
|
|
112
|
-
emit_error("Invalid type. Choose: sse, http, or stdio")
|
|
160
|
+
emit_error("Invalid type. Choose: sse, http, or stdio", message_group=group_id)
|
|
113
161
|
|
|
114
|
-
def prompt_sse_config(self) -> Optional[Dict]:
|
|
162
|
+
def prompt_sse_config(self, group_id: str = None) -> Optional[Dict]:
|
|
115
163
|
"""Prompt for SSE server configuration."""
|
|
116
|
-
|
|
164
|
+
emit_info("Configuring SSE server", message_group=group_id)
|
|
117
165
|
|
|
118
166
|
# URL
|
|
119
|
-
url = self.prompt_url("SSE")
|
|
167
|
+
url = self.prompt_url("SSE", group_id)
|
|
120
168
|
if not url:
|
|
121
169
|
return None
|
|
122
170
|
|
|
@@ -127,16 +175,13 @@ class MCPConfigWizard:
|
|
|
127
175
|
}
|
|
128
176
|
|
|
129
177
|
# Headers (optional)
|
|
130
|
-
if
|
|
131
|
-
headers = self.prompt_headers()
|
|
178
|
+
if confirm_ask("Add custom headers?", default=False):
|
|
179
|
+
headers = self.prompt_headers(group_id)
|
|
132
180
|
if headers:
|
|
133
181
|
config["headers"] = headers
|
|
134
182
|
|
|
135
183
|
# Timeout
|
|
136
|
-
timeout_str =
|
|
137
|
-
"Connection timeout (seconds)",
|
|
138
|
-
default="30"
|
|
139
|
-
)
|
|
184
|
+
timeout_str = prompt_ask("Connection timeout (seconds)", default="30")
|
|
140
185
|
try:
|
|
141
186
|
config["timeout"] = int(timeout_str)
|
|
142
187
|
except ValueError:
|
|
@@ -144,12 +189,12 @@ class MCPConfigWizard:
|
|
|
144
189
|
|
|
145
190
|
return config
|
|
146
191
|
|
|
147
|
-
def prompt_http_config(self) -> Optional[Dict]:
|
|
192
|
+
def prompt_http_config(self, group_id: str = None) -> Optional[Dict]:
|
|
148
193
|
"""Prompt for HTTP server configuration."""
|
|
149
|
-
|
|
194
|
+
emit_info("Configuring HTTP server", message_group=group_id)
|
|
150
195
|
|
|
151
196
|
# URL
|
|
152
|
-
url = self.prompt_url("HTTP")
|
|
197
|
+
url = self.prompt_url("HTTP", group_id)
|
|
153
198
|
if not url:
|
|
154
199
|
return None
|
|
155
200
|
|
|
@@ -160,16 +205,13 @@ class MCPConfigWizard:
|
|
|
160
205
|
}
|
|
161
206
|
|
|
162
207
|
# Headers (optional)
|
|
163
|
-
if
|
|
164
|
-
headers = self.prompt_headers()
|
|
208
|
+
if confirm_ask("Add custom headers?", default=False):
|
|
209
|
+
headers = self.prompt_headers(group_id)
|
|
165
210
|
if headers:
|
|
166
211
|
config["headers"] = headers
|
|
167
212
|
|
|
168
213
|
# Timeout
|
|
169
|
-
timeout_str =
|
|
170
|
-
"Request timeout (seconds)",
|
|
171
|
-
default="30"
|
|
172
|
-
)
|
|
214
|
+
timeout_str = prompt_ask("Request timeout (seconds)", default="30")
|
|
173
215
|
try:
|
|
174
216
|
config["timeout"] = int(timeout_str)
|
|
175
217
|
except ValueError:
|
|
@@ -177,19 +219,16 @@ class MCPConfigWizard:
|
|
|
177
219
|
|
|
178
220
|
return config
|
|
179
221
|
|
|
180
|
-
def prompt_stdio_config(self) -> Optional[Dict]:
|
|
222
|
+
def prompt_stdio_config(self, group_id: str = None) -> Optional[Dict]:
|
|
181
223
|
"""Prompt for Stdio server configuration."""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
224
|
+
emit_info("Configuring Stdio server", message_group=group_id)
|
|
225
|
+
emit_info("Examples:", message_group=group_id)
|
|
226
|
+
emit_info(" • npx -y @modelcontextprotocol/server-filesystem /path", message_group=group_id)
|
|
227
|
+
emit_info(" • python mcp_server.py", message_group=group_id)
|
|
228
|
+
emit_info(" • node server.js", message_group=group_id)
|
|
187
229
|
|
|
188
230
|
# Command
|
|
189
|
-
command =
|
|
190
|
-
"\n[yellow]Enter command[/yellow]",
|
|
191
|
-
default=None
|
|
192
|
-
)
|
|
231
|
+
command = prompt_ask("Enter command", default=None)
|
|
193
232
|
|
|
194
233
|
if not command:
|
|
195
234
|
return None
|
|
@@ -202,10 +241,7 @@ class MCPConfigWizard:
|
|
|
202
241
|
}
|
|
203
242
|
|
|
204
243
|
# Arguments
|
|
205
|
-
args_str =
|
|
206
|
-
"Enter arguments (space-separated)",
|
|
207
|
-
default=""
|
|
208
|
-
)
|
|
244
|
+
args_str = prompt_ask("Enter arguments (space-separated)", default="")
|
|
209
245
|
if args_str:
|
|
210
246
|
# Simple argument parsing (handles quoted strings)
|
|
211
247
|
import shlex
|
|
@@ -215,28 +251,22 @@ class MCPConfigWizard:
|
|
|
215
251
|
config["args"] = args_str.split()
|
|
216
252
|
|
|
217
253
|
# Working directory (optional)
|
|
218
|
-
cwd =
|
|
219
|
-
"Working directory (optional)",
|
|
220
|
-
default=""
|
|
221
|
-
)
|
|
254
|
+
cwd = prompt_ask("Working directory (optional)", default="")
|
|
222
255
|
if cwd:
|
|
223
256
|
import os
|
|
224
257
|
if os.path.isdir(os.path.expanduser(cwd)):
|
|
225
258
|
config["cwd"] = os.path.expanduser(cwd)
|
|
226
259
|
else:
|
|
227
|
-
emit_warning(f"Directory '{cwd}' not found, ignoring")
|
|
260
|
+
emit_warning(f"Directory '{cwd}' not found, ignoring", message_group=group_id)
|
|
228
261
|
|
|
229
262
|
# Environment variables (optional)
|
|
230
|
-
if
|
|
231
|
-
env = self.prompt_env_vars()
|
|
263
|
+
if confirm_ask("Add environment variables?", default=False):
|
|
264
|
+
env = self.prompt_env_vars(group_id)
|
|
232
265
|
if env:
|
|
233
266
|
config["env"] = env
|
|
234
267
|
|
|
235
268
|
# Timeout
|
|
236
|
-
timeout_str =
|
|
237
|
-
"Startup timeout (seconds)",
|
|
238
|
-
default="30"
|
|
239
|
-
)
|
|
269
|
+
timeout_str = prompt_ask("Startup timeout (seconds)", default="30")
|
|
240
270
|
try:
|
|
241
271
|
config["timeout"] = int(timeout_str)
|
|
242
272
|
except ValueError:
|
|
@@ -244,58 +274,55 @@ class MCPConfigWizard:
|
|
|
244
274
|
|
|
245
275
|
return config
|
|
246
276
|
|
|
247
|
-
def prompt_url(self, server_type: str) -> Optional[str]:
|
|
277
|
+
def prompt_url(self, server_type: str, group_id: str = None) -> Optional[str]:
|
|
248
278
|
"""Prompt for and validate URL."""
|
|
249
279
|
while True:
|
|
250
|
-
url =
|
|
251
|
-
f"[yellow]Enter {server_type} server URL[/yellow]",
|
|
252
|
-
default=None
|
|
253
|
-
)
|
|
280
|
+
url = prompt_ask(f"Enter {server_type} server URL", default=None)
|
|
254
281
|
|
|
255
282
|
if not url:
|
|
256
|
-
if
|
|
283
|
+
if confirm_ask("Cancel configuration?", default=False):
|
|
257
284
|
return None
|
|
258
285
|
continue
|
|
259
286
|
|
|
260
287
|
if self.validate_url(url):
|
|
261
288
|
return url
|
|
262
289
|
|
|
263
|
-
emit_error("Invalid URL. Must be http:// or https://")
|
|
290
|
+
emit_error("Invalid URL. Must be http:// or https://", message_group=group_id)
|
|
264
291
|
|
|
265
|
-
def prompt_headers(self) -> Dict[str, str]:
|
|
292
|
+
def prompt_headers(self, group_id: str = None) -> Dict[str, str]:
|
|
266
293
|
"""Prompt for HTTP headers."""
|
|
267
294
|
headers = {}
|
|
268
|
-
|
|
269
|
-
|
|
295
|
+
emit_info("Enter headers (format: Name: Value)", message_group=group_id)
|
|
296
|
+
emit_info("Press Enter with empty name to finish", message_group=group_id)
|
|
270
297
|
|
|
271
298
|
while True:
|
|
272
|
-
name =
|
|
299
|
+
name = prompt_ask("Header name", default="")
|
|
273
300
|
if not name:
|
|
274
301
|
break
|
|
275
302
|
|
|
276
|
-
value =
|
|
303
|
+
value = prompt_ask(f"Value for '{name}'", default="")
|
|
277
304
|
headers[name] = value
|
|
278
305
|
|
|
279
|
-
if not
|
|
306
|
+
if not confirm_ask("Add another header?", default=True):
|
|
280
307
|
break
|
|
281
308
|
|
|
282
309
|
return headers
|
|
283
310
|
|
|
284
|
-
def prompt_env_vars(self) -> Dict[str, str]:
|
|
311
|
+
def prompt_env_vars(self, group_id: str = None) -> Dict[str, str]:
|
|
285
312
|
"""Prompt for environment variables."""
|
|
286
313
|
env = {}
|
|
287
|
-
|
|
288
|
-
|
|
314
|
+
emit_info("Enter environment variables", message_group=group_id)
|
|
315
|
+
emit_info("Press Enter with empty name to finish", message_group=group_id)
|
|
289
316
|
|
|
290
317
|
while True:
|
|
291
|
-
name =
|
|
318
|
+
name = prompt_ask("Variable name", default="")
|
|
292
319
|
if not name:
|
|
293
320
|
break
|
|
294
321
|
|
|
295
|
-
value =
|
|
322
|
+
value = prompt_ask(f"Value for '{name}'", default="")
|
|
296
323
|
env[name] = value
|
|
297
324
|
|
|
298
|
-
if not
|
|
325
|
+
if not confirm_ask("Add another variable?", default=True):
|
|
299
326
|
break
|
|
300
327
|
|
|
301
328
|
return env
|
|
@@ -325,7 +352,7 @@ class MCPConfigWizard:
|
|
|
325
352
|
# Otherwise check if it's in PATH
|
|
326
353
|
return shutil.which(command) is not None
|
|
327
354
|
|
|
328
|
-
def test_connection(self, config: ServerConfig) -> bool:
|
|
355
|
+
def test_connection(self, config: ServerConfig, group_id: str = None) -> bool:
|
|
329
356
|
"""
|
|
330
357
|
Test connection to the configured server.
|
|
331
358
|
|
|
@@ -335,7 +362,7 @@ class MCPConfigWizard:
|
|
|
335
362
|
Returns:
|
|
336
363
|
True if connection successful, False otherwise
|
|
337
364
|
"""
|
|
338
|
-
emit_info("Testing connection...")
|
|
365
|
+
emit_info("Testing connection...", message_group=group_id)
|
|
339
366
|
|
|
340
367
|
try:
|
|
341
368
|
# Try to create the server instance
|
|
@@ -349,60 +376,67 @@ class MCPConfigWizard:
|
|
|
349
376
|
# Try to get the pydantic server (this validates config)
|
|
350
377
|
server = managed.get_pydantic_server()
|
|
351
378
|
if server:
|
|
352
|
-
emit_success("✓ Configuration valid")
|
|
379
|
+
emit_success("✓ Configuration valid", message_group=group_id)
|
|
353
380
|
return True
|
|
354
381
|
|
|
355
|
-
emit_error("✗ Failed to create server instance")
|
|
382
|
+
emit_error("✗ Failed to create server instance", message_group=group_id)
|
|
356
383
|
return False
|
|
357
384
|
|
|
358
385
|
except Exception as e:
|
|
359
|
-
emit_error(f"✗ Configuration error: {e}")
|
|
386
|
+
emit_error(f"✗ Configuration error: {e}", message_group=group_id)
|
|
360
387
|
return False
|
|
361
388
|
|
|
362
|
-
def prompt_confirmation(self, config: ServerConfig) -> bool:
|
|
389
|
+
def prompt_confirmation(self, config: ServerConfig, group_id: str = None) -> bool:
|
|
363
390
|
"""Show summary and ask for confirmation."""
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
391
|
+
emit_info("Configuration Summary:", message_group=group_id)
|
|
392
|
+
emit_info(f" Name: {config.name}", message_group=group_id)
|
|
393
|
+
emit_info(f" Type: {config.type}", message_group=group_id)
|
|
367
394
|
|
|
368
395
|
if config.type in ["sse", "http"]:
|
|
369
|
-
|
|
396
|
+
emit_info(f" URL: {config.config.get('url')}", message_group=group_id)
|
|
370
397
|
elif config.type == "stdio":
|
|
371
|
-
|
|
398
|
+
emit_info(f" Command: {config.config.get('command')}", message_group=group_id)
|
|
372
399
|
args = config.config.get('args', [])
|
|
373
400
|
if args:
|
|
374
|
-
|
|
401
|
+
emit_info(f" Arguments: {' '.join(args)}", message_group=group_id)
|
|
375
402
|
|
|
376
|
-
|
|
403
|
+
emit_info(f" Timeout: {config.config.get('timeout', 30)}s", message_group=group_id)
|
|
377
404
|
|
|
378
405
|
# Test connection if requested
|
|
379
|
-
if
|
|
380
|
-
if not self.test_connection(config):
|
|
381
|
-
if not
|
|
406
|
+
if confirm_ask("Test connection?", default=True):
|
|
407
|
+
if not self.test_connection(config, group_id):
|
|
408
|
+
if not confirm_ask("Continue anyway?", default=False):
|
|
382
409
|
return False
|
|
383
410
|
|
|
384
|
-
return
|
|
411
|
+
return confirm_ask("Save this configuration?", default=True)
|
|
385
412
|
|
|
386
413
|
|
|
387
|
-
def run_add_wizard() -> bool:
|
|
414
|
+
def run_add_wizard(group_id: str = None) -> bool:
|
|
388
415
|
"""
|
|
389
416
|
Run the MCP add wizard and register the server.
|
|
390
417
|
|
|
418
|
+
Args:
|
|
419
|
+
group_id: Optional message group ID for grouping related messages
|
|
420
|
+
|
|
391
421
|
Returns:
|
|
392
422
|
True if server was added, False otherwise
|
|
393
423
|
"""
|
|
424
|
+
if group_id is None:
|
|
425
|
+
import uuid
|
|
426
|
+
group_id = str(uuid.uuid4())
|
|
427
|
+
|
|
394
428
|
wizard = MCPConfigWizard()
|
|
395
|
-
config = wizard.run_wizard()
|
|
429
|
+
config = wizard.run_wizard(group_id)
|
|
396
430
|
|
|
397
431
|
if config:
|
|
398
432
|
try:
|
|
399
433
|
manager = get_mcp_manager()
|
|
400
434
|
server_id = manager.register_server(config)
|
|
401
435
|
|
|
402
|
-
emit_success(f"\n✅ Server '{config.name}' added successfully!")
|
|
403
|
-
emit_info(f"Server ID: {server_id}")
|
|
404
|
-
emit_info("Use '/mcp list' to see all servers")
|
|
405
|
-
emit_info(f"Use '/mcp start {config.name}' to start the server")
|
|
436
|
+
emit_success(f"\n✅ Server '{config.name}' added successfully!", message_group=group_id)
|
|
437
|
+
emit_info(f"Server ID: {server_id}", message_group=group_id)
|
|
438
|
+
emit_info("Use '/mcp list' to see all servers", message_group=group_id)
|
|
439
|
+
emit_info(f"Use '/mcp start {config.name}' to start the server", message_group=group_id)
|
|
406
440
|
|
|
407
441
|
# Also save to mcp_servers.json for persistence
|
|
408
442
|
from code_puppy.config import MCP_SERVERS_FILE, load_mcp_server_configs
|
|
@@ -426,12 +460,12 @@ def run_add_wizard() -> bool:
|
|
|
426
460
|
with open(MCP_SERVERS_FILE, 'w') as f:
|
|
427
461
|
json.dump(data, f, indent=2)
|
|
428
462
|
|
|
429
|
-
emit_info(f"[dim]Configuration saved to {MCP_SERVERS_FILE}[/dim]")
|
|
463
|
+
emit_info(f"[dim]Configuration saved to {MCP_SERVERS_FILE}[/dim]", message_group=group_id)
|
|
430
464
|
return True
|
|
431
465
|
|
|
432
466
|
except Exception as e:
|
|
433
|
-
emit_error(f"Failed to add server: {e}")
|
|
467
|
+
emit_error(f"Failed to add server: {e}", message_group=group_id)
|
|
434
468
|
return False
|
|
435
469
|
else:
|
|
436
|
-
emit_warning("Configuration cancelled")
|
|
470
|
+
emit_warning("Configuration cancelled", message_group=group_id)
|
|
437
471
|
return False
|