MCP v2 PR-1b: action dispatcher, confirm modal, set_label end-to-end
App.tsx now listens on "mcp://request" and resolves each call:
needsConfirm=true queues a confirm modal (Accept/Reject, or
"Always allow <tool>" which appends the bare tool name to the
policy's allow bucket on the fly); needsConfirm=false runs straight
through. Replies via mcp_action_reply with externally-tagged
Result. The only wired-up tool for now is set_label, which delegates
to the existing ops.setLabel path.
McpConfirm.tsx (new) — themed amber-bordered modal sibling to the
existing overlays. Enter = accept, Esc = reject. Shows tool, the
policy reason that triggered the prompt, a human-readable summary
("Rename pane X → Y"), and an expandable raw-args section.
Audit log: subscription lifted from AuditTab up to App.tsx so events
fired while the panel is closed (or on Config/Policy tab) still land
in the ring. AuditTab becomes presentational; McpPanel forwards
entries + clearAudit + computes the unread badge from a baseline
seen-count.
StrictMode race fix: both new App-level listeners (mcp://audit and
mcp://request) use the cancelled-flag pattern so a late-resolving
listen() Promise after a strict-mode pretend-unmount tears itself
down instead of leaking a second subscription. Previously this
manifested as duplicate audit rows and a need-to-click-twice on
modal buttons.
This commit is contained in:
parent
464c576b79
commit
26ffe8859a
6 changed files with 397 additions and 49 deletions
|
|
@ -596,3 +596,117 @@
|
|||
margin: 8px 0 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ---- Confirm modal ------------------------------------------------------ */
|
||||
|
||||
.mcp-confirm {
|
||||
position: fixed;
|
||||
top: 20vh;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: min(520px, 92vw);
|
||||
max-height: 60vh;
|
||||
background: #161616;
|
||||
color: #ccc;
|
||||
border: 1px solid #c09040;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.7);
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
font-family: "Cascadia Mono", "JetBrains Mono", "Consolas", monospace;
|
||||
}
|
||||
|
||||
.mcp-confirm-header {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
background: linear-gradient(180deg, #2a2010, #161616);
|
||||
}
|
||||
.mcp-confirm-title { font-size: 13px; font-weight: 600; }
|
||||
.mcp-confirm-title code {
|
||||
color: #c09040;
|
||||
background: transparent;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mcp-confirm-body {
|
||||
padding: 14px 16px;
|
||||
overflow-y: auto;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #2a2a2a transparent;
|
||||
}
|
||||
.mcp-confirm-body::-webkit-scrollbar { width: 8px; }
|
||||
.mcp-confirm-body::-webkit-scrollbar-thumb {
|
||||
background: #2a2a2a;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #1a1a1a;
|
||||
}
|
||||
|
||||
.mcp-confirm-summary { margin: 0 0 8px; color: #ddd; }
|
||||
.mcp-confirm-reason { margin: 0 0 8px; color: #888; font-size: 11px; }
|
||||
.mcp-confirm-reason em { color: #c09040; font-style: normal; }
|
||||
|
||||
.mcp-confirm-args {
|
||||
margin-top: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.mcp-confirm-args summary {
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 2px 0;
|
||||
}
|
||||
.mcp-confirm-args summary:hover { color: #aaa; }
|
||||
.mcp-confirm-args pre {
|
||||
background: #0c0c0c;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 3px;
|
||||
padding: 8px;
|
||||
margin: 6px 0 0;
|
||||
color: #aaa;
|
||||
font-size: 11px;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.mcp-confirm-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
border-top: 1px solid #2a2a2a;
|
||||
background: #111;
|
||||
}
|
||||
.mcp-confirm-reject,
|
||||
.mcp-confirm-accept {
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
padding: 5px 14px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #2a2a3a;
|
||||
}
|
||||
.mcp-confirm-reject { background: #1a1a1a; color: #aaa; }
|
||||
.mcp-confirm-reject:hover { background: #2a1a1a; color: #e08080; border-color: #4a2020; }
|
||||
.mcp-confirm-accept { background: #1a2a1a; color: #80c080; border-color: #2a4a2a; }
|
||||
.mcp-confirm-accept:hover { background: #2a4a2a; color: #a0e0a0; }
|
||||
|
||||
.mcp-confirm-always {
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
padding: 5px 14px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
background: #1a1a2a;
|
||||
color: #aac;
|
||||
border: 1px solid #2a2a4a;
|
||||
margin-right: auto;
|
||||
}
|
||||
.mcp-confirm-always:hover {
|
||||
background: #2a2a4a;
|
||||
color: #ccd;
|
||||
border-color: #4488cc;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue