Add auth mode, fix MCP token handling, and parallelize startup loading

Auth mode: when a tool requires an API key, the thread enters a special
mode where the next user message is routed directly to the credential
store, bypassing logs, turns, history, and compaction entirely. This
prevents tokens from leaking into debug output or persistent storage.

Fix MCP auth: auth_mcp now actually uses the token parameter (was
ignored as _token) and falls back to manual token entry when OAuth
and DCR are both unsupported.

Parallel loading: WASM tools, WASM channels, and MCP servers now load
concurrently at startup. Within each loader, individual items also
load in parallel (join_all for WASM, JoinSet for MCP servers).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-06 13:35:17 -08:00
co-authored by Claude Opus 4.6
parent 9b729795fb
commit 2cdd04a359
7 changed files with 533 additions and 142 deletions
+63 -5
View File
@@ -460,12 +460,35 @@ impl ExtensionManager {
async fn auth_mcp(
&self,
name: &str,
_token: Option<&str>,
token: Option<&str>,
) -> Result<AuthResult, ExtensionError> {
let server = get_mcp_server(name)
.await
.map_err(|e| ExtensionError::NotInstalled(e.to_string()))?;
// If a token was provided directly, store it and we're done.
if let Some(token_value) = token {
let secret_name = server.token_secret_name();
let params =
CreateSecretParams::new(&secret_name, token_value).with_provider(name.to_string());
self.secrets
.create(&self.user_id, params)
.await
.map_err(|e| ExtensionError::AuthFailed(e.to_string()))?;
tracing::info!("MCP server '{}' authenticated via manual token", name);
return Ok(AuthResult {
name: name.to_string(),
kind: ExtensionKind::McpServer,
auth_url: None,
callback_type: None,
instructions: None,
setup_url: None,
awaiting_token: false,
status: "authenticated".to_string(),
});
}
// Check if already authenticated
if is_authenticated(&server, &self.secrets, &self.user_id).await {
return Ok(AuthResult {
@@ -483,7 +506,7 @@ impl ExtensionManager {
// Run the full OAuth flow (opens browser, waits for callback)
match authorize_mcp_server(&server, &self.secrets, &self.user_id).await {
Ok(_token) => {
tracing::info!("MCP server '{}' authenticated successfully", name);
tracing::info!("MCP server '{}' authenticated via OAuth", name);
Ok(AuthResult {
name: name.to_string(),
kind: ExtensionKind::McpServer,
@@ -496,10 +519,45 @@ impl ExtensionManager {
})
}
Err(crate::tools::mcp::auth::AuthError::NotSupported) => {
// Server doesn't support OAuth at all, try to build a non-interactive auth URL
self.auth_mcp_build_url(name, &server).await
// Server doesn't support OAuth, try building a URL first
match self.auth_mcp_build_url(name, &server).await {
Ok(result) => Ok(result),
Err(_) => {
// No OAuth, no DCR: fall back to manual token entry
Ok(AuthResult {
name: name.to_string(),
kind: ExtensionKind::McpServer,
auth_url: None,
callback_type: None,
instructions: Some(format!(
"Server '{}' does not support OAuth. \
Please provide an API token/key for this server.",
name
)),
setup_url: None,
awaiting_token: true,
status: "awaiting_token".to_string(),
})
}
}
}
Err(e) => {
// OAuth failed for some other reason, fall back to manual token
Ok(AuthResult {
name: name.to_string(),
kind: ExtensionKind::McpServer,
auth_url: None,
callback_type: None,
instructions: Some(format!(
"OAuth failed for '{}': {}. \
Please provide an API token/key manually.",
name, e
)),
setup_url: None,
awaiting_token: true,
status: "awaiting_token".to_string(),
})
}
Err(e) => Err(ExtensionError::AuthFailed(e.to_string())),
}
}