From 2ce0cc398ce427120dc3f80ac0766470ecbed981 Mon Sep 17 00:00:00 2001 From: jaaacki Date: Thu, 6 Aug 2026 23:40:06 +0800 Subject: [PATCH] fix(mcp): honour deferred flag from transport_config Every MCP server was constructed with deferred: Some(false), so no tool was ever marked deferred. ToolSearch filters tool_defs on .filter(|d| d.deferred), so it always matched an empty set and returned "No deferred tools matching" regardless of query, tool count, or how the server was installed. aionrs supports the flag per server (McpServerConfig::deferred, defaulting to true when omitted) and registers ToolSearchTool unconditionally, so the feature was reachable in the engine but never enabled by the host. Read the flag from the same transport_config JSON that command/args/env/url/ headers already come from. An absent key still yields false, so existing behaviour is unchanged; an explicit true opts a server in. Scoped to row_to_mcp_server_config (DB-configured servers). Session-scoped and internal team servers are left as-is. Refs #793 --- crates/aionui-ai-agent/src/factory/aionrs.rs | 39 ++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/crates/aionui-ai-agent/src/factory/aionrs.rs b/crates/aionui-ai-agent/src/factory/aionrs.rs index da9fba085..8ac62efb6 100644 --- a/crates/aionui-ai-agent/src/factory/aionrs.rs +++ b/crates/aionui-ai-agent/src/factory/aionrs.rs @@ -501,6 +501,11 @@ async fn row_to_mcp_server_config( let value: serde_json::Value = serde_json::from_str(&row.transport_config).map_err(|e| format!("invalid transport_config JSON: {e}"))?; + // Whether this server's tools are sent to the LLM as name-only stubs, to be + // loaded on demand via ToolSearch. Absent key preserves the previous + // behaviour (all schemas eager); an explicit `true` opts the server in. + let deferred = value.get("deferred").and_then(|v| v.as_bool()).unwrap_or(false); + match row.transport_type.as_str() { "stdio" => { let command = value @@ -531,7 +536,7 @@ async fn row_to_mcp_server_config( env: Some(env), url: None, headers: None, - deferred: Some(false), + deferred: Some(deferred), startup_timeout_ms: None, }) } @@ -557,7 +562,7 @@ async fn row_to_mcp_server_config( env: None, url: Some(url.to_owned()), headers: Some(headers), - deferred: Some(false), + deferred: Some(deferred), startup_timeout_ms: None, }) } @@ -583,7 +588,7 @@ async fn row_to_mcp_server_config( env: None, url: Some(url.to_owned()), headers: Some(headers), - deferred: Some(false), + deferred: Some(deferred), startup_timeout_ms: None, }) } @@ -1018,6 +1023,34 @@ mod tests { ); } + #[tokio::test] + async fn row_to_mcp_server_config_defaults_deferred_to_false() { + let row = make_row("docs", "http", r#"{"url":"https://example.test/mcp"}"#, true, false); + + let config = row_to_mcp_server_config(&row, "user-row", "conv-row", test_broadcaster()) + .await + .expect("convert"); + + assert_eq!(config.deferred, Some(false)); + } + + #[tokio::test] + async fn row_to_mcp_server_config_honours_explicit_deferred() { + let row = make_row( + "docs", + "http", + r#"{"url":"https://example.test/mcp","deferred":true}"#, + true, + false, + ); + + let config = row_to_mcp_server_config(&row, "user-row", "conv-row", test_broadcaster()) + .await + .expect("convert"); + + assert_eq!(config.deferred, Some(true)); + } + struct ProviderMappingCase<'a> { name: &'a str, platform: &'a str,