hitl/generic-approval: drop client-side MCP gate, dispatch approve_always

The 'Always Allow' button is now driven entirely by the server-supplied
allowed_decisions palette. The card no longer peeks at
context.mcp_connector_id to decide whether to render the button, and no
longer fires a separate trust-tool HTTP call on click - one
{type: 'approve_always'} dispatch is enough; the agent middleware
handles the in-memory promotion and (for MCP tools) the database save
via its trusted_tool_saver callback.

Drops the dead trustMCPTool / untrustMCPTool service helpers - they had
no remaining callers after this rework. The backing HTTP routes are
kept on the server as a programmatic surface.
This commit is contained in:
CREDO23 2026-05-15 14:59:45 +02:00
parent 98b6977c68
commit 1f1b6c5425
2 changed files with 7 additions and 38 deletions

View file

@ -2,13 +2,11 @@
import { CornerDownLeftIcon, Pencil } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { TextShimmerLoader } from "@/components/prompt-kit/loader";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { getToolDisplayName } from "@/contracts/enums/toolIcons";
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import type { HitlDecision, InterruptResult, PerToolApprovalCard } from "../types";
import { useHitlDecision } from "../use-hitl-decision";
import { useHitlPhase } from "../use-hitl-phase";
@ -81,12 +79,11 @@ function GenericApprovalCardView({
const mcpServer = interruptData.context?.mcp_server as string | undefined;
const toolDescription = interruptData.context?.tool_description as string | undefined;
const mcpConnectorId = interruptData.context?.mcp_connector_id as number | undefined;
const isMCPTool = mcpConnectorId != null;
const reviewConfig = interruptData.review_configs?.[0];
const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"];
const canEdit = allowedDecisions.includes("edit");
const canApproveAlways = allowedDecisions.includes("approve_always");
const hasChanged = useMemo(() => {
return JSON.stringify(editedParams) !== JSON.stringify(args);
@ -113,16 +110,11 @@ function GenericApprovalCardView({
editedParams,
]);
const handleAlwaysAllow = useCallback(() => {
if (phase !== "pending" || !isMCPTool) return;
const handleApproveAlways = useCallback(() => {
if (phase !== "pending" || !canApproveAlways) return;
setProcessing();
onDecision({ type: "approve" });
connectorsApiService.trustMCPTool(mcpConnectorId, toolName).catch(() => {
toast.error(
"Failed to save 'Always Allow' preference. The tool will still require approval next time."
);
});
}, [phase, setProcessing, onDecision, isMCPTool, mcpConnectorId, toolName]);
onDecision({ type: "approve_always" });
}, [phase, setProcessing, onDecision, canApproveAlways]);
useEffect(() => {
const handler = (e: KeyboardEvent) => {
@ -214,8 +206,8 @@ function GenericApprovalCardView({
<CornerDownLeftIcon className="size-3 opacity-60" />
</Button>
)}
{isMCPTool && (
<Button size="sm" className="rounded-lg" onClick={handleAlwaysAllow}>
{canApproveAlways && (
<Button size="sm" className="rounded-lg" onClick={handleApproveAlways}>
Always Allow
</Button>
)}

View file

@ -405,29 +405,6 @@ class ConnectorsApiService {
);
};
// =============================================================================
// MCP Tool Trust (Allow-List) Methods
// =============================================================================
/**
* Add a tool to the MCP connector's "Always Allow" list.
* Subsequent calls to this tool will skip HITL approval.
*/
trustMCPTool = async (connectorId: number, toolName: string): Promise<void> => {
await baseApiService.post(`/api/v1/connectors/mcp/${connectorId}/trust-tool`, undefined, {
body: { tool_name: toolName },
});
};
/**
* Remove a tool from the MCP connector's "Always Allow" list.
*/
untrustMCPTool = async (connectorId: number, toolName: string): Promise<void> => {
await baseApiService.post(`/api/v1/connectors/mcp/${connectorId}/untrust-tool`, undefined, {
body: { tool_name: toolName },
});
};
/** Live stats for the Obsidian connector tile. */
getObsidianStats = async (vaultId: string): Promise<ObsidianStats> => {
return baseApiService.get<ObsidianStats>(