Skip to content

Commit ed9f714

Browse files
feat(cli): Adds the ability to run MCP prompt commands in non-interactive mode (#10194)
Co-authored-by: Allen Hutchison <adh@google.com>
1 parent 5b750f5 commit ed9f714

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

packages/cli/src/nonInteractiveCli.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ vi.mock('./services/CommandService.js', () => ({
5959
},
6060
}));
6161

62+
vi.mock('./services/FileCommandLoader.js');
63+
vi.mock('./services/McpPromptLoader.js');
64+
6265
describe('runNonInteractive', () => {
6366
let mockConfig: Config;
6467
let mockSettings: LoadedSettings;
@@ -938,6 +941,46 @@ describe('runNonInteractive', () => {
938941
expect(processStdoutSpy).toHaveBeenCalledWith('Acknowledged');
939942
});
940943

944+
it('should instantiate CommandService with correct loaders for slash commands', async () => {
945+
// This test indirectly checks that handleSlashCommand is using the right loaders.
946+
const { FileCommandLoader } = await import(
947+
'./services/FileCommandLoader.js'
948+
);
949+
const { McpPromptLoader } = await import('./services/McpPromptLoader.js');
950+
951+
mockGetCommands.mockReturnValue([]); // No commands found, so it will fall through
952+
const events: ServerGeminiStreamEvent[] = [
953+
{ type: GeminiEventType.Content, value: 'Acknowledged' },
954+
{
955+
type: GeminiEventType.Finished,
956+
value: { reason: undefined, usageMetadata: { totalTokenCount: 1 } },
957+
},
958+
];
959+
mockGeminiClient.sendMessageStream.mockReturnValue(
960+
createStreamFromEvents(events),
961+
);
962+
963+
await runNonInteractive(
964+
mockConfig,
965+
mockSettings,
966+
'/mycommand',
967+
'prompt-id-loaders',
968+
);
969+
970+
// Check that loaders were instantiated with the config
971+
expect(FileCommandLoader).toHaveBeenCalledTimes(1);
972+
expect(FileCommandLoader).toHaveBeenCalledWith(mockConfig);
973+
expect(McpPromptLoader).toHaveBeenCalledTimes(1);
974+
expect(McpPromptLoader).toHaveBeenCalledWith(mockConfig);
975+
976+
// Check that instances were passed to CommandService.create
977+
expect(mockCommandServiceCreate).toHaveBeenCalledTimes(1);
978+
const loadersArg = mockCommandServiceCreate.mock.calls[0][0];
979+
expect(loadersArg).toHaveLength(2);
980+
expect(loadersArg[0]).toBe(vi.mocked(McpPromptLoader).mock.instances[0]);
981+
expect(loadersArg[1]).toBe(vi.mocked(FileCommandLoader).mock.instances[0]);
982+
});
983+
941984
it('should allow a normally-excluded tool when --allowed-tools is set', async () => {
942985
// By default, ShellTool is excluded in non-interactive mode.
943986
// This test ensures that --allowed-tools overrides this exclusion.

packages/cli/src/nonInteractiveCliCommands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from '@google/gemini-cli-core';
1515
import { CommandService } from './services/CommandService.js';
1616
import { FileCommandLoader } from './services/FileCommandLoader.js';
17+
import { McpPromptLoader } from './services/McpPromptLoader.js';
1718
import type { CommandContext } from './ui/commands/types.js';
1819
import { createNonInteractiveUI } from './ui/noninteractive/nonInteractiveUi.js';
1920
import type { LoadedSettings } from './config/settings.js';
@@ -38,9 +39,8 @@ export const handleSlashCommand = async (
3839
return;
3940
}
4041

41-
// Only custom commands are supported for now.
4242
const commandService = await CommandService.create(
43-
[new FileCommandLoader(config)],
43+
[new McpPromptLoader(config), new FileCommandLoader(config)],
4444
abortController.signal,
4545
);
4646
const commands = commandService.getCommands();

0 commit comments

Comments
 (0)