rho-coding-agent 1.32.0

A lightweight agent harness inspired by Pi
Documentation
//! `/mcp` inventory picker.

use crate::tools::mcp::{McpServerReport, McpServerStatus, McpSessionReport};

use super::{
    picker_overlay::OverlayChrome, PickerAction, PickerBadge, PickerBadgeTone, PickerItem,
    PickerLayout, UiPicker,
};

pub(super) struct McpPickerContext<'a> {
    pub(super) report: &'a McpSessionReport,
    pub(super) config_path: &'a std::path::Path,
}

pub(super) fn picker(context: McpPickerContext<'_>) -> UiPicker {
    let mut items = Vec::with_capacity(context.report.servers.len().saturating_add(1));
    items.push(mode_item(context.report, context.config_path));
    items.extend(context.report.servers.iter().map(server_item));

    UiPicker::new("MCP servers", items, PickerAction::Dismiss)
        .with_layout(PickerLayout::Overlay)
        .with_badge_placement(super::PickerBadgePlacement::Detail)
        .with_overlay_chrome(OverlayChrome {
            nav_label: " SERVERS".into(),
            detail_label: Some(" DETAILS".into()),
            nav_keys_hint: "↑↓ servers".into(),
        })
        .with_confirm_verb("close")
}

fn mode_item(report: &McpSessionReport, config_path: &std::path::Path) -> PickerItem {
    use crate::tools::mcp::McpLoadMode;

    let config = crate::paths::display(config_path);
    let summary = report.summary();
    let (status, healthy, detail) = if !summary.configured {
        (
            "not configured".into(),
            true,
            format!("No MCP servers in {config}. Add entries under [mcp.servers]."),
        )
    } else {
        match summary.mode {
            McpLoadMode::Native => (
                format!("{} connected", summary.connected),
                summary.problems == 0,
                format!(
                    "{} enabled, {} exported tool{}, {} problem{}. Config: {config}.",
                    summary.enabled,
                    summary.exported_tools,
                    super::plural_suffix(summary.exported_tools),
                    summary.problems,
                    super::plural_suffix(summary.problems),
                ),
            ),
            McpLoadMode::UnsupportedAgent => (
                "unsupported agent".into(),
                summary.enabled == 0 && summary.problems == 0,
                format!(
                    "Native MCP loads only for Rho agents. The active agent does not host MCP tools. Config: {config}."
                ),
            ),
            McpLoadMode::ToolsDisabled => (
                "tools disabled".into(),
                true,
                format!(
                    "This session started with tools disabled, so MCP was not connected. Config: {config}."
                ),
            ),
        }
    };
    PickerItem {
        section: Some("STATUS".into()),
        label: "Session".into(),
        detail: Some(detail),
        preview: None,
        badge: Some(PickerBadge {
            text: status,
            tone: if healthy {
                PickerBadgeTone::Healthy
            } else {
                PickerBadgeTone::Warning
            },
        }),
        value: "session".into(),
        selection_verb: None,
    }
}

fn server_item(server: &McpServerReport) -> PickerItem {
    PickerItem {
        section: Some("SERVERS".into()),
        label: server.identity.clone(),
        detail: Some(server.detail_text()),
        preview: None,
        badge: Some(PickerBadge {
            text: server.status().as_str().into(),
            tone: if server.status().is_healthy() {
                if server.status() == McpServerStatus::Connected {
                    PickerBadgeTone::Healthy
                } else {
                    PickerBadgeTone::Internal
                }
            } else {
                PickerBadgeTone::Warning
            },
        }),
        value: server.identity.clone(),
        selection_verb: None,
    }
}