Files
Kigi-CLI/crates/codegen/kigi-shell/src/session/mcp_servers.rs
T
ZacharyZhang-NY 5e4e24db99 M2 audit: excise managed connectors and xAI media-gen tools
Managed connectors (grok.com MCP admin) removed root-and-branch:
- The managed-MCP fetch/injection pipeline is gone, including the whole
  kigi-shell-session-support crate (managed-config fetch client, gateway
  tool catalog + dispatch, header injection, refresh task), reactive
  managed re-auth, mcp_doctor's grok.com-source discovery, and the
  [managed_mcps] config surface.
- TUI: the 'Managed by grok.com' section, connectors URL/deep-link,
  Action::OpenManagedConnectors, and session_team_id are gone. Local MCP
  management (list/toggle/add/remove/auth/tools) is fully intact.
- Kept as LOCAL policy: managed-settings.json MCP allow/deny enforcement,
  the multi-source local MCP merge, folder-trust gating. PluginOrigin
  Project/User labels kept (they tag locally discovered plugin dirs).

imagine/media-gen tools (xAI image/video generation) removed:
- image_gen, image_edit, video_gen, image_to_video, reference_to_video
  implementations, registrations, ToolKind/ToolInput/Output variants
  (serde-safe), config plumbing end to end, ZDR video machinery,
  /imagine + /imagine-video commands and guidance text, the bundled
  imagine skill (added to legacy cleanup so user installs delete it),
  and the media-gen render path.
- Kept: image INPUT (paste/attach, [Image #N] meta, pdf/image fetch,
  clipboard wrap), generic media-ref rendering, and the generic tool
  401-retry machinery (tests renamed, assertions unweakened).
- deploy_app stays: it is a permanently-disabled local stub deploying
  nowhere.

121 files changed, 8 deleted. Gates: workspace check/clippy 0/0, fmt,
deny ok; suites green (tools 2554, shell 4862, tui 6608, workspace
1042). Remaining grok.com strings live only in the auth-method ids and
changelog archives (§9/M3 sweep).
2026-07-17 23:45:05 -04:00

152 lines
5.6 KiB
Rust

//! MCP server re-exports + shell-side wrappers for timeout override resolution.
pub use kigi_mcp::servers::{
AcpServerEntry, HttpConfig, MCP_TOOL_NAME_DELIMITER, McpClient, McpClientTimeoutOverrides,
McpConfigDiff, McpError, McpInitStrategy, McpMetaConfigMap, McpServerMetaConfig, McpServerName,
McpService, McpState, McpTool, McpToolRegistration, OauthInteractivity, SharedMcpPool,
mcp_server_name, mcp_target_str, mcp_transport_str, parse_mcp_meta_config, parse_mcp_tool_name,
sanitize_descriptor_segment, validate_tool_name,
};
use std::collections::HashMap;
use std::path::Path;
use agent_client_protocol as acp;
use kigi_mcp::oauth_config::{McpOAuthConfig, McpOAuthConfigMap};
use kigi_mcp::servers as inner;
fn resolve_overrides(
server_name: &str,
cwd: Option<&Path>,
) -> Option<inner::McpClientTimeoutOverrides> {
let config = match cwd {
Some(cwd) => crate::util::config::get_mcp_server_config_with_project(server_name, cwd),
None => crate::util::config::get_mcp_server_config(server_name),
};
// Fall back to the globally-resolved startup timeout so servers without a
// per-server `startup_timeout_sec` (e.g. `~/.claude.json` imports) still get it.
let global_startup = crate::util::config::resolved_mcp_startup_timeout_secs();
Some(inner::McpClientTimeoutOverrides {
startup_timeout_sec: config
.as_ref()
.and_then(|c| c.startup_timeout_sec)
.or(Some(global_startup)),
tool_timeout_sec: config.as_ref().and_then(|c| c.tool_timeout_sec),
tool_timeouts: config.as_ref().and_then(|c| c.tool_timeouts.clone()),
expose_image_base64: config.as_ref().and_then(|c| c.expose_image_base64),
})
}
/// Build the config-resolved event data from a list of MCP server configs.
pub fn build_config_resolved_event(
configs: &[acp::McpServer],
cwd: &Path,
) -> kigi_file_utils::events::Event {
let disabled: Vec<String> = crate::util::config::disabled_mcp_server_names(cwd)
.into_iter()
.collect();
let servers = configs
.iter()
.map(|c| kigi_file_utils::events::McpConfigServer {
name: inner::mcp_server_name(c).to_string(),
transport: inner::mcp_transport_str(c).to_string(),
source: "local".to_string(),
})
.collect();
kigi_file_utils::events::Event::McpConfigResolved { servers, disabled }
}
pub async fn start_mcp_server(
mcp_server: acp::McpServer,
session_id: Option<&str>,
cwd: Option<&Path>,
meta_config: Option<&inner::McpServerMetaConfig>,
byo_config: Option<&McpOAuthConfig>,
event_writer: &kigi_file_utils::events::EventWriter,
mode: OauthInteractivity,
) -> Result<inner::McpClient, inner::McpError> {
let overrides = resolve_overrides(inner::mcp_server_name(&mcp_server), cwd);
inner::start_mcp_server(
mcp_server,
session_id,
overrides.as_ref(),
meta_config,
byo_config,
event_writer,
mode,
)
.await
}
/// Build all pending MCP clients for one init pass as a single merged list: config-declared
/// servers (HTTP/stdio, spawned lock-free via [`start_mcp_servers`]) and SDK in-process
/// servers (built under a brief lock via `McpState::build_pending_acp_clients`). SDK clients
/// never fail to build, so they enter as `Ok`. One entry point so the init batch doesn't
/// invoke two builders.
pub async fn build_pending_clients(
mcp_state: &tokio::sync::Mutex<inner::McpState>,
configs_to_start: Vec<acp::McpServer>,
session_id: Option<&str>,
cwd: Option<&Path>,
meta_config_map: &inner::McpMetaConfigMap,
oauth_config_map: &McpOAuthConfigMap,
event_writer: &kigi_file_utils::events::EventWriter,
mode: OauthInteractivity,
) -> Vec<Result<inner::McpClient, inner::McpError>> {
let mut results = start_mcp_servers(
configs_to_start,
session_id,
cwd,
meta_config_map,
oauth_config_map,
event_writer,
mode,
)
.await;
// Re-resolve SDK (ACP) config.toml overrides for THIS init, matching HTTP/stdio, so a
// mid-session config change applies on the next init (resolved outside the lock — it
// reads config.toml — then handed to the pure, under-lock builder).
let acp_overrides: HashMap<String, inner::McpClientTimeoutOverrides> = {
let names = mcp_state.lock().await.pending_acp_server_names();
names
.iter()
.filter_map(|name| resolve_overrides(name, cwd).map(|o| (name.clone(), o)))
.collect()
};
// Brief lock, no `.await` held: the SDK clients are built synchronously (pure).
let acp_clients = mcp_state
.lock()
.await
.build_pending_acp_clients(&acp_overrides);
results.extend(acp_clients.into_iter().map(Ok));
results
}
pub async fn start_mcp_servers(
mcp_servers: Vec<acp::McpServer>,
session_id: Option<&str>,
cwd: Option<&Path>,
meta_config_map: &inner::McpMetaConfigMap,
oauth_config_map: &McpOAuthConfigMap,
event_writer: &kigi_file_utils::events::EventWriter,
mode: OauthInteractivity,
) -> Vec<Result<inner::McpClient, inner::McpError>> {
let overrides_map: HashMap<String, inner::McpClientTimeoutOverrides> = mcp_servers
.iter()
.filter_map(|s| {
let name = inner::mcp_server_name(s);
resolve_overrides(name, cwd).map(|o| (name.to_string(), o))
})
.collect();
inner::start_mcp_servers(
mcp_servers,
session_id,
&overrides_map,
meta_config_map,
oauth_config_map,
event_writer,
mode,
)
.await
}