Skip to content

Commit 4e7f095

Browse files
authored
Merge pull request #230 from IBM/code-stuff
Code stuff
2 parents b38b0a4 + 18e752e commit 4e7f095

24 files changed

Lines changed: 394 additions & 153 deletions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "mcp-cli"
7-
version = "0.18"
7+
version = "0.19"
88
description = "A cli for the Model Context Provider"
99
requires-python = ">=3.11"
1010
readme = "README.md"

src/mcp_cli/dashboard/bridge.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,25 @@ async def on_app_launched(self, app_info: Any) -> None:
236236
),
237237
"timestamp": _now(),
238238
}
239-
self._running_apps[app_info.tool_name] = payload
239+
# Key by resource_uri when available so different tools sharing
240+
# the same resource replace each other rather than accumulating.
241+
app_key = app_info.resource_uri or app_info.tool_name
242+
self._running_apps[app_key] = payload
240243
await self._broadcast(_envelope("APP_LAUNCHED", payload))
241244

242245
async def on_app_closed(self, tool_name: str) -> None:
243246
"""Notify dashboard that an MCP App closed."""
244-
self._running_apps.pop(tool_name, None)
247+
# Try removing by tool_name first, then scan by resource_uri
248+
if tool_name not in self._running_apps:
249+
to_remove = [
250+
k
251+
for k, v in self._running_apps.items()
252+
if v.get("tool_name") == tool_name
253+
]
254+
for k in to_remove:
255+
self._running_apps.pop(k, None)
256+
else:
257+
self._running_apps.pop(tool_name, None)
245258
await self._broadcast(
246259
_envelope(
247260
"APP_CLOSED",
@@ -280,7 +293,10 @@ async def _on_client_connected(self, ws: Any) -> None:
280293
if self._view_registry:
281294
await ws.send(
282295
_json.dumps(
283-
_envelope("VIEW_REGISTRY", {"views": self._view_registry})
296+
_envelope(
297+
"VIEW_REGISTRY",
298+
{"agent_id": self.agent_id, "views": self._view_registry},
299+
)
284300
)
285301
)
286302
# CONFIG_STATE (model, provider, servers, system prompt preview)
@@ -614,8 +630,8 @@ async def _handle_switch_session(self, msg: dict[str, Any]) -> None:
614630
if ctx.conversation_history:
615631
try:
616632
ctx.save_session()
617-
except Exception:
618-
pass
633+
except Exception as exc:
634+
logger.warning("Failed to save current session before switch: %s", exc)
619635

620636
# Clear and load the target session
621637
try:
@@ -757,9 +773,19 @@ async def request_tool_approval(
757773
"timestamp": _now(),
758774
}
759775
# Create a future that the tool processor can await
760-
fut: asyncio.Future[bool] = asyncio.get_running_loop().create_future()
776+
loop = asyncio.get_running_loop()
777+
fut: asyncio.Future[bool] = loop.create_future()
761778
self._pending_approvals[call_id] = fut
762779
await self._broadcast(_envelope("TOOL_APPROVAL_REQUEST", payload))
780+
781+
# Auto-deny after 5 minutes if no response (prevents hanging forever)
782+
def _timeout() -> None:
783+
if not fut.done():
784+
logger.warning("Tool approval for %s timed out after 5m", tool_name)
785+
fut.set_result(False)
786+
self._pending_approvals.pop(call_id, None)
787+
788+
loop.call_later(300, _timeout)
763789
return fut
764790

765791
async def _handle_tool_approval_response(self, msg: dict[str, Any]) -> None:
@@ -1064,9 +1090,11 @@ def _build_activity_history(self) -> list[dict[str, Any]] | None:
10641090
"result": result_content,
10651091
"error": None,
10661092
"success": True,
1067-
"arguments": self._serialise(arguments)
1068-
if arguments
1069-
else None,
1093+
"arguments": (
1094+
self._serialise(arguments)
1095+
if arguments
1096+
else None
1097+
),
10701098
},
10711099
}
10721100
)

src/mcp_cli/dashboard/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ async def broadcast(self, msg: dict[str, Any]) -> None:
9797
for client in list(self._clients):
9898
try:
9999
await client.send(payload)
100-
except Exception:
100+
except Exception as exc:
101+
logger.debug("Failed to send to client, removing: %s", exc)
101102
dead.append(client)
102103
for c in dead:
103104
self._clients.discard(c)

src/mcp_cli/dashboard/static/css/drawers.css

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@
2626
}
2727
#settings-panel textarea:focus { border-color: var(--dash-accent); outline: none; }
2828
#settings-panel .btn-row { display: flex; gap: 6px; margin-top: 6px; }
29-
#settings-panel .btn-sm {
29+
.btn-sm {
3030
background: transparent; border: 1px solid var(--dash-border); color: var(--dash-fg);
3131
padding: 3px 10px; border-radius: var(--dash-radius); cursor: pointer; font-size: 11px;
3232
font-family: var(--dash-font-ui);
3333
}
34-
#settings-panel .btn-sm:hover { background: var(--dash-bg-hover); }
35-
#settings-panel .btn-sm.primary { background: var(--dash-accent); color: var(--dash-bg); border-color: var(--dash-accent); }
36-
#settings-panel .btn-sm.primary:hover { opacity: 0.9; }
34+
.btn-sm:hover { background: var(--dash-bg-hover); }
35+
.btn-sm.primary { background: var(--dash-accent); color: var(--dash-bg); border-color: var(--dash-accent); }
36+
.btn-sm.primary:hover { opacity: 0.9; }
3737
.server-list { list-style: none; padding: 0; margin: 4px 0 0 0; }
3838
.server-item {
3939
display: flex; align-items: center; gap: 6px; padding: 4px 0;
@@ -88,3 +88,9 @@
8888
.session-actions button:hover { color: var(--dash-fg); background: var(--dash-bg-hover); }
8989
.session-actions button.delete:hover { color: var(--dash-error); }
9090
.session-empty { color: var(--dash-fg-muted); font-size: 12px; text-align: center; padding: 16px 0; }
91+
92+
/* ── Focus styles ─────────────────────────────────────────────────── */
93+
.btn-sm:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }
94+
.session-item:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }
95+
#settings-panel select:focus-visible,
96+
#settings-panel textarea:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }

