Enhance MCP tool trust functionality to support OAuth-backed connectors and improve error handling in the UI. Refactor API calls to use baseApiService for consistency.

This commit is contained in:
CREDO23 2026-04-23 08:03:32 +02:00
parent 7245ab4046
commit 16f47578d7
3 changed files with 20 additions and 26 deletions

View file

@ -3105,13 +3105,18 @@ async def trust_mcp_tool(
"""Add a tool to the MCP connector's trusted (always-allow) list. """Add a tool to the MCP connector's trusted (always-allow) list.
Once trusted, the tool executes without HITL approval on subsequent calls. Once trusted, the tool executes without HITL approval on subsequent calls.
Works for both generic MCP_CONNECTOR and OAuth-backed MCP connectors
(LINEAR_CONNECTOR, JIRA_CONNECTOR, etc.) by checking for ``server_config``.
""" """
try: try:
from sqlalchemy import cast
from sqlalchemy.dialects.postgresql import JSONB as PG_JSONB
result = await session.execute( result = await session.execute(
select(SearchSourceConnector).filter( select(SearchSourceConnector).filter(
SearchSourceConnector.id == connector_id, SearchSourceConnector.id == connector_id,
SearchSourceConnector.connector_type SearchSourceConnector.user_id == user.id,
== SearchSourceConnectorType.MCP_CONNECTOR, cast(SearchSourceConnector.config, PG_JSONB).has_key("server_config"), # noqa: W601
) )
) )
connector = result.scalars().first() connector = result.scalars().first()
@ -3156,13 +3161,17 @@ async def untrust_mcp_tool(
"""Remove a tool from the MCP connector's trusted list. """Remove a tool from the MCP connector's trusted list.
The tool will require HITL approval again on subsequent calls. The tool will require HITL approval again on subsequent calls.
Works for both generic MCP_CONNECTOR and OAuth-backed MCP connectors.
""" """
try: try:
from sqlalchemy import cast
from sqlalchemy.dialects.postgresql import JSONB as PG_JSONB
result = await session.execute( result = await session.execute(
select(SearchSourceConnector).filter( select(SearchSourceConnector).filter(
SearchSourceConnector.id == connector_id, SearchSourceConnector.id == connector_id,
SearchSourceConnector.connector_type SearchSourceConnector.user_id == user.id,
== SearchSourceConnectorType.MCP_CONNECTOR, cast(SearchSourceConnector.config, PG_JSONB).has_key("server_config"), # noqa: W601
) )
) )
connector = result.scalars().first() connector = result.scalars().first()

View file

@ -3,6 +3,7 @@
import type { ToolCallMessagePartComponent } from "@assistant-ui/react"; import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
import { CornerDownLeftIcon, Pen } from "lucide-react"; import { CornerDownLeftIcon, Pen } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { TextShimmerLoader } from "@/components/prompt-kit/loader";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@ -116,8 +117,8 @@ function GenericApprovalCard({
if (phase !== "pending" || !isMCPTool) return; if (phase !== "pending" || !isMCPTool) return;
setProcessing(); setProcessing();
onDecision({ type: "approve" }); onDecision({ type: "approve" });
connectorsApiService.trustMCPTool(mcpConnectorId, toolName).catch((err) => { connectorsApiService.trustMCPTool(mcpConnectorId, toolName).catch(() => {
console.error("Failed to trust MCP tool:", err); toast.error("Failed to save 'Always Allow' preference. The tool will still require approval next time.");
}); });
}, [phase, setProcessing, onDecision, isMCPTool, mcpConnectorId, toolName]); }, [phase, setProcessing, onDecision, isMCPTool, mcpConnectorId, toolName]);

View file

@ -414,16 +414,8 @@ class ConnectorsApiService {
* Subsequent calls to this tool will skip HITL approval. * Subsequent calls to this tool will skip HITL approval.
*/ */
trustMCPTool = async (connectorId: number, toolName: string): Promise<void> => { trustMCPTool = async (connectorId: number, toolName: string): Promise<void> => {
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; await baseApiService.post(`/api/v1/connectors/mcp/${connectorId}/trust-tool`, undefined, {
const token = body: { tool_name: toolName },
typeof window !== "undefined" ? document.cookie.match(/fapiToken=([^;]+)/)?.[1] : undefined;
await fetch(`${backendUrl}/api/v1/connectors/mcp/${connectorId}/trust-tool`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify({ tool_name: toolName }),
}); });
}; };
@ -431,16 +423,8 @@ class ConnectorsApiService {
* Remove a tool from the MCP connector's "Always Allow" list. * Remove a tool from the MCP connector's "Always Allow" list.
*/ */
untrustMCPTool = async (connectorId: number, toolName: string): Promise<void> => { untrustMCPTool = async (connectorId: number, toolName: string): Promise<void> => {
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; await baseApiService.post(`/api/v1/connectors/mcp/${connectorId}/untrust-tool`, undefined, {
const token = body: { tool_name: toolName },
typeof window !== "undefined" ? document.cookie.match(/fapiToken=([^;]+)/)?.[1] : undefined;
await fetch(`${backendUrl}/api/v1/connectors/mcp/${connectorId}/untrust-tool`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify({ tool_name: toolName }),
}); });
}; };
} }