From 6892d006d680902b32e07743434101db4cafec92 Mon Sep 17 00:00:00 2001 From: yuchou87 Date: Tue, 17 Feb 2026 10:39:39 +0800 Subject: [PATCH] perf(agent): reduce memory footprint by storing minimal MCP dependencies Replace full *config.Config reference with config.MCPConfig value type in AgentLoop to allow garbage collection of unused configuration data. Changes: - AgentLoop now stores only MCPConfig and workspacePath (minimal deps) - Add mcp.Manager.LoadFromMCPConfig() for minimal dependency version - Keep LoadFromConfig() for backward compatibility - Full Config object can be GC'd after NewAgentLoop() returns This optimization reduces memory usage by not holding references to unused channel, provider, gateway, and device configurations. --- pkg/agent/loop.go | 8 +++++--- pkg/mcp/manager.go | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index c4d7c4ae7..69aa8759a 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -43,7 +43,8 @@ type AgentLoop struct { contextBuilder *ContextBuilder tools *tools.ToolRegistry mcpManager *mcp.Manager // MCP server manager for resource cleanup - mcpConfig *config.Config // Config for lazy MCP initialization + mcpConfig config.MCPConfig // MCP config for lazy initialization (minimal dependency) + workspacePath string // Workspace path for resolving relative envFile paths mcpInitOnce sync.Once // Ensures MCP is initialized only once subagentManager *tools.SubagentManager // Subagent manager for MCP tool registration running atomic.Bool @@ -160,7 +161,8 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers contextBuilder: contextBuilder, tools: toolsRegistry, mcpManager: mcpManager, - mcpConfig: cfg, // Store config for lazy initialization in Run() + mcpConfig: cfg.Tools.MCP, // Store only MCP config (minimal dependency) + workspacePath: workspace, // Store workspace path for envFile resolution subagentManager: subagentManager, summarizing: sync.Map{}, } @@ -172,7 +174,7 @@ func (al *AgentLoop) Run(ctx context.Context) error { // Initialize MCP servers using the agent's lifecycle context // This ensures MCP connections are cancelled when the agent stops al.mcpInitOnce.Do(func() { - if err := al.mcpManager.LoadFromConfig(ctx, al.mcpConfig); err != nil { + if err := al.mcpManager.LoadFromMCPConfig(ctx, al.mcpConfig, al.workspacePath); err != nil { logger.WarnCF("agent", "Failed to load MCP servers, MCP tools will not be available", map[string]interface{}{ "error": err.Error(), diff --git a/pkg/mcp/manager.go b/pkg/mcp/manager.go index 8449486f5..833755cbd 100644 --- a/pkg/mcp/manager.go +++ b/pkg/mcp/manager.go @@ -114,29 +114,32 @@ func NewManager() *Manager { // LoadFromConfig loads MCP servers from configuration func (m *Manager) LoadFromConfig(ctx context.Context, cfg *config.Config) error { - if !cfg.Tools.MCP.Enabled { + return m.LoadFromMCPConfig(ctx, cfg.Tools.MCP, cfg.WorkspacePath()) +} + +// LoadFromMCPConfig loads MCP servers from MCP configuration and workspace path. +// This is the minimal dependency version that doesn't require the full Config object. +func (m *Manager) LoadFromMCPConfig(ctx context.Context, mcpCfg config.MCPConfig, workspacePath string) error { + if !mcpCfg.Enabled { logger.InfoCF("mcp", "MCP integration is disabled", nil) return nil } - if len(cfg.Tools.MCP.Servers) == 0 { + if len(mcpCfg.Servers) == 0 { logger.InfoCF("mcp", "No MCP servers configured", nil) return nil } logger.InfoCF("mcp", "Initializing MCP servers", map[string]interface{}{ - "count": len(cfg.Tools.MCP.Servers), + "count": len(mcpCfg.Servers), }) - // Get workspace path for resolving relative envFile paths - workspacePath := cfg.WorkspacePath() - var wg sync.WaitGroup - errs := make(chan error, len(cfg.Tools.MCP.Servers)) + errs := make(chan error, len(mcpCfg.Servers)) enabledCount := 0 - for name, serverCfg := range cfg.Tools.MCP.Servers { + for name, serverCfg := range mcpCfg.Servers { if !serverCfg.Enabled { logger.DebugCF("mcp", "Skipping disabled server", map[string]interface{}{