src/mcp_cli/dashboard/static/css/grid.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,7 @@
135135
z-index: 10;
136136
}
137137
.resize-handle-row:hover, .resize-handle-row.dragging { background: var(--dash-accent); }
138+
139+
/* ── Focus styles ─────────────────────────────────────────────────── */
140+
.panel-btn:focus-visible,
141+
.panel-view-toggle:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }

src/mcp_cli/dashboard/static/css/notifications.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,7 @@
9494
}
9595
#approval-dialog .btn-approve:hover { opacity: 0.9; }
9696
#approval-dialog .btn-deny:hover { background: var(--dash-bg-hover); }
97+
98+
/* ── Focus styles ─────────────────────────────────────────────────── */
99+
.btn-approve:focus-visible,
100+
.btn-deny:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }

src/mcp_cli/dashboard/static/css/responsive.css

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@
8181
font-size: 14px;
8282
}
8383
.sidebar-section-btn {
84-
min-width: 28px;
85-
min-height: 28px;
84+
min-width: 36px;
85+
min-height: 36px;
8686
font-size: 13px;
8787
padding: 4px 6px;
8888
}
@@ -127,3 +127,12 @@
127127
font-size: 18px;
128128
}
129129
}
130+
131+
/* ── Reduced motion ───────────────────────────────────────────────── */
132+
@media (prefers-reduced-motion: reduce) {
133+
*, *::before, *::after {
134+
animation-duration: 0.01ms !important;
135+
animation-iteration-count: 1 !important;
136+
transition-duration: 0.01ms !important;
137+
}
138+
}

src/mcp_cli/dashboard/static/css/sidebar.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ body.mobile-sidebar #sidebar-resize-handle { display: none; }
186186
}
187187
.sidebar-section-btn:hover { background: var(--dash-bg-hover); color: var(--dash-fg); }
188188
.sidebar-section.maximized { flex: 100 !important; }
189-
.sidebar-section:not(.maximized):not(.expanded) { }
190189
/* When any section is maximized, collapse siblings */
191190
#sidebar-view-slot:has(.sidebar-section.maximized) .sidebar-section:not(.maximized) {
192191
flex: 0 0 auto !important;
@@ -237,6 +236,12 @@ body.mobile-sidebar #sidebar-panel {
237236
body.mobile-sidebar #sidebar-panel.open { display: flex; }
238237
body.mobile-sidebar #sidebar-header { display: flex; }
239238

239+
/* ── Focus styles ─────────────────────────────────────────────────── */
240+
#sidebar-toggle:focus-visible,
241+
#sidebar-close:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }
242+
.sidebar-section-header:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }
243+
.sidebar-section-btn:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }
244+
240245
/* ── Container-aware layout (ResizeObserver classes for IDE embedding) */
241246
body.container-narrow .grid-row { flex-direction: column; }
242247
body.container-narrow .resize-handle-col { display: none; }

src/mcp_cli/dashboard/static/css/toolbar.css

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
align-items: center;
55
gap: 12px;
66
padding: 0 12px;
7-
height: 36px;
8-
min-height: 36px;
7+
height: var(--dash-toolbar-height);
8+
min-height: var(--dash-toolbar-height);
99
background: var(--dash-bg-surface);
1010
border-bottom: 1px solid var(--dash-border);
1111
flex-shrink: 0;
@@ -78,7 +78,6 @@
7878
font-family: var(--dash-font-ui);
7979
cursor: pointer;
8080
max-width: 140px;
81-
outline: none;
8281
}
8382
.tb-select:hover { border-color: var(--dash-accent); }
8483
.tb-label {
@@ -92,3 +91,8 @@
9291
align-items: center;
9392
gap: 4px;
9493
}
94+
95+
/* ── Focus styles ─────────────────────────────────────────────────── */
96+
.tb-btn:focus-visible,
97+
.tb-select:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; }
98+
.dropdown-item:focus-visible { outline: 2px solid var(--dash-accent); outline-offset: -2px; background: var(--dash-bg-hover); }

src/mcp_cli/dashboard/static/css/variables.css

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ html, body { height: 100%; overflow: hidden; }
1919
--dash-font-ui: 'Inter',-apple-system,sans-serif;
2020
--dash-font-size: 13px;
2121
--dash-radius: 6px;
22-
--dash-spacing: 8px;
2322
--dash-toolbar-height: 36px;
2423
--dash-touch-target: 44px;
25-
--dash-panel-min-width: 200px;
2624
--dash-panel-min-height: 150px;
2725
}
2826

0 commit comments

Comments
 (0)