mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
fix(tui): implement /mcp inventory for tui_app_server (#14931)
## Problem
The `/mcp` command did not work in the app-server TUI (remote mode). On
`main`, `add_mcp_output()` called `McpManager::effective_servers()`
in-process, which only sees locally configured servers, and then emitted
a generic stub message for the app-server to handle. In remote usage,
that left `/mcp` without a real inventory view.
## Solution
Implement `/mcp` for the app-server TUI by fetching MCP server inventory
directly from the app-server via the paginated `mcpServerStatus/list`
RPC and rendering the results into chat history.
The command now follows a three-phase lifecycle:
1. Loading: `ChatWidget::add_mcp_output()` inserts a transient
`McpInventoryLoadingCell` and emits `AppEvent::FetchMcpInventory`. This
gives immediate feedback that the command registered.
2. Fetch: `App::fetch_mcp_inventory()` spawns a background task that
calls `fetch_all_mcp_server_statuses()` over an app-server request
handle. When the RPC completes, it sends `AppEvent::McpInventoryLoaded {
result }`.
3. Resolve: `App::handle_mcp_inventory_result()` clears the loading cell
and renders either `new_mcp_tools_output_from_statuses(...)` or an error
message.
This keeps the main app event loop responsive, so the TUI can repaint
before the remote RPC finishes.
## Notes
- No `app-server` changes were required.
- The rendered inventory includes auth, tools, resources, and resource
templates, plus transport details when they are available from local
config for display enrichment.
- The app-server RPC does not expose authoritative `enabled` or
`disabled_reason` state for MCP servers, so the remote `/mcp` view no
longer renders a `Status:` row rather than guessing from local config.
- RPC failures surface in history as `Failed to load MCP inventory:
...`.
## Tests
- `slash_mcp_requests_inventory_via_app_server`
- `mcp_inventory_maps_prefix_tool_names_by_server`
- `handle_mcp_inventory_result_clears_committed_loading_cell`
- `mcp_tools_output_from_statuses_renders_status_only_servers`
- `mcp_inventory_loading_snapshot`
This commit is contained in:
@@ -67,7 +67,6 @@ use codex_core::find_thread_name_by_id;
|
||||
use codex_core::git_info::current_branch_name;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_core::git_info::local_git_branches;
|
||||
use codex_core::mcp::McpManager;
|
||||
use codex_core::plugins::PluginsManager;
|
||||
use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
@@ -8243,18 +8242,39 @@ impl ChatWidget {
|
||||
PlainHistoryCell::new(vec![line.into()])
|
||||
}
|
||||
|
||||
/// Begin the asynchronous MCP inventory flow: show a loading spinner and
|
||||
/// request the app-server fetch via `AppEvent::FetchMcpInventory`.
|
||||
///
|
||||
/// The spinner lives in `active_cell` and is cleared by
|
||||
/// [`clear_mcp_inventory_loading`] once the result arrives.
|
||||
pub(crate) fn add_mcp_output(&mut self) {
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
self.config.codex_home.clone(),
|
||||
self.flush_answer_stream_with_separator();
|
||||
self.flush_active_cell();
|
||||
self.active_cell = Some(Box::new(history_cell::new_mcp_inventory_loading(
|
||||
self.config.animations,
|
||||
)));
|
||||
if mcp_manager
|
||||
.effective_servers(&self.config, /*auth*/ None)
|
||||
.is_empty()
|
||||
self.bump_active_cell_revision();
|
||||
self.request_redraw();
|
||||
self.app_event_tx.send(AppEvent::FetchMcpInventory);
|
||||
}
|
||||
|
||||
/// Remove the MCP loading spinner if it is still the active cell.
|
||||
///
|
||||
/// Uses `Any`-based type checking so that a late-arriving inventory result
|
||||
/// does not accidentally clear an unrelated cell that was set in the meantime.
|
||||
pub(crate) fn clear_mcp_inventory_loading(&mut self) {
|
||||
let Some(active) = self.active_cell.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if !active
|
||||
.as_any()
|
||||
.is::<history_cell::McpInventoryLoadingCell>()
|
||||
{
|
||||
self.add_to_history(history_cell::empty_mcp_output());
|
||||
} else {
|
||||
self.add_app_server_stub_message("MCP tool inventory");
|
||||
return;
|
||||
}
|
||||
self.active_cell = None;
|
||||
self.bump_active_cell_revision();
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn add_connectors_output(&mut self) {
|
||||
|
||||
Reference in New Issue
Block a user