Skip to content

Commit d2b5b4f

Browse files
feat(ui): improve discoverability of MCP slash commands (google-gemini#6080)
Co-authored-by: Rinil Kunhiraman <[email protected]> Co-authored-by: Allen Hutchison <[email protected]>
1 parent a027010 commit d2b5b4f

File tree

4 files changed

+210
-488
lines changed

4 files changed

+210
-488
lines changed

packages/cli/src/ui/components/Help.tsx

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import type React from 'react';
88
import { Box, Text } from 'ink';
99
import { Colors } from '../colors.js';
10-
import type { SlashCommand } from '../commands/types.js';
10+
import { type SlashCommand, CommandKind } from '../commands/types.js';
1111

1212
interface Help {
1313
commands: readonly SlashCommand[];
@@ -64,34 +64,43 @@ export const Help: React.FC<Help> = ({ commands }) => (
6464
<Text bold color={Colors.Foreground}>
6565
Commands:
6666
</Text>
67-
{commands.map((command: SlashCommand) => (
68-
<Box key={command.name} flexDirection="column">
69-
<Text color={Colors.Foreground}>
70-
<Text bold color={Colors.AccentPurple}>
71-
{' '}
72-
/{command.name}
67+
{commands
68+
.filter((command) => command.description)
69+
.map((command: SlashCommand) => (
70+
<Box key={command.name} flexDirection="column">
71+
<Text color={Colors.Foreground}>
72+
<Text bold color={Colors.AccentPurple}>
73+
{' '}
74+
/{command.name}
75+
</Text>
76+
{command.kind === CommandKind.MCP_PROMPT && (
77+
<Text color={Colors.Gray}> [MCP]</Text>
78+
)}
79+
{command.description && ' - ' + command.description}
7380
</Text>
74-
{command.description && ' - ' + command.description}
75-
</Text>
76-
{command.subCommands &&
77-
command.subCommands.map((subCommand) => (
78-
<Text key={subCommand.name} color={Colors.Foreground}>
79-
<Text bold color={Colors.AccentPurple}>
80-
{' '}
81-
{subCommand.name}
81+
{command.subCommands &&
82+
command.subCommands.map((subCommand) => (
83+
<Text key={subCommand.name} color={Colors.Foreground}>
84+
<Text bold color={Colors.AccentPurple}>
85+
{' '}
86+
{subCommand.name}
87+
</Text>
88+
{subCommand.description && ' - ' + subCommand.description}
8289
</Text>
83-
{subCommand.description && ' - ' + subCommand.description}
84-
</Text>
85-
))}
86-
</Box>
87-
))}
90+
))}
91+
</Box>
92+
))}
8893
<Text color={Colors.Foreground}>
8994
<Text bold color={Colors.AccentPurple}>
9095
{' '}
9196
!{' '}
9297
</Text>
9398
- shell command
9499
</Text>
100+
<Text color={Colors.Foreground}>
101+
<Text color={Colors.Gray}>[MCP]</Text> - Model Context Protocol command
102+
(from external servers)
103+
</Text>
95104

96105
<Box height={1} />
97106

packages/cli/src/ui/components/SuggestionsDisplay.tsx

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
import { Box, Text } from 'ink';
88
import { Colors } from '../colors.js';
99
import { PrepareLabel } from './PrepareLabel.js';
10-
import { isSlashCommand } from '../utils/commandUtils.js';
10+
import { CommandKind } from '../commands/types.js';
1111
export interface Suggestion {
1212
label: string;
1313
value: string;
1414
description?: string;
1515
matchedIndex?: number;
16+
commandKind?: CommandKind;
1617
}
1718
interface SuggestionsDisplayProps {
1819
suggestions: Suggestion[];
@@ -53,21 +54,6 @@ export function SuggestionsDisplay({
5354
);
5455
const visibleSuggestions = suggestions.slice(startIndex, endIndex);
5556

56-
const isSlashCommandMode = isSlashCommand(userInput);
57-
let commandNameWidth = 0;
58-
59-
if (isSlashCommandMode) {
60-
const maxLabelLength = visibleSuggestions.length
61-
? Math.max(...visibleSuggestions.map((s) => s.label.length))
62-
: 0;
63-
64-
const maxAllowedWidth = Math.floor(width * 0.35);
65-
commandNameWidth = Math.max(
66-
15,
67-
Math.min(maxLabelLength + 2, maxAllowedWidth),
68-
);
69-
}
70-
7157
return (
7258
<Box flexDirection="column" paddingX={1} width={width}>
7359
{scrollOffset > 0 && <Text color={Colors.Foreground}></Text>}
@@ -88,31 +74,33 @@ export function SuggestionsDisplay({
8874
return (
8975
<Box key={`${suggestion.value}-${originalIndex}`} width={width}>
9076
<Box flexDirection="row">
91-
{isSlashCommandMode ? (
92-
<>
93-
<Box width={commandNameWidth} flexShrink={0}>
94-
{labelElement}
95-
</Box>
96-
{suggestion.description ? (
97-
<Box flexGrow={1} marginLeft={1}>
98-
<Text color={textColor} wrap="wrap">
99-
{suggestion.description}
100-
</Text>
101-
</Box>
102-
) : null}
103-
</>
104-
) : (
105-
<>
106-
{labelElement}
107-
{suggestion.description ? (
108-
<Box flexGrow={1} marginLeft={1}>
109-
<Text color={textColor} wrap="wrap">
110-
{suggestion.description}
111-
</Text>
112-
</Box>
113-
) : null}
114-
</>
115-
)}
77+
{(() => {
78+
const isSlashCommand = userInput.startsWith('/');
79+
return (
80+
<>
81+
{isSlashCommand ? (
82+
<Box flexShrink={0} paddingRight={2}>
83+
{labelElement}
84+
{suggestion.commandKind === CommandKind.MCP_PROMPT && (
85+
<Text color={Colors.Gray}> [MCP]</Text>
86+
)}
87+
</Box>
88+
) : (
89+
labelElement
90+
)}
91+
{suggestion.description && (
92+
<Box
93+
flexGrow={1}
94+
paddingLeft={isSlashCommand ? undefined : 1}
95+
>
96+
<Text color={textColor} wrap="truncate">
97+
{suggestion.description}
98+
</Text>
99+
</Box>
100+
)}
101+
</>
102+
);
103+
})()}
116104
</Box>
117105
</Box>
118106
);

0 commit comments

Comments
 (0)