feat(mcp): add progress bar, throttling, and input validation for MCP tool progress (#19772)

This commit is contained in:
Jasmeet Bhatia
2026-02-24 09:13:51 -08:00
committed by GitHub
parent 4efdbe9089
commit c0b76af442
16 changed files with 647 additions and 46 deletions
@@ -187,8 +187,6 @@ type ToolInfoProps = {
description: string;
status: CoreToolCallStatus;
emphasis: TextEmphasis;
progressMessage?: string;
progressPercent?: number;
originalRequestName?: string;
};
@@ -197,8 +195,6 @@ export const ToolInfo: React.FC<ToolInfoProps> = ({
description,
status: coreStatus,
emphasis,
progressMessage,
progressPercent,
originalRequestName,
}) => {
const status = mapCoreStatusToDisplayStatus(coreStatus);
@@ -220,24 +216,6 @@ export const ToolInfo: React.FC<ToolInfoProps> = ({
// Hide description for completed Ask User tools (the result display speaks for itself)
const isCompletedAskUser = isCompletedAskUserTool(name, status);
let displayDescription = description;
if (status === ToolCallStatus.Executing) {
const parts: string[] = [];
if (progressMessage) {
parts.push(progressMessage);
}
if (progressPercent !== undefined) {
parts.push(`${Math.round(progressPercent)}%`);
}
if (parts.length > 0) {
const progressInfo = parts.join(' - ');
displayDescription = description
? `${description} (${progressInfo})`
: progressInfo;
}
}
return (
<Box overflow="hidden" height={1} flexGrow={1} flexShrink={1}>
<Text strikethrough={status === ToolCallStatus.Canceled} wrap="truncate">
@@ -253,7 +231,7 @@ export const ToolInfo: React.FC<ToolInfoProps> = ({
{!isCompletedAskUser && (
<>
{' '}
<Text color={theme.text.secondary}>{displayDescription}</Text>
<Text color={theme.text.secondary}>{description}</Text>
</>
)}
</Text>
@@ -261,6 +239,54 @@ export const ToolInfo: React.FC<ToolInfoProps> = ({
);
};
export interface McpProgressIndicatorProps {
progress: number;
total?: number;
message?: string;
barWidth: number;
}
export const McpProgressIndicator: React.FC<McpProgressIndicatorProps> = ({
progress,
total,
message,
barWidth,
}) => {
const percentage =
total && total > 0
? Math.min(100, Math.round((progress / total) * 100))
: null;
let rawFilled: number;
if (total && total > 0) {
rawFilled = Math.round((progress / total) * barWidth);
} else {
rawFilled = Math.floor(progress) % (barWidth + 1);
}
const filled = Math.max(
0,
Math.min(Number.isFinite(rawFilled) ? rawFilled : 0, barWidth),
);
const empty = Math.max(0, barWidth - filled);
const progressBar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
return (
<Box flexDirection="column">
<Box>
<Text color={theme.text.accent}>
{progressBar} {percentage !== null ? `${percentage}%` : `${progress}`}
</Text>
</Box>
{message && (
<Text color={theme.text.secondary} wrap="truncate">
{message}
</Text>
)}
</Box>
);
};
export const TrailingIndicator: React.FC = () => (
<Text color={theme.text.primary} wrap="truncate">
{' '}