Skip to content

Commit e47aa56

Browse files
echobtfactorydroid
andauthored
refactor(mcp): replace modal popup with inline card UI and remove emojis (#233)
- Remove all emojis from MCP panel interactive items (Add, View Tools, Reload, server statuses) - Replace modal popup for 'Add MCP Server' with inline card-based UI - Add McpCard::new_add_mode() to open MCP card directly in add server mode - Add CardHandler::open_mcp_add_mode() method for opening add mode - Modal popups are now reserved only for command palette This improves UX by keeping the add server flow within the card area that replaces the input, avoiding disruptive popup modals. Co-authored-by: Droid Agent <droid@factory.ai>
1 parent c00f4ce commit e47aa56

File tree

4 files changed

+77
-47
lines changed

4 files changed

+77
-47
lines changed

cortex-tui/src/cards/mcp.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,29 @@ impl McpCard {
226226
}
227227
}
228228

229+
/// Creates a new MCP card directly in AddServer mode.
230+
pub fn new_add_mode(servers: Vec<McpServerInfo>) -> Self {
231+
let items: Vec<SelectionItem> = servers
232+
.iter()
233+
.map(|server| {
234+
let status_text = server.format_status_description();
235+
SelectionItem::new(&server.name)
236+
.with_description(status_text)
237+
.with_current(server.status == McpStatus::Running)
238+
})
239+
.collect();
240+
241+
let list = SelectionList::new(items).with_max_visible(10);
242+
243+
Self {
244+
servers,
245+
list,
246+
mode: McpCardMode::AddServer {
247+
name: String::new(),
248+
},
249+
}
250+
}
251+
229252
/// Gets the currently selected server name.
230253
fn selected_server_name(&self) -> Option<&str> {
231254
self.list

cortex-tui/src/interactive/builders/mcp.rs

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,18 @@ pub fn build_mcp_selector(servers: &[McpServerInfo]) -> InteractiveState {
1010
// Add global actions first (separated from servers)
1111
items.push(
1212
InteractiveItem::new("__add__", "Add MCP Server")
13-
.with_icon('+')
1413
.with_description("Configure a new server (stdio, HTTP, or from registry)")
1514
.with_shortcut('a'),
1615
);
1716

1817
items.push(
1918
InteractiveItem::new("__tools__", "View All Tools")
20-
.with_icon('T')
2119
.with_description("List tools from all running servers")
2220
.with_shortcut('t'),
2321
);
2422

2523
items.push(
2624
InteractiveItem::new("__reload__", "Reload All Servers")
27-
.with_icon('R')
2825
.with_description("Restart all MCP servers")
2926
.with_shortcut('r'),
3027
);
@@ -38,18 +35,17 @@ pub fn build_mcp_selector(servers: &[McpServerInfo]) -> InteractiveState {
3835

3936
// Add server entries
4037
for server in servers {
41-
let (icon, status_text) = match server.status {
42-
McpStatus::Running => ('*', "running"),
43-
McpStatus::Starting => ('~', "starting"),
44-
McpStatus::Stopped => ('-', "stopped"),
45-
McpStatus::Error => ('!', "error"),
38+
let status_text = match server.status {
39+
McpStatus::Running => "running",
40+
McpStatus::Starting => "starting",
41+
McpStatus::Stopped => "stopped",
42+
McpStatus::Error => "error",
4643
};
4744

4845
let description = format!("{} - {} tools", status_text, server.tool_count);
4946

50-
let mut item = InteractiveItem::new(&server.name, &server.name)
51-
.with_icon(icon)
52-
.with_description(description);
47+
let mut item =
48+
InteractiveItem::new(&server.name, &server.name).with_description(description);
5349

5450
if server.requires_auth {
5551
item = item.with_metadata("requires_auth".to_string());
@@ -84,43 +80,22 @@ pub fn build_mcp_server_actions(server: &McpServerInfo) -> InteractiveState {
8480

8581
match server.status {
8682
McpStatus::Running | McpStatus::Starting => {
87-
items.push(
88-
InteractiveItem::new("stop", "Stop Server")
89-
.with_icon('x')
90-
.with_shortcut('s'),
91-
);
92-
items.push(
93-
InteractiveItem::new("restart", "Restart Server")
94-
.with_icon('R')
95-
.with_shortcut('r'),
96-
);
83+
items.push(InteractiveItem::new("stop", "Stop Server").with_shortcut('s'));
84+
items.push(InteractiveItem::new("restart", "Restart Server").with_shortcut('r'));
9785
}
9886
McpStatus::Stopped | McpStatus::Error => {
99-
items.push(
100-
InteractiveItem::new("start", "Start Server")
101-
.with_icon('>')
102-
.with_shortcut('s'),
103-
);
87+
items.push(InteractiveItem::new("start", "Start Server").with_shortcut('s'));
10488
}
10589
}
10690

10791
if server.requires_auth {
108-
items.push(
109-
InteractiveItem::new("auth", "Configure Authentication")
110-
.with_icon('K')
111-
.with_shortcut('a'),
112-
);
92+
items.push(InteractiveItem::new("auth", "Configure Authentication").with_shortcut('a'));
11393
}
11494

115-
items.push(
116-
InteractiveItem::new("logs", "View Logs")
117-
.with_icon('L')
118-
.with_shortcut('l'),
119-
);
95+
items.push(InteractiveItem::new("logs", "View Logs").with_shortcut('l'));
12096

12197
items.push(
12298
InteractiveItem::new("remove", "Remove Server")
123-
.with_icon('X')
12499
.with_shortcut('d')
125100
.with_description("Remove this server from configuration"),
126101
);

cortex-tui/src/runner/card_handler.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,23 @@ impl CardHandler {
162162
self.card_stack.push(Box::new(card));
163163
}
164164

165+
/// Opens the MCP servers card directly in Add Server mode.
166+
///
167+
/// # Arguments
168+
///
169+
/// * `servers` - List of MCP servers (for reference)
170+
///
171+
/// # Example
172+
///
173+
/// ```rust,ignore
174+
/// let servers = vec![McpServerInfo::new("filesystem").with_status(McpStatus::Running)];
175+
/// handler.open_mcp_add_mode(servers);
176+
/// ```
177+
pub fn open_mcp_add_mode(&mut self, servers: Vec<McpServerInfo>) {
178+
let card = McpCard::new_add_mode(servers);
179+
self.card_stack.push(Box::new(card));
180+
}
181+
165182
/// Opens the help card.
166183
///
167184
/// # Example

cortex-tui/src/runner/event_loop.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6137,16 +6137,31 @@ impl EventLoop {
61376137
// Handle MCP server selection
61386138
match item_id.as_str() {
61396139
"__add__" => {
6140-
// Open the MCP Manager modal for adding a server interactively
6141-
use crate::modal::McpManagerModal;
6142-
let servers = self.app_state.mcp_servers.clone();
6143-
let mut modal = McpManagerModal::new(servers);
6144-
// Trigger add mode by sending 'a' key
6145-
modal.handle_key(crossterm::event::KeyEvent::new(
6146-
crossterm::event::KeyCode::Char('a'),
6147-
crossterm::event::KeyModifiers::empty(),
6148-
));
6149-
self.modal_stack.push(Box::new(modal));
6140+
// Open the MCP Card in Add Server mode (inline card, not modal popup)
6141+
use crate::cards::mcp::{McpServerInfo as CardMcpServerInfo, McpStatus};
6142+
let servers: Vec<CardMcpServerInfo> = self
6143+
.app_state
6144+
.mcp_servers
6145+
.iter()
6146+
.map(|s| {
6147+
let status = match s.status {
6148+
crate::modal::mcp_manager::McpStatus::Running => {
6149+
McpStatus::Running
6150+
}
6151+
crate::modal::mcp_manager::McpStatus::Starting => {
6152+
McpStatus::Starting
6153+
}
6154+
crate::modal::mcp_manager::McpStatus::Stopped => {
6155+
McpStatus::Stopped
6156+
}
6157+
crate::modal::mcp_manager::McpStatus::Error => McpStatus::Error,
6158+
};
6159+
CardMcpServerInfo::new(&s.name)
6160+
.with_status(status)
6161+
.with_tool_count(s.tool_count)
6162+
})
6163+
.collect();
6164+
self.card_handler.open_mcp_add_mode(servers);
61506165
return false;
61516166
}
61526167
"__tools__" => {

0 commit comments

Comments
 (0)