Dont show MCP UI/Apps until tool is approved (#6492)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-01-21 09:09:37 -05:00
committed by GitHub
parent 51cc5db387
commit 727679ce92
4 changed files with 46 additions and 4 deletions
@@ -10,6 +10,7 @@ import {
getToolResponses,
getToolConfirmationContent,
getElicitationContent,
getPendingToolConfirmationIds,
NotificationEvent,
} from '../types/message';
import { Message } from '../api';
@@ -99,6 +100,8 @@ export default function GooseMessage({
return responseMap;
}, [messages, messageIndex, toolRequests]);
const pendingConfirmationIds = getPendingToolConfirmationIds(messages);
return (
<div className="goose-message flex w-[90%] justify-start min-w-0">
<div className="flex flex-col w-full min-w-0">
@@ -157,6 +160,7 @@ export default function GooseMessage({
toolResponse={toolResponsesMap.get(toolRequest.id)}
notifications={toolCallNotifications.get(toolRequest.id)}
isStreamingMessage={isStreaming}
isPendingApproval={pendingConfirmationIds.has(toolRequest.id)}
append={append}
/>
</div>
@@ -55,6 +55,7 @@ interface ToolCallWithResponseProps {
toolResponse?: ToolResponseMessageContent;
notifications?: NotificationEvent[];
isStreamingMessage?: boolean;
isPendingApproval: boolean;
append?: (value: string) => void;
}
@@ -110,10 +111,8 @@ function McpAppWrapper({
? requestWithMeta.toolCall.value.arguments
: undefined;
// Memoize toolInput to prevent unnecessary re-renders
const toolInput = useMemo(() => ({ arguments: toolArguments || {} }), [toolArguments]);
// Memoize toolResult to prevent unnecessary re-renders
const toolResult = useMemo(() => {
if (!toolResponse) return undefined;
const resultWithMeta = toolResponse.toolResult as ToolResultWithMeta;
@@ -153,6 +152,7 @@ export default function ToolCallWithResponse({
toolResponse,
notifications,
isStreamingMessage,
isPendingApproval,
append,
}: ToolCallWithResponseProps) {
// Handle both the wrapped ToolResult format and the unwrapped format
@@ -173,6 +173,8 @@ export default function ToolCallWithResponse({
requestWithMeta._meta?.ui?.resourceUri || resultWithMeta?.value?._meta?.ui?.resourceUri
);
const shouldShowMcpContent = !isPendingApproval;
return (
<>
<div
@@ -191,7 +193,8 @@ export default function ToolCallWithResponse({
/>
</div>
{/* MCP UI — Inline */}
{!hasMcpAppResourceURI &&
{shouldShowMcpContent &&
!hasMcpAppResourceURI &&
toolResponse?.toolResult &&
getToolResultContent(toolResponse.toolResult).map((content, index) => {
const resourceContent = isEmbeddedResource(content)
@@ -214,7 +217,8 @@ export default function ToolCallWithResponse({
}
})}
{hasMcpAppResourceURI && sessionId && (
{/* MCP App */}
{shouldShowMcpContent && hasMcpAppResourceURI && sessionId && (
<McpAppWrapper
toolRequest={toolRequest}
toolResponse={toolResponse}
@@ -160,6 +160,7 @@ export const SessionMessages: React.FC<SessionMessagesProps> = ({
isCancelledMessage={
toolResponsesMap.get(toolRequest.id) == undefined
}
isPendingApproval={false}
key={toolRequest.id}
toolRequest={toolRequest}
toolResponse={toolResponsesMap.get(toolRequest.id)}
+33
View File
@@ -73,6 +73,39 @@ export function getToolConfirmationContent(
);
}
export function getToolConfirmationId(
content: ActionRequired & { type: 'actionRequired' }
): string | undefined {
if (content.data.actionType === 'toolConfirmation') {
return content.data.id;
}
return undefined;
}
export function getPendingToolConfirmationIds(messages: Message[]): Set<string> {
const pendingIds = new Set<string>();
const respondedIds = new Set<string>();
for (const message of messages) {
const responses = getToolResponses(message);
for (const response of responses) {
respondedIds.add(response.id);
}
}
for (const message of messages) {
const confirmation = getToolConfirmationContent(message);
if (confirmation) {
const confirmationId = getToolConfirmationId(confirmation);
if (confirmationId && !respondedIds.has(confirmationId)) {
pendingIds.add(confirmationId);
}
}
}
return pendingIds;
}
export function getElicitationContent(
message: Message
): (ActionRequired & { type: 'actionRequired' }) | undefined {