code-puppy 0.0.135__py3-none-any.whl → 0.0.137__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/agent.py +15 -17
- code_puppy/agents/agent_manager.py +320 -9
- code_puppy/agents/base_agent.py +58 -2
- code_puppy/agents/runtime_manager.py +68 -42
- code_puppy/command_line/command_handler.py +82 -33
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/add_command.py +183 -0
- code_puppy/command_line/mcp/base.py +35 -0
- code_puppy/command_line/mcp/handler.py +133 -0
- code_puppy/command_line/mcp/help_command.py +146 -0
- code_puppy/command_line/mcp/install_command.py +176 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +126 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +92 -0
- code_puppy/command_line/mcp/search_command.py +117 -0
- code_puppy/command_line/mcp/start_all_command.py +126 -0
- code_puppy/command_line/mcp/start_command.py +98 -0
- code_puppy/command_line/mcp/status_command.py +185 -0
- code_puppy/command_line/mcp/stop_all_command.py +109 -0
- code_puppy/command_line/mcp/stop_command.py +79 -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 +259 -0
- code_puppy/command_line/model_picker_completion.py +21 -4
- code_puppy/command_line/prompt_toolkit_completion.py +9 -0
- code_puppy/config.py +5 -5
- code_puppy/main.py +23 -17
- code_puppy/mcp/__init__.py +42 -16
- code_puppy/mcp/async_lifecycle.py +51 -49
- code_puppy/mcp/blocking_startup.py +125 -113
- code_puppy/mcp/captured_stdio_server.py +63 -70
- code_puppy/mcp/circuit_breaker.py +63 -47
- code_puppy/mcp/config_wizard.py +169 -136
- code_puppy/mcp/dashboard.py +79 -71
- code_puppy/mcp/error_isolation.py +147 -100
- code_puppy/mcp/examples/retry_example.py +55 -42
- code_puppy/mcp/health_monitor.py +152 -141
- code_puppy/mcp/managed_server.py +100 -93
- code_puppy/mcp/manager.py +168 -156
- code_puppy/mcp/registry.py +148 -110
- code_puppy/mcp/retry_manager.py +63 -61
- code_puppy/mcp/server_registry_catalog.py +271 -225
- code_puppy/mcp/status_tracker.py +80 -80
- code_puppy/mcp/system_tools.py +47 -52
- code_puppy/messaging/message_queue.py +20 -13
- code_puppy/messaging/renderers.py +30 -15
- code_puppy/state_management.py +103 -0
- code_puppy/tui/app.py +64 -7
- code_puppy/tui/components/chat_view.py +3 -3
- code_puppy/tui/components/human_input_modal.py +12 -8
- code_puppy/tui/screens/__init__.py +2 -2
- code_puppy/tui/screens/mcp_install_wizard.py +208 -179
- code_puppy/tui/tests/test_agent_command.py +3 -3
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/METADATA +1 -1
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/RECORD +60 -42
- code_puppy/command_line/mcp_commands.py +0 -1789
- {code_puppy-0.0.135.data → code_puppy-0.0.137.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/licenses/LICENSE +0 -0
code_puppy/mcp/dashboard.py
CHANGED
|
@@ -4,27 +4,28 @@ MCP Dashboard Implementation
|
|
|
4
4
|
Provides visual status dashboard for MCP servers using Rich tables.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from datetime import datetime
|
|
8
|
-
from typing import Dict, List,
|
|
9
|
-
|
|
10
|
-
from rich.console import Console
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Dict, List, Optional
|
|
9
|
+
|
|
11
10
|
from rich import box
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
12
13
|
|
|
13
|
-
from .status_tracker import ServerState, Event
|
|
14
14
|
from .manager import get_mcp_manager
|
|
15
|
+
from .status_tracker import ServerState
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class MCPDashboard:
|
|
18
19
|
"""Visual dashboard for MCP server status monitoring"""
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
def __init__(self):
|
|
21
22
|
"""Initialize the MCP Dashboard"""
|
|
22
23
|
self.console = Console()
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
def render_dashboard(self) -> Table:
|
|
25
26
|
"""
|
|
26
27
|
Render the main MCP server status dashboard
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
Returns:
|
|
29
30
|
Table: Rich table with server status information
|
|
30
31
|
"""
|
|
@@ -34,9 +35,9 @@ class MCPDashboard:
|
|
|
34
35
|
box=box.ROUNDED,
|
|
35
36
|
show_header=True,
|
|
36
37
|
header_style="bold blue",
|
|
37
|
-
title_style="bold cyan"
|
|
38
|
+
title_style="bold cyan",
|
|
38
39
|
)
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
# Define columns
|
|
41
42
|
table.add_column("Name", style="white", no_wrap=True, min_width=10)
|
|
42
43
|
table.add_column("Type", style="white", no_wrap=True, width=8)
|
|
@@ -44,100 +45,107 @@ class MCPDashboard:
|
|
|
44
45
|
table.add_column("Health", style="white", no_wrap=True, width=8)
|
|
45
46
|
table.add_column("Uptime", style="white", no_wrap=True, width=10)
|
|
46
47
|
table.add_column("Latency", style="white", no_wrap=True, width=10)
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
# Get manager and server info
|
|
49
50
|
try:
|
|
50
51
|
manager = get_mcp_manager()
|
|
51
52
|
servers = manager.list_servers()
|
|
52
|
-
|
|
53
|
+
|
|
53
54
|
if not servers:
|
|
54
55
|
# Empty state
|
|
55
56
|
table.add_row(
|
|
56
|
-
"[dim]No servers configured[/dim]",
|
|
57
|
-
"-", "-", "-", "-", "-"
|
|
57
|
+
"[dim]No servers configured[/dim]", "-", "-", "-", "-", "-"
|
|
58
58
|
)
|
|
59
59
|
else:
|
|
60
60
|
# Add row for each server
|
|
61
61
|
for server in servers:
|
|
62
62
|
row_data = self.render_server_row(server)
|
|
63
63
|
table.add_row(*row_data)
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
except Exception as e:
|
|
66
66
|
# Error state
|
|
67
67
|
table.add_row(
|
|
68
68
|
"[red]Error loading servers[/red]",
|
|
69
|
-
"-",
|
|
69
|
+
"-",
|
|
70
|
+
"-",
|
|
71
|
+
"-",
|
|
72
|
+
"-",
|
|
73
|
+
f"[red]{str(e)}[/red]",
|
|
70
74
|
)
|
|
71
|
-
|
|
75
|
+
|
|
72
76
|
return table
|
|
73
|
-
|
|
77
|
+
|
|
74
78
|
def render_server_row(self, server) -> List[str]:
|
|
75
79
|
"""
|
|
76
80
|
Render a single server row for the dashboard
|
|
77
|
-
|
|
81
|
+
|
|
78
82
|
Args:
|
|
79
83
|
server: ServerInfo object with server details
|
|
80
|
-
|
|
84
|
+
|
|
81
85
|
Returns:
|
|
82
86
|
List[str]: Formatted row data for the table
|
|
83
87
|
"""
|
|
84
88
|
# Server name
|
|
85
89
|
name = server.name or server.id[:8]
|
|
86
|
-
|
|
90
|
+
|
|
87
91
|
# Server type
|
|
88
92
|
server_type = server.type.upper() if server.type else "UNK"
|
|
89
|
-
|
|
93
|
+
|
|
90
94
|
# State indicator
|
|
91
95
|
state_indicator = self.render_state_indicator(server.state)
|
|
92
|
-
|
|
96
|
+
|
|
93
97
|
# Health indicator
|
|
94
98
|
health_indicator = self.render_health_indicator(server.health)
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
# Uptime
|
|
97
101
|
uptime_str = self.format_uptime(server.start_time) if server.start_time else "-"
|
|
98
|
-
|
|
102
|
+
|
|
99
103
|
# Latency
|
|
100
|
-
latency_str =
|
|
101
|
-
|
|
104
|
+
latency_str = (
|
|
105
|
+
self.format_latency(server.latency_ms)
|
|
106
|
+
if server.latency_ms is not None
|
|
107
|
+
else "-"
|
|
108
|
+
)
|
|
109
|
+
|
|
102
110
|
return [
|
|
103
111
|
name,
|
|
104
112
|
server_type,
|
|
105
113
|
state_indicator,
|
|
106
114
|
health_indicator,
|
|
107
115
|
uptime_str,
|
|
108
|
-
latency_str
|
|
116
|
+
latency_str,
|
|
109
117
|
]
|
|
110
|
-
|
|
118
|
+
|
|
111
119
|
def render_health_indicator(self, health: Optional[Dict]) -> str:
|
|
112
120
|
"""
|
|
113
121
|
Render health status indicator
|
|
114
|
-
|
|
122
|
+
|
|
115
123
|
Args:
|
|
116
124
|
health: Health status dictionary or None
|
|
117
|
-
|
|
125
|
+
|
|
118
126
|
Returns:
|
|
119
127
|
str: Formatted health indicator with color
|
|
120
128
|
"""
|
|
121
129
|
if not health:
|
|
122
130
|
return "[dim]?[/dim]"
|
|
123
|
-
|
|
124
|
-
is_healthy = health.get(
|
|
125
|
-
error = health.get(
|
|
126
|
-
|
|
131
|
+
|
|
132
|
+
is_healthy = health.get("is_healthy", False)
|
|
133
|
+
error = health.get("error")
|
|
134
|
+
|
|
127
135
|
if is_healthy:
|
|
128
136
|
return "[green]✓[/green]"
|
|
129
137
|
elif error:
|
|
130
138
|
return "[red]✗[/red]"
|
|
131
139
|
else:
|
|
132
140
|
return "[yellow]?[/yellow]"
|
|
133
|
-
|
|
141
|
+
|
|
134
142
|
def render_state_indicator(self, state: ServerState) -> str:
|
|
135
143
|
"""
|
|
136
144
|
Render server state indicator
|
|
137
|
-
|
|
145
|
+
|
|
138
146
|
Args:
|
|
139
147
|
state: Current server state
|
|
140
|
-
|
|
148
|
+
|
|
141
149
|
Returns:
|
|
142
150
|
str: Formatted state indicator with color and symbol
|
|
143
151
|
"""
|
|
@@ -149,68 +157,68 @@ class MCPDashboard:
|
|
|
149
157
|
ServerState.STOPPING: "[yellow]⏳ Stop[/yellow]",
|
|
150
158
|
ServerState.QUARANTINED: "[yellow]⏸ Quar[/yellow]",
|
|
151
159
|
}
|
|
152
|
-
|
|
160
|
+
|
|
153
161
|
return indicators.get(state, "[dim]? Unk[/dim]")
|
|
154
|
-
|
|
162
|
+
|
|
155
163
|
def render_metrics_summary(self, metrics: Dict) -> str:
|
|
156
164
|
"""
|
|
157
165
|
Render a summary of server metrics
|
|
158
|
-
|
|
166
|
+
|
|
159
167
|
Args:
|
|
160
168
|
metrics: Dictionary of server metrics
|
|
161
|
-
|
|
169
|
+
|
|
162
170
|
Returns:
|
|
163
171
|
str: Formatted metrics summary
|
|
164
172
|
"""
|
|
165
173
|
if not metrics:
|
|
166
174
|
return "No metrics"
|
|
167
|
-
|
|
175
|
+
|
|
168
176
|
parts = []
|
|
169
|
-
|
|
177
|
+
|
|
170
178
|
# Request count
|
|
171
|
-
if
|
|
179
|
+
if "request_count" in metrics:
|
|
172
180
|
parts.append(f"Req: {metrics['request_count']}")
|
|
173
|
-
|
|
181
|
+
|
|
174
182
|
# Error rate
|
|
175
|
-
if
|
|
176
|
-
error_rate = metrics[
|
|
183
|
+
if "error_rate" in metrics:
|
|
184
|
+
error_rate = metrics["error_rate"]
|
|
177
185
|
if error_rate > 0.1: # 10%
|
|
178
186
|
parts.append(f"[red]Err: {error_rate:.1%}[/red]")
|
|
179
187
|
elif error_rate > 0.05: # 5%
|
|
180
188
|
parts.append(f"[yellow]Err: {error_rate:.1%}[/yellow]")
|
|
181
189
|
else:
|
|
182
190
|
parts.append(f"[green]Err: {error_rate:.1%}[/green]")
|
|
183
|
-
|
|
191
|
+
|
|
184
192
|
# Response time
|
|
185
|
-
if
|
|
186
|
-
avg_time = metrics[
|
|
193
|
+
if "avg_response_time" in metrics:
|
|
194
|
+
avg_time = metrics["avg_response_time"]
|
|
187
195
|
parts.append(f"Avg: {avg_time:.0f}ms")
|
|
188
|
-
|
|
196
|
+
|
|
189
197
|
return " | ".join(parts) if parts else "No data"
|
|
190
|
-
|
|
198
|
+
|
|
191
199
|
def format_uptime(self, start_time: datetime) -> str:
|
|
192
200
|
"""
|
|
193
201
|
Format uptime duration in human readable format
|
|
194
|
-
|
|
202
|
+
|
|
195
203
|
Args:
|
|
196
204
|
start_time: Server start timestamp
|
|
197
|
-
|
|
205
|
+
|
|
198
206
|
Returns:
|
|
199
207
|
str: Formatted uptime string (e.g., "2h 15m")
|
|
200
208
|
"""
|
|
201
209
|
if not start_time:
|
|
202
210
|
return "-"
|
|
203
|
-
|
|
211
|
+
|
|
204
212
|
try:
|
|
205
213
|
uptime = datetime.now() - start_time
|
|
206
|
-
|
|
214
|
+
|
|
207
215
|
# Handle negative uptime (clock skew, etc.)
|
|
208
216
|
if uptime.total_seconds() < 0:
|
|
209
217
|
return "0s"
|
|
210
|
-
|
|
218
|
+
|
|
211
219
|
# Format based on duration
|
|
212
220
|
total_seconds = int(uptime.total_seconds())
|
|
213
|
-
|
|
221
|
+
|
|
214
222
|
if total_seconds < 60: # Less than 1 minute
|
|
215
223
|
return f"{total_seconds}s"
|
|
216
224
|
elif total_seconds < 3600: # Less than 1 hour
|
|
@@ -234,23 +242,23 @@ class MCPDashboard:
|
|
|
234
242
|
return f"{days}d {hours}h"
|
|
235
243
|
else:
|
|
236
244
|
return f"{days}d"
|
|
237
|
-
|
|
245
|
+
|
|
238
246
|
except Exception:
|
|
239
247
|
return "?"
|
|
240
|
-
|
|
248
|
+
|
|
241
249
|
def format_latency(self, latency_ms: float) -> str:
|
|
242
250
|
"""
|
|
243
251
|
Format latency in human readable format
|
|
244
|
-
|
|
252
|
+
|
|
245
253
|
Args:
|
|
246
254
|
latency_ms: Latency in milliseconds
|
|
247
|
-
|
|
255
|
+
|
|
248
256
|
Returns:
|
|
249
257
|
str: Formatted latency string with color coding
|
|
250
258
|
"""
|
|
251
259
|
if latency_ms is None:
|
|
252
260
|
return "-"
|
|
253
|
-
|
|
261
|
+
|
|
254
262
|
try:
|
|
255
263
|
if latency_ms < 0:
|
|
256
264
|
return "invalid"
|
|
@@ -265,27 +273,27 @@ class MCPDashboard:
|
|
|
265
273
|
else: # Very slow
|
|
266
274
|
seconds = latency_ms / 1000
|
|
267
275
|
return f"[red]{seconds:.1f}s[/red]"
|
|
268
|
-
|
|
276
|
+
|
|
269
277
|
except (ValueError, TypeError):
|
|
270
278
|
return "error"
|
|
271
|
-
|
|
279
|
+
|
|
272
280
|
def print_dashboard(self) -> None:
|
|
273
281
|
"""Print the dashboard to console"""
|
|
274
282
|
table = self.render_dashboard()
|
|
275
283
|
self.console.print(table)
|
|
276
284
|
self.console.print() # Add spacing
|
|
277
|
-
|
|
285
|
+
|
|
278
286
|
def get_dashboard_string(self) -> str:
|
|
279
287
|
"""
|
|
280
288
|
Get dashboard as a string for programmatic use
|
|
281
|
-
|
|
289
|
+
|
|
282
290
|
Returns:
|
|
283
291
|
str: Dashboard rendered as plain text
|
|
284
292
|
"""
|
|
285
293
|
# Create a console that captures output
|
|
286
294
|
console = Console(file=None, width=80)
|
|
287
|
-
|
|
295
|
+
|
|
288
296
|
with console.capture() as capture:
|
|
289
297
|
console.print(self.render_dashboard())
|
|
290
|
-
|
|
291
|
-
return capture.get()
|
|
298
|
+
|
|
299
|
+
return capture.get()
|