mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 15:40:18 +00:00
Restrict MCP runtime auth to the NEAR AI companion
This commit is contained in:
+2
-2
@@ -11,7 +11,6 @@ use clap::{Args, Subcommand};
|
||||
use crate::config::{Config, LlmConfig};
|
||||
use crate::db::Database;
|
||||
use crate::secrets::SecretsStore;
|
||||
use crate::settings::Settings;
|
||||
use crate::tools::mcp::{
|
||||
McpClient, McpProcessManager, McpServerConfig, McpSessionManager, OAuthConfig,
|
||||
auth::{authorize_mcp_server, is_authenticated},
|
||||
@@ -728,7 +727,8 @@ async fn get_secrets_store() -> anyhow::Result<Arc<dyn SecretsStore + Send + Syn
|
||||
}
|
||||
|
||||
fn resolve_llm_from_env() -> Result<LlmConfig, crate::error::ConfigError> {
|
||||
LlmConfig::resolve(&Settings::default())
|
||||
let settings = crate::config::load_bootstrap_settings(None)?;
|
||||
LlmConfig::resolve(&settings)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+40
-1
@@ -55,7 +55,10 @@ pub struct McpServerConfig {
|
||||
///
|
||||
/// This is used for companion MCP servers that should reuse an existing
|
||||
/// provider identity instead of running their own MCP OAuth flow.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
///
|
||||
/// Security: this field is runtime-only. Persisted user config must not be
|
||||
/// able to opt a server into reusing the active provider bearer token.
|
||||
#[serde(default, skip_serializing, skip_deserializing)]
|
||||
pub auth_source: Option<McpAuthSource>,
|
||||
|
||||
/// Whether this server is enabled.
|
||||
@@ -178,6 +181,15 @@ impl McpServerConfig {
|
||||
});
|
||||
}
|
||||
|
||||
if self.uses_runtime_auth_source() && !is_nearai_companion_server_name(&self.name) {
|
||||
return Err(ConfigError::InvalidConfig {
|
||||
reason: format!(
|
||||
"Runtime auth source is only allowed for reserved server '{}'",
|
||||
NEARAI_COMPANION_MCP_NAME
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
match self.effective_transport() {
|
||||
EffectiveTransport::Http => {
|
||||
if self.url.is_empty() {
|
||||
@@ -872,6 +884,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_ignores_persisted_auth_source() {
|
||||
let raw = serde_json::json!({
|
||||
"name": "user-managed",
|
||||
"url": "https://mcp.example.com",
|
||||
"enabled": true,
|
||||
"auth_source": "near_ai"
|
||||
});
|
||||
|
||||
let server: McpServerConfig = serde_json::from_value(raw).expect("server");
|
||||
assert_eq!(server.auth_source, None);
|
||||
}
|
||||
|
||||
#[cfg(feature = "libsql")]
|
||||
#[test]
|
||||
fn test_derive_nearai_companion_mcp_server_strips_trailing_v1() {
|
||||
@@ -934,6 +959,20 @@ mod tests {
|
||||
assert!(config.requires_auth());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_rejects_runtime_auth_on_user_managed_server() {
|
||||
let config = McpServerConfig::new("user-managed", "https://mcp.example.com")
|
||||
.with_auth_source(McpAuthSource::NearAi);
|
||||
|
||||
let err = config
|
||||
.validate()
|
||||
.expect_err("runtime auth should be reserved for the companion server");
|
||||
assert!(
|
||||
err.to_string().contains(NEARAI_COMPANION_MCP_NAME),
|
||||
"expected reserved-name validation message, got: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_requires_auth_remote_https_without_oauth() {
|
||||
// Remote HTTPS servers need auth even without pre-configured OAuth (DCR)
|
||||
|
||||
Reference in New Issue
Block a user