From ce46e75dec0d125b55b9f9ff923a342fc1bb4c96 Mon Sep 17 00:00:00 2001 From: Coffee Date: Mon, 16 Mar 2026 17:46:44 +0800 Subject: [PATCH] Fix chat_api companion config issues --- src/app.rs | 8 ++++++- src/cli/mcp.rs | 11 +++++++++ src/extensions/manager.rs | 48 +++++++++++++++++++++++++++++++++++---- src/tools/mcp/config.rs | 14 ++++++++++-- 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/app.rs b/src/app.rs index ad4f27a0..3c7db63f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -481,7 +481,13 @@ impl AppBuilder { match servers_result { Ok(mut servers) => { if let Some(companion) = companion_mcp_server { - servers.upsert(companion); + let companion_name = companion.name.clone(); + if !servers.insert_if_absent(companion) { + tracing::debug!( + "Skipping derived MCP companion '{}': an existing config with that name is already present", + companion_name + ); + } } let enabled: Vec<_> = servers.enabled_servers().cloned().collect(); if !enabled.is_empty() { diff --git a/src/cli/mcp.rs b/src/cli/mcp.rs index dddfbae1..cc77e7ba 100644 --- a/src/cli/mcp.rs +++ b/src/cli/mcp.rs @@ -410,6 +410,17 @@ async fn auth_server(name: String, user_id: String) -> anyhow::Result<()> { .cloned() .ok_or_else(|| anyhow::anyhow!("Server '{}' not found", name))?; + if server.uses_runtime_auth_source() { + println!(); + println!( + " Server '{}' reuses your active NEAR AI authentication and does not support separate MCP OAuth.", + name + ); + println!(" Configure NEAR AI auth (API key or session login) instead."); + println!(); + return Ok(()); + } + // Initialize secrets store let secrets = get_secrets_store().await?; diff --git a/src/extensions/manager.rs b/src/extensions/manager.rs index 163e3a5e..d519aa5f 100644 --- a/src/extensions/manager.rs +++ b/src/extensions/manager.rs @@ -614,8 +614,11 @@ impl ExtensionManager { match self.load_mcp_servers().await { Ok(servers) => { for server in &servers.servers { - let authenticated = - is_authenticated(server, &self.secrets, &self.user_id).await; + let authenticated = if server.uses_runtime_auth_source() { + self.is_runtime_authenticated(server).await + } else { + is_authenticated(server, &self.secrets, &self.user_id).await + }; let clients = self.mcp_clients.read().await; let active = clients.contains_key(&server.name); @@ -646,7 +649,7 @@ impl ExtensionManager { active, tools, needs_setup: false, - has_auth: false, + has_auth: server.requires_auth(), installed: true, activation_error: None, version: None, @@ -1292,12 +1295,35 @@ impl ExtensionManager { }; if let Some(ref companion) = self.companion_mcp_server { - servers.upsert(companion.clone()); + servers.insert_if_absent(companion.clone()); } Ok(servers) } + async fn is_runtime_authenticated(&self, server: &McpServerConfig) -> bool { + match server.auth_source { + Some(crate::tools::mcp::config::McpAuthSource::NearAi) => { + if self.nearai_api_key.is_some() { + return true; + } + + if let Ok(key) = std::env::var("NEARAI_API_KEY") + && !key.trim().is_empty() + { + return true; + } + + if let Some(ref session) = self.nearai_session_manager { + return session.has_token().await; + } + + false + } + None => false, + } + } + async fn get_mcp_server( &self, name: &str, @@ -1850,6 +1876,20 @@ impl ExtensionManager { .await .map_err(|e| ExtensionError::NotInstalled(e.to_string()))?; + if server.uses_runtime_auth_source() { + if self.is_runtime_authenticated(&server).await { + return Ok(AuthResult::authenticated(name, ExtensionKind::McpServer)); + } + + return Ok(AuthResult::needs_setup( + name, + ExtensionKind::McpServer, + "This MCP server reuses your active NEAR AI authentication. Configure a NEAR AI API key or sign in to NEAR AI first, then try again." + .to_string(), + None, + )); + } + // Check if already authenticated if is_authenticated(&server, &self.secrets, &self.user_id).await { return Ok(AuthResult::authenticated(name, ExtensionKind::McpServer)); diff --git a/src/tools/mcp/config.rs b/src/tools/mcp/config.rs index ad392eed..7a6980ae 100644 --- a/src/tools/mcp/config.rs +++ b/src/tools/mcp/config.rs @@ -289,8 +289,8 @@ impl McpServerConfig { } } -/// Default name used for the companion chat-api MCP server derived from NEAR AI config. -pub const NEARAI_COMPANION_MCP_NAME: &str = "chat_api"; +/// Reserved name used for the companion chat-api MCP server derived from NEAR AI config. +pub const NEARAI_COMPANION_MCP_NAME: &str = "_nearai_companion_mcp"; /// Build the companion chat-api MCP server from the active NearAI config. /// @@ -414,6 +414,16 @@ impl McpServersFile { } } + /// Insert a server only if no server with the same name already exists. + pub fn insert_if_absent(&mut self, config: McpServerConfig) -> bool { + if self.get(&config.name).is_some() { + false + } else { + self.servers.push(config); + true + } + } + /// Remove a server by name. pub fn remove(&mut self, name: &str) -> bool { let len_before = self.servers.len();