mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-30 07:51:07 -07:00
feat(mcp): add progress bar, throttling, and input validation for MCP tool progress (#19772)
This commit is contained in:
@@ -1,16 +1,22 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import {
|
||||
CoreEventEmitter,
|
||||
CoreEvent,
|
||||
coreEvents,
|
||||
type UserFeedbackPayload,
|
||||
type McpProgressPayload,
|
||||
} from './events.js';
|
||||
|
||||
vi.mock('./debugLogger.js', () => ({
|
||||
debugLogger: { log: vi.fn() },
|
||||
}));
|
||||
|
||||
describe('CoreEventEmitter', () => {
|
||||
let events: CoreEventEmitter;
|
||||
|
||||
@@ -360,4 +366,63 @@ describe('CoreEventEmitter', () => {
|
||||
expect(listener.mock.calls[0][0]).toMatchObject({ prompt: 'Consent 10' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('emitMcpProgress validation', () => {
|
||||
const basePayload: McpProgressPayload = {
|
||||
serverName: 'test-server',
|
||||
callId: 'call-1',
|
||||
progressToken: 'token-1',
|
||||
progress: 0,
|
||||
};
|
||||
|
||||
let listener: ReturnType<typeof vi.fn>;
|
||||
|
||||
afterEach(() => {
|
||||
if (listener) {
|
||||
coreEvents.off(CoreEvent.McpProgress, listener);
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects NaN progress', () => {
|
||||
listener = vi.fn();
|
||||
coreEvents.on(CoreEvent.McpProgress, listener);
|
||||
|
||||
coreEvents.emitMcpProgress({ ...basePayload, progress: NaN });
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects negative progress', () => {
|
||||
listener = vi.fn();
|
||||
coreEvents.on(CoreEvent.McpProgress, listener);
|
||||
|
||||
coreEvents.emitMcpProgress({ ...basePayload, progress: -1 });
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects Infinity progress', () => {
|
||||
listener = vi.fn();
|
||||
coreEvents.on(CoreEvent.McpProgress, listener);
|
||||
|
||||
coreEvents.emitMcpProgress({ ...basePayload, progress: Infinity });
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('emits valid progress payload', () => {
|
||||
listener = vi.fn();
|
||||
coreEvents.on(CoreEvent.McpProgress, listener);
|
||||
|
||||
const payload: McpProgressPayload = {
|
||||
...basePayload,
|
||||
progress: 5,
|
||||
total: 10,
|
||||
message: 'test',
|
||||
};
|
||||
coreEvents.emitMcpProgress(payload);
|
||||
|
||||
expect(listener).toHaveBeenCalledExactlyOnceWith(payload);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
TokenStorageInitializationEvent,
|
||||
KeychainAvailabilityEvent,
|
||||
} from '../telemetry/types.js';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
/**
|
||||
* Defines the severity level for user-facing feedback.
|
||||
@@ -353,6 +354,10 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
|
||||
* Notifies subscribers that progress has been made on an MCP tool call.
|
||||
*/
|
||||
emitMcpProgress(payload: McpProgressPayload): void {
|
||||
if (!Number.isFinite(payload.progress) || payload.progress < 0) {
|
||||
debugLogger.log(`Invalid progress value: ${payload.progress}`);
|
||||
return;
|
||||
}
|
||||
this.emit(CoreEvent.McpProgress, payload);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user