Skip to content

Commit 756f5e5

Browse files
authored
feat: enable require approval switch for tools in the UI (#1675)
Signed-off-by: Peter Jausovec <peter.jausovec@solo.io>
1 parent a14738a commit 756f5e5

6 files changed

Lines changed: 441 additions & 69 deletions

File tree

ui/src/app/actions/agents.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ function fromAgentFormDataToAgent(agentFormData: AgentFormData): Agent {
6666
namespace = agentNamespace;
6767
}
6868

69+
const requireApproval =
70+
mcpServer.requireApproval && mcpServer.requireApproval.length > 0
71+
? mcpServer.requireApproval
72+
: undefined;
73+
6974
return {
7075
type: "McpServer",
7176
mcpServer: {
@@ -74,6 +79,7 @@ function fromAgentFormDataToAgent(agentFormData: AgentFormData): Agent {
7479
kind: mcpServer.kind,
7580
apiGroup: mcpServer.apiGroup,
7681
toolNames: mcpServer.toolNames,
82+
...(requireApproval ? { requireApproval } : {}),
7783
},
7884
} as Tool;
7985
}
@@ -242,6 +248,11 @@ function fromAgentFormDataToSandboxAgent(agentFormData: AgentFormData): SandboxA
242248
namespace = agentNamespace;
243249
}
244250

251+
const requireApproval =
252+
mcpServer.requireApproval && mcpServer.requireApproval.length > 0
253+
? mcpServer.requireApproval
254+
: undefined;
255+
245256
return {
246257
type: "McpServer",
247258
mcpServer: {
@@ -250,6 +261,7 @@ function fromAgentFormDataToSandboxAgent(agentFormData: AgentFormData): SandboxA
250261
kind: mcpServer.kind,
251262
apiGroup: mcpServer.apiGroup,
252263
toolNames: mcpServer.toolNames,
264+
...(requireApproval ? { requireApproval } : {}),
253265
},
254266
} as Tool;
255267
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
2+
import { useState } from "react";
3+
import { SelectToolsDialog } from "./SelectToolsDialog";
4+
import type { AgentResponse, Tool, ToolsResponse } from "@/types";
5+
6+
const toolServer = "kagent/kagent-tool-server";
7+
8+
const availableTools: ToolsResponse[] = [
9+
{
10+
id: "cilium_get_endpoint_logs",
11+
server_name: toolServer,
12+
description: "Get the logs of an endpoint in the cluster",
13+
created_at: "2026-01-01T00:00:00Z",
14+
updated_at: "2026-01-01T00:00:00Z",
15+
deleted_at: "",
16+
group_kind: "MCPServer.kagent.dev",
17+
},
18+
{
19+
id: "other_tool",
20+
server_name: toolServer,
21+
description: "Another tool on the same server",
22+
created_at: "2026-01-01T00:00:00Z",
23+
updated_at: "2026-01-01T00:00:00Z",
24+
deleted_at: "",
25+
group_kind: "MCPServer.kagent.dev",
26+
},
27+
];
28+
29+
const availableAgents: AgentResponse[] = [
30+
{
31+
id: 1,
32+
agent: {
33+
metadata: { name: "argo-rollouts-conversion-agent", namespace: "kagent" },
34+
spec: {
35+
description:
36+
"The Argo Rollouts Converter AI Agent specializes in converting Kubernetes Deployments to Argo Rollouts.",
37+
type: "Declarative",
38+
},
39+
},
40+
model: "gpt-4",
41+
modelProvider: "openai",
42+
modelConfigRef: "default",
43+
tools: [],
44+
deploymentReady: true,
45+
accepted: true,
46+
},
47+
];
48+
49+
const mcpToolSelected: Tool = {
50+
type: "McpServer",
51+
mcpServer: {
52+
name: "kagent-tool-server",
53+
namespace: "kagent",
54+
kind: "MCPServer",
55+
apiGroup: "kagent.dev",
56+
toolNames: ["cilium_get_endpoint_logs", "other_tool"],
57+
requireApproval: ["cilium_get_endpoint_logs"],
58+
},
59+
};
60+
61+
const agentToolSelected: Tool = {
62+
type: "Agent",
63+
agent: {
64+
name: "argo-rollouts-conversion-agent",
65+
namespace: "kagent",
66+
kind: "Agent",
67+
apiGroup: "kagent.dev",
68+
},
69+
};
70+
71+
function DialogHarness({
72+
initialSelected,
73+
availableToolsOverride,
74+
availableAgentsOverride,
75+
}: {
76+
initialSelected: Tool[];
77+
availableToolsOverride?: ToolsResponse[];
78+
availableAgentsOverride?: AgentResponse[];
79+
}) {
80+
const [open, setOpen] = useState(true);
81+
const [selected, setSelected] = useState<Tool[]>(initialSelected);
82+
83+
return (
84+
<div className="flex flex-col gap-4">
85+
<p className="text-sm text-muted-foreground">
86+
Dialog stays open for visual checks. Use the story controls or reopen from the button if you close it.
87+
</p>
88+
<button
89+
type="button"
90+
className="w-fit rounded-md border px-3 py-1.5 text-sm"
91+
onClick={() => setOpen(true)}
92+
>
93+
Open dialog
94+
</button>
95+
<SelectToolsDialog
96+
open={open}
97+
onOpenChange={setOpen}
98+
availableTools={availableToolsOverride ?? availableTools}
99+
selectedTools={selected}
100+
onToolsSelected={setSelected}
101+
availableAgents={availableAgentsOverride ?? availableAgents}
102+
loadingAgents={false}
103+
currentAgentNamespace="kagent"
104+
/>
105+
</div>
106+
);
107+
}
108+
109+
const meta: Meta<typeof SelectToolsDialog> = {
110+
title: "Create/SelectToolsDialog",
111+
component: SelectToolsDialog,
112+
parameters: {
113+
layout: "fullscreen",
114+
},
115+
decorators: [
116+
(Story) => (
117+
<div className="min-h-[90vh] w-full bg-background p-6">
118+
<Story />
119+
</div>
120+
),
121+
],
122+
};
123+
124+
export default meta;
125+
type Story = StoryObj<typeof SelectToolsDialog>;
126+
127+
/** MCP tools in the right-hand list with “Require approval” on a separate row; one tool has approval enabled. */
128+
export const McpToolsWithRequireApproval: Story = {
129+
render: () => <DialogHarness initialSelected={[mcpToolSelected]} />,
130+
};
131+
132+
/** Selected MCP tools plus an agent-as-tool row (green icon) — approval controls only on MCP rows. */
133+
export const MixedMcpAndAgentSelected: Story = {
134+
render: () => <DialogHarness initialSelected={[mcpToolSelected, agentToolSelected]} />,
135+
};
136+
137+
/** Stress long titles and descriptions in the selected panel (truncation / line-clamp). */
138+
export const LongTextSelected: Story = {
139+
render: () => {
140+
const longTools: ToolsResponse[] = [
141+
{
142+
id: "very_long_tool_name_that_should_wrap_and_not_break_layout",
143+
server_name: "kagent/very-long-mcp-server-name-for-storybook",
144+
description:
145+
"This is an intentionally long description that should clamp to a single line in the selected-tools panel and show a tooltip on hover for the full text when supported.",
146+
created_at: "2026-01-01T00:00:00Z",
147+
updated_at: "2026-01-01T00:00:00Z",
148+
deleted_at: "",
149+
group_kind: "MCPServer.kagent.dev",
150+
},
151+
];
152+
const longMcp: Tool = {
153+
type: "McpServer",
154+
mcpServer: {
155+
name: "very-long-mcp-server-name-for-storybook",
156+
namespace: "kagent",
157+
kind: "MCPServer",
158+
apiGroup: "kagent.dev",
159+
toolNames: ["very_long_tool_name_that_should_wrap_and_not_break_layout"],
160+
requireApproval: [],
161+
},
162+
};
163+
return <DialogHarness initialSelected={[longMcp]} availableToolsOverride={longTools} />;
164+
},
165+
};

0 commit comments

Comments
 (0)