Merge pull request #2489 from afjcjsbx/fix/mcp-reload-discovery-tools
fix(agent): reinitialize MCP and discovery tools after reload
This commit is contained in:
commit
08283dde61
3 changed files with 85 additions and 0 deletions
|
|
@ -1056,8 +1056,23 @@ func (al *AgentLoop) ReloadProviderAndConfig(
|
||||||
|
|
||||||
al.mu.Unlock()
|
al.mu.Unlock()
|
||||||
|
|
||||||
|
oldMCPManager := al.mcp.reset()
|
||||||
al.hookRuntime.reset(al)
|
al.hookRuntime.reset(al)
|
||||||
configureHookManagerFromConfig(al.hooks, cfg)
|
configureHookManagerFromConfig(al.hooks, cfg)
|
||||||
|
if err := al.ensureHooksInitialized(ctx); err != nil {
|
||||||
|
logger.WarnCF("agent", "Configured hooks failed to reinitialize after reload",
|
||||||
|
map[string]any{"error": err.Error()})
|
||||||
|
}
|
||||||
|
if oldMCPManager != nil {
|
||||||
|
if err := oldMCPManager.Close(); err != nil {
|
||||||
|
logger.WarnCF("agent", "Failed to close previous MCP manager during reload",
|
||||||
|
map[string]any{"error": err.Error()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := al.ensureMCPInitialized(ctx); err != nil {
|
||||||
|
logger.WarnCF("agent", "MCP failed to reinitialize after reload",
|
||||||
|
map[string]any{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
// Close old provider after releasing the lock
|
// Close old provider after releasing the lock
|
||||||
// This prevents blocking readers while closing
|
// This prevents blocking readers while closing
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@ type mcpRuntime struct {
|
||||||
initErr error
|
initErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mcpRuntime) reset() *mcp.Manager {
|
||||||
|
r.mu.Lock()
|
||||||
|
manager := r.manager
|
||||||
|
r.manager = nil
|
||||||
|
r.initErr = nil
|
||||||
|
r.initOnce = sync.Once{}
|
||||||
|
r.mu.Unlock()
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mcpRuntime) setManager(manager *mcp.Manager) {
|
func (r *mcpRuntime) setManager(manager *mcp.Manager) {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.manager = manager
|
r.manager = manager
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,73 @@
|
||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/mcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func boolPtr(b bool) *bool { return &b }
|
func boolPtr(b bool) *bool { return &b }
|
||||||
|
|
||||||
|
func TestMCPRuntimeResetClearsState(t *testing.T) {
|
||||||
|
var rt mcpRuntime
|
||||||
|
manager := mcp.NewManager()
|
||||||
|
rt.setManager(manager)
|
||||||
|
rt.setInitErr(errors.New("stale init error"))
|
||||||
|
rt.initOnce.Do(func() {})
|
||||||
|
|
||||||
|
got := rt.reset()
|
||||||
|
if got != manager {
|
||||||
|
t.Fatalf("reset() manager = %p, want %p", got, manager)
|
||||||
|
}
|
||||||
|
if rt.hasManager() {
|
||||||
|
t.Fatal("expected manager to be cleared after reset")
|
||||||
|
}
|
||||||
|
if err := rt.getInitErr(); err != nil {
|
||||||
|
t.Fatalf("getInitErr() = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reran := false
|
||||||
|
rt.initOnce.Do(func() { reran = true })
|
||||||
|
if !reran {
|
||||||
|
t.Fatal("expected initOnce to be reset")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReloadProviderAndConfig_ResetsMCPRuntime(t *testing.T) {
|
||||||
|
al, cfg, _, _, cleanup := newTestAgentLoop(t)
|
||||||
|
defer cleanup()
|
||||||
|
defer al.Close()
|
||||||
|
|
||||||
|
manager := mcp.NewManager()
|
||||||
|
al.mcp.setManager(manager)
|
||||||
|
al.mcp.setInitErr(errors.New("stale init error"))
|
||||||
|
al.mcp.initOnce.Do(func() {})
|
||||||
|
|
||||||
|
if !al.mcp.hasManager() {
|
||||||
|
t.Fatal("expected MCP manager to exist before reload")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := al.ReloadProviderAndConfig(context.Background(), &mockProvider{}, cfg); err != nil {
|
||||||
|
t.Fatalf("ReloadProviderAndConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if al.mcp.hasManager() {
|
||||||
|
t.Fatal("expected MCP manager to be cleared when reloaded config has MCP disabled")
|
||||||
|
}
|
||||||
|
if err := al.mcp.getInitErr(); err != nil {
|
||||||
|
t.Fatalf("getInitErr() = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reran := false
|
||||||
|
al.mcp.initOnce.Do(func() { reran = true })
|
||||||
|
if !reran {
|
||||||
|
t.Fatal("expected MCP initOnce to be reset after reload")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestServerIsDeferred(t *testing.T) {
|
func TestServerIsDeferred(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue