Skip to content

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

Merged
jacob314 merged 3 commits intogoogle-gemini:mainfrom
jasmeetsb:feature/mcp-progress-enhancements
Feb 24, 2026
Merged

feat(mcp): add progress bar, throttling, and input validation for MCP tool progress#19772
jacob314 merged 3 commits intogoogle-gemini:mainfrom
jasmeetsb:feature/mcp-progress-enhancements

Conversation

@jasmeetsb
Copy link
Copy Markdown
Contributor

Follow-up to #19046 to add enhancements to the long-running MCP tool call progress feature.

Fixes #16934

Summary

ezgif-2e5b15c9805e533b

Adds a visual progress bar, throttling, input validation, and percentage capping to MCP tool progress reporting.

Feature Details

  • Progress bar: McpProgressIndicator, rendered inside the tool's bordered content box. Replaces feat(core): add support for MCP progress updates #19046's inline text.
  • Throttle: Per-callId leading+trailing edge throttle (100ms) in scheduler.ts to prevent UI lag from high-frequency progress events.
  • Percentage cap: Math.min(100, ...) for servers reporting progress > total.
  • Input validation: Number.isFinite() + >= 0 guard in emitMcpProgress to reject NaN/negative/Infinity values.
  • Raw progress fields: Added progress/progressTotal to ExecutingToolCall and mapped through to UI, since the bar needs raw values to compute fill width.

Related Issues

Fixes #16934

How to Validate

Manual

Configure an MCP server that sends progress notifications (e.g., @modelcontextprotocol/server-everything):

{
  "mcpServers": {
    "everything": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-everything"]
    }
  }
}

Run npm start and prompt: "Use the longRunningOperation tool with duration 10 and steps 5"

Testing done

  • Added 24 new unit tests covering throttling, input validation, field preservation, progress bar rendering, and mapping
  • Full test suite: 10,462 tests passed, 0 failures
  • Manual: verified determinate/indeterminate progress, rapid events, error mid-progress, and no-progress regression

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

@jasmeetsb jasmeetsb requested a review from a team as a code owner February 21, 2026 00:45
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @jasmeetsb, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience for long-running Model Context Protocol (MCP) tool calls by providing a more robust and visually appealing progress reporting mechanism. It addresses potential performance issues with frequent updates and improves data integrity by validating incoming progress information, ensuring a smoother and more reliable display of tool execution status.

Highlights

  • Visual Progress Bar: Introduced a new McpProgressIndicator component for displaying a visual progress bar within the tool's bordered content box, replacing previous inline text progress.
  • Progress Event Throttling: Implemented a per-callId leading+trailing edge throttle (100ms) in the scheduler to prevent UI lag caused by high-frequency progress events from MCP tools.
  • Input Validation and Percentage Capping: Added input validation for emitMcpProgress to reject invalid progress values (NaN, negative, Infinity) and capped reported percentages at 100% to handle cases where progress > total.
  • Raw Progress Fields: Extended ExecutingToolCall and related UI types to include raw progress and progressTotal fields, allowing the progress bar to accurately compute fill width.
Changelog
  • packages/cli/src/ui/components/messages/ToolMessage.test.tsx
    • Updated existing test to verify McpProgressIndicator rendering with percentage and message.
    • Added a new test case for rendering indeterminate progress when the total is not provided.
    • Removed assertions for the old inline text progress format.
    • Added snapshot tests for various progress display scenarios.
  • packages/cli/src/ui/components/messages/ToolMessage.tsx
    • Imported McpProgressIndicator component and CoreToolCallStatus.
    • Removed progressMessage and progressPercent props from ToolInfo component.
    • Added progress and progressTotal props to ToolMessage component.
    • Conditionally rendered McpProgressIndicator when the tool status is Executing and progress is defined.
  • packages/cli/src/ui/components/messages/ToolShared.test.tsx
    • Added a new test file for the McpProgressIndicator component.
    • Included tests for determinate progress at 50% and 100%.
    • Added a test for indeterminate progress display with raw count.
    • Verified progress rendering with an accompanying message.
    • Confirmed that progress exceeding the total is clamped to 100%.
  • packages/cli/src/ui/components/messages/ToolShared.tsx
    • Removed progressMessage and progressPercent props from ToolInfoProps and its component.
    • Removed the logic for constructing inline progress messages within ToolInfo.
    • Introduced McpProgressIndicatorProps interface.
    • Implemented the McpProgressIndicator React component to display a visual progress bar, percentage, and optional message, handling both determinate and indeterminate states.
  • packages/cli/src/ui/components/messages/snapshots/ToolMessage.test.tsx.snap
    • Updated snapshots to reflect the new McpProgressIndicator rendering within ToolMessage.
  • packages/cli/src/ui/components/messages/snapshots/ToolShared.test.tsx.snap
    • Added new snapshots for the McpProgressIndicator component.
  • packages/cli/src/ui/hooks/toolMapping.test.ts
    • Added a test to verify that raw progress and progressTotal fields are correctly mapped from ExecutingToolCall.
    • Added a test to confirm that progress fields are undefined for non-executing tool calls.
  • packages/cli/src/ui/hooks/toolMapping.ts
    • Added progress and progressTotal variables to the mapToDisplay function.
    • Mapped progress and progressTotal from ExecutingToolCall to the display tool object.
  • packages/cli/src/ui/types.ts
    • Added optional progress and progressTotal properties to the IndividualToolCallDisplay interface.
  • packages/core/src/scheduler/scheduler.test.ts
    • Added a new test suite for Scheduler MCP Progress Throttle.
    • Included tests for leading-edge updates, batching rapid updates, and trailing-edge application.
    • Verified flushing pending progress on scheduler disposal.
    • Confirmed that the scheduler does not respond to progress events after disposal.
    • Tested independent throttling for concurrent calls.
    • Ensured progress is ignored for unknown or terminal call IDs.
    • Added a test to prevent stale trailing payloads from applying after a call is cancelled.
  • packages/core/src/scheduler/scheduler.ts
    • Initialized progressThrottles map to manage per-callId throttling state.
    • Modified dispose method to call flushProgressThrottle.
    • Updated handleMcpProgress to implement leading+trailing edge throttling logic.
    • Introduced applyProgress private method to encapsulate the logic for updating tool call status with progress information, including percentage calculation, capping, and validation of total.
    • Implemented flushProgressThrottle to clear timers and apply any pending progress updates.
  • packages/core/src/scheduler/state-manager.test.ts
    • Added tests to ensure progress, progressTotal, progressMessage, and progressPercent are preserved when transitioning to Executing status.
    • Added a test to confirm progress fields are preserved after subsequent liveOutput updates.
  • packages/core/src/scheduler/state-manager.ts
    • Imported ROOT_SCHEDULER_ID from the same file.
    • Modified toExecuting method to preserve progress and progressTotal fields during state updates.
  • packages/core/src/scheduler/types.ts
    • Added optional progress and progressTotal properties to the ExecutingToolCall type.
  • packages/core/src/utils/events.test.ts
    • Added mock for debugLogger.
    • Added a new test suite for emitMcpProgress validation.
    • Included tests to reject NaN, negative, and Infinity values for progress.
    • Verified that valid progress payloads are emitted correctly.
  • packages/core/src/utils/events.ts
    • Imported debugLogger.
    • Added input validation to emitMcpProgress to check if payload.progress is a finite, non-negative number, logging invalid values and preventing their emission.
Activity
  • The author added 24 new unit tests covering throttling, input validation, field preservation, progress bar rendering, and mapping.
  • The full test suite passed with 10,462 tests and 0 failures.
  • Manual validation was performed to verify determinate/indeterminate progress, rapid events, error mid-progress, and no-progress regression.
  • The pull request includes a pre-merge checklist with documentation and test updates marked as complete, and validation on Linux (npm run) confirmed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a visual progress bar, throttling, and input validation for MCP tool progress reporting. The changes are well-tested with new unit tests covering various scenarios. The implementation correctly integrates the new McpProgressIndicator component and handles progress updates with throttling and validation. The ToolMessage component now uses the new progress indicator, and the Scheduler correctly processes and throttles progress events. The CoreEventEmitter also includes validation for McpProgressPayload to ensure data integrity, aligning with the principle of treating user-provided data as untrusted. Overall, the changes enhance the user experience by providing clear progress feedback and improve the robustness of the system by validating incoming progress data and preventing UI lag.

Comment thread packages/core/src/scheduler/scheduler.test.ts Outdated
Comment thread packages/core/src/utils/events.ts
Comment thread packages/core/src/utils/events.test.ts
Comment thread packages/core/src/utils/events.test.ts
Comment thread packages/core/src/utils/events.test.ts
Comment thread packages/core/src/scheduler/state-manager.test.ts
Comment thread packages/core/src/scheduler/scheduler.ts
Comment thread packages/cli/src/ui/components/messages/ToolShared.tsx
Comment thread packages/cli/src/ui/components/messages/ToolMessage.test.tsx
Comment thread packages/cli/src/ui/components/messages/ToolMessage.test.tsx
@jasmeetsb
Copy link
Copy Markdown
Contributor Author

@jacob314 - For your review. Thanks

@jasmeetsb jasmeetsb force-pushed the feature/mcp-progress-enhancements branch from 96e7846 to 9e805b6 Compare February 21, 2026 01:05
@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/platform Issues related to Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! labels Feb 21, 2026
@jasmeetsb jasmeetsb force-pushed the feature/mcp-progress-enhancements branch from 9e805b6 to 4516447 Compare February 23, 2026 18:48
Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an excellent visual improvement for long-running MCP tools! The implementation correctly calculates the progress dimensions, properly bounds values with safety checks, and implements a solid throttle in Scheduler to preserve Ink UI performance under high-frequency updates.

I’ve verified the diffs and ran npm run preflight which completed successfully with all tests passing.

Here are a few minor findings/nitpicks that do not block approval:

Minor Improvements / Nitpicks

  • Unused progressPercent type field: In packages/cli/src/ui/types.ts and packages/cli/src/ui/hooks/toolMapping.ts, the progressPercent property is mapped and defined but is no longer consumed by any UI component (removed from ToolMessage and ToolShared). You can safely remove it from the UI layer to clean up the type definition if desired.
  • Top Margin in Progress Bar Container: McpProgressIndicator has marginTop={1} on its root Box. Because the container border box generally lacks a paddingY, this causes the progress bar to sit one line lower inside the top border compared to standard ToolResultDisplay text output. It looks fine, but could be adjusted if strict visual alignment with standard tool output is desired.
  • Indeterminate Jumpiness: The modulo math (Math.floor(progress) % (barWidth + 1)) works beautifully for progress values that increment monotonically (e.g., 1, 2, 3... steps). If an MCP server sends progress as arbitrary raw byte counts without a total (e.g., jumping from 1024 to 4096), the bar will appear to jump around randomly. Given the MCP spec, this is acceptable, but something to be aware of for arbitrary tools.

Great work on the tests and defensive input validation (Number.isFinite) too!

@jasmeetsb jasmeetsb force-pushed the feature/mcp-progress-enhancements branch from 4516447 to 1f2d3b2 Compare February 23, 2026 22:13
@jasmeetsb
Copy link
Copy Markdown
Contributor Author

Thanks @jacob314
New commit addresses item 1 and 2.

@jasmeetsb jasmeetsb force-pushed the feature/mcp-progress-enhancements branch from 1f2d3b2 to 11d54dc Compare February 23, 2026 22:50
private isProcessing = false;
private isCancelling = false;
private readonly requestQueue: SchedulerQueueItem[] = [];
private readonly progressThrottles = new Map<
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please revert the throttle logic from core. If there is a really a performance issue here it should be addressed on the package/cli side throttling the rendering rather than filtering the events. the logic for throttling here seems a bit fragile and hard to get right.

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved once the scheduler changes are reverted. Don't think that is the right layer for UI specific throttling.

@jacob314
Copy link
Copy Markdown
Contributor

Some thoughts from Gemini about removing the scheduler logic:
We can completely remove the timers and helpers, and instead use a simple timestamp-based "drop" throttle (very similar to how shell.ts handles rapid
binary progress updates). This allows handleMcpProgress to remain a single, self-contained method just like it was originally implemented in #19046.

Here is what the vastly simplified code would look like in scheduler.ts:

1   // Replace the complex object map with a simple timestamp map
2   private readonly lastProgressUpdates = new Map<string, number>();
3
4   private readonly handleMcpProgress = (payload: McpProgressPayload) => {
5     const { callId } = payload;
6
7     const call = this.state.getToolCall(callId);
8     if (!call || call.status !== CoreToolCallStatus.Executing) {
9       return;

10 }
11
12 // Simple time-based throttle: drop events that occur within 100ms
13 const now = Date.now();
14 const lastUpdate = this.lastProgressUpdates.get(callId) ?? 0;
15 if (now - lastUpdate < 100) {
16 return;
17 }
18 this.lastProgressUpdates.set(callId, now);
19
20 const validTotal =
21 payload.total !== undefined &&
22 Number.isFinite(payload.total) &&
23 payload.total > 0
24 ? payload.total
25 : undefined;
26
27 this.state.updateStatus(callId, CoreToolCallStatus.Executing, {
28 progressMessage: payload.message,
29 progressPercent: validTotal
30 ? Math.min(100, (payload.progress / validTotal) * 100)
31 : undefined,
32 progress: payload.progress,
33 progressTotal: validTotal,
34 });
35 };

What this improves:

  1. Removes Boilerplate: We can completely delete applyProgress, flushProgressThrottle, and the dispose() timer cleanup logic.
  2. Safety: No risk of memory leaks from un-cleared setTimeout handles or issues with background timers overriding state transitions.
  3. Alignment: It keeps all the MCP progress logic contained inside handleMcpProgress, preserving the original structural footprint of the scheduler
    prior to this PR.

… tool progress

Enhances MCP progress reporting (layered on google-gemini#19046) with:

- Visual progress bar component (McpProgressIndicator) using block
  characters, supporting determinate (percentage) and indeterminate
  (step count) modes
- Per-callId leading+trailing throttle (100ms) in Scheduler to prevent
  UI lag from high-frequency MCP server progress events
- Percentage capping at 100% for misbehaving servers
- Input validation in emitMcpProgress rejecting NaN/negative/Infinity
- Raw progress/progressTotal fields propagated through the pipeline
  for bar fill computation

Fixes google-gemini#16934
@jasmeetsb jasmeetsb force-pushed the feature/mcp-progress-enhancements branch from 11d54dc to fd451be Compare February 24, 2026 16:31
@jasmeetsb
Copy link
Copy Markdown
Contributor Author

@jacob314 - Thanks for the feedback. I went with removing the throttle entirely rather than the timestamp-based drop suggested by Gemini. The drop approach simplifies the code but still puts UI-specific throttling in Core's scheduler, which doesn't fully address your feedback about it being the wrong layer. Since Ink already batches terminal writes and my manual testing with rapid events (200 at 10ms intervals) showed no lag, this can possibly be addressed later.

Now the PR only addresses Visual Progress Bar and Input validation for MCP Tool call progress.

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@jacob314 jacob314 enabled auto-merge February 24, 2026 16:59
@jacob314 jacob314 added this pull request to the merge queue Feb 24, 2026
Merged via the queue into google-gemini:main with commit c0b76af Feb 24, 2026
27 checks passed
BryanBradfo pushed a commit to BryanBradfo/gemini-cli that referenced this pull request Mar 5, 2026
liamhelmer pushed a commit to badal-io/gemini-cli that referenced this pull request Mar 12, 2026
warrenzhu25 pushed a commit to warrenzhu25/gemini-cli that referenced this pull request Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues related to Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p2 Important but can be addressed in a future release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for MCP progress reports and MCP tasks

2 participants