Feat/add tool enable or disable configuration (#1071)

* Add tools enable or diable config
This commit is contained in:
lxowalle 2026-03-05 14:53:26 +08:00 committed by GitHub
parent 10ad9e83f9
commit 6f5930624b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 367 additions and 123 deletions

View file

@ -230,19 +230,25 @@ func setupCronTool(
// Create cron service // Create cron service
cronService := cron.NewCronService(cronStorePath, nil) cronService := cron.NewCronService(cronStorePath, nil)
// Create and register CronTool // Create and register CronTool if enabled
cronTool, err := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, cfg) var cronTool *tools.CronTool
if cfg.Tools.IsToolEnabled("cron") {
var err error
cronTool, err = tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, cfg)
if err != nil { if err != nil {
log.Fatalf("Critical error during CronTool initialization: %v", err) log.Fatalf("Critical error during CronTool initialization: %v", err)
} }
agentLoop.RegisterTool(cronTool) agentLoop.RegisterTool(cronTool)
}
// Set the onJob handler // Set onJob handler
if cronTool != nil {
cronService.SetOnJob(func(job *cron.CronJob) (string, error) { cronService.SetOnJob(func(job *cron.CronJob) (string, error) {
result := cronTool.ExecuteJob(context.Background(), job) result := cronTool.ExecuteJob(context.Background(), job)
return result, nil return result, nil
}) })
}
return cronService return cronService
} }

View file

@ -232,24 +232,41 @@
} }
}, },
"tools": { "tools": {
"allow_read_paths": null,
"allow_write_paths": null,
"web": { "web": {
"enabled": true,
"brave": { "brave": {
"enabled": false, "enabled": false,
"api_key": "YOUR_BRAVE_API_KEY", "api_key": "YOUR_BRAVE_API_KEY",
"max_results": 5 "max_results": 5
}, },
"tavily": {
"enabled": false,
"api_key": "",
"base_url": "",
"max_results": 0
},
"duckduckgo": { "duckduckgo": {
"enabled": true, "enabled": true,
"max_results": 5 "max_results": 5
}, },
"perplexity": { "perplexity": {
"enabled": false, "enabled": false,
"api_key": "pplx-xxx", "api_key": "",
"max_results": 5 "max_results": 5
}, },
"proxy": "" "glm_search": {
"enabled": false,
"api_key": "",
"base_url": "https://open.bigmodel.cn/api/paas/v4/web_search",
"search_engine": "search_std",
"max_results": 5
},
"fetch_limit_bytes": 10485760
}, },
"cron": { "cron": {
"enabled": true,
"exec_timeout_minutes": 5 "exec_timeout_minutes": 5
}, },
"mcp": { "mcp": {
@ -318,19 +335,75 @@
} }
}, },
"exec": { "exec": {
"enable_deny_patterns": false, "enabled": true,
"custom_deny_patterns": [] "enable_deny_patterns": true,
"custom_deny_patterns": null,
"custom_allow_patterns": null
}, },
"skills": { "skills": {
"enabled": true,
"registries": { "registries": {
"clawhub": { "clawhub": {
"enabled": true, "enabled": true,
"base_url": "https://clawhub.ai", "base_url": "https://clawhub.ai",
"search_path": "/api/v1/search", "auth_token": "",
"skills_path": "/api/v1/skills", "search_path": "",
"download_path": "/api/v1/download" "skills_path": "",
"download_path": "",
"timeout": 0,
"max_zip_size": 0,
"max_response_size": 0
} }
},
"max_concurrent_searches": 2,
"search_cache": {
"max_size": 50,
"ttl_seconds": 300
} }
},
"media_cleanup": {
"enabled": true,
"max_age_minutes": 30,
"interval_minutes": 5
},
"append_file": {
"enabled": true
},
"edit_file": {
"enabled": true
},
"find_skills": {
"enabled": true
},
"i2c": {
"enabled": false
},
"install_skill": {
"enabled": true
},
"list_dir": {
"enabled": true
},
"message": {
"enabled": true
},
"read_file": {
"enabled": true
},
"spawn": {
"enabled": true
},
"spi": {
"enabled": false
},
"subagent": {
"enabled": true
},
"web_fetch": {
"enabled": true
},
"write_file": {
"enabled": true
} }
}, },
"heartbeat": { "heartbeat": {

View file

@ -60,17 +60,30 @@ func NewAgentInstance(
allowWritePaths := compilePatterns(cfg.Tools.AllowWritePaths) allowWritePaths := compilePatterns(cfg.Tools.AllowWritePaths)
toolsRegistry := tools.NewToolRegistry() toolsRegistry := tools.NewToolRegistry()
if cfg.Tools.IsToolEnabled("read_file") {
toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths)) toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths))
}
if cfg.Tools.IsToolEnabled("write_file") {
toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths)) toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths))
}
if cfg.Tools.IsToolEnabled("list_dir") {
toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths)) toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths))
}
if cfg.Tools.IsToolEnabled("exec") {
execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg) execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg)
if err != nil { if err != nil {
log.Fatalf("Critical error: unable to initialize exec tool: %v", err) log.Fatalf("Critical error: unable to initialize exec tool: %v", err)
} }
toolsRegistry.Register(execTool) toolsRegistry.Register(execTool)
}
if cfg.Tools.IsToolEnabled("edit_file") {
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths)) toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths))
}
if cfg.Tools.IsToolEnabled("append_file") {
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths)) toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths))
}
sessionsDir := filepath.Join(workspace, "sessions") sessionsDir := filepath.Join(workspace, "sessions")
sessionsManager := session.NewSessionManager(sessionsDir) sessionsManager := session.NewSessionManager(sessionsDir)

View file

@ -108,6 +108,7 @@ func registerSharedTools(
} }
// Web tools // Web tools
if cfg.Tools.IsToolEnabled("web") {
searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{ searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{
BraveAPIKey: cfg.Tools.Web.Brave.APIKey, BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
BraveMaxResults: cfg.Tools.Web.Brave.MaxResults, BraveMaxResults: cfg.Tools.Web.Brave.MaxResults,
@ -133,18 +134,26 @@ func registerSharedTools(
} else if searchTool != nil { } else if searchTool != nil {
agent.Tools.Register(searchTool) agent.Tools.Register(searchTool)
} }
}
if cfg.Tools.IsToolEnabled("web_fetch") {
fetchTool, err := tools.NewWebFetchToolWithProxy(50000, cfg.Tools.Web.Proxy, cfg.Tools.Web.FetchLimitBytes) fetchTool, err := tools.NewWebFetchToolWithProxy(50000, cfg.Tools.Web.Proxy, cfg.Tools.Web.FetchLimitBytes)
if err != nil { if err != nil {
logger.ErrorCF("agent", "Failed to create web fetch tool", map[string]any{"error": err.Error()}) logger.ErrorCF("agent", "Failed to create web fetch tool", map[string]any{"error": err.Error()})
} else { } else {
agent.Tools.Register(fetchTool) agent.Tools.Register(fetchTool)
} }
}
// Hardware tools (I2C, SPI) - Linux only, returns error on other platforms // Hardware tools (I2C, SPI) - Linux only, returns error on other platforms
if cfg.Tools.IsToolEnabled("i2c") {
agent.Tools.Register(tools.NewI2CTool()) agent.Tools.Register(tools.NewI2CTool())
}
if cfg.Tools.IsToolEnabled("spi") {
agent.Tools.Register(tools.NewSPITool()) agent.Tools.Register(tools.NewSPITool())
}
// Message tool // Message tool
if cfg.Tools.IsToolEnabled("message") {
messageTool := tools.NewMessageTool() messageTool := tools.NewMessageTool()
messageTool.SetSendCallback(func(channel, chatID, content string) error { messageTool.SetSendCallback(func(channel, chatID, content string) error {
pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second) pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second)
@ -156,20 +165,33 @@ func registerSharedTools(
}) })
}) })
agent.Tools.Register(messageTool) agent.Tools.Register(messageTool)
}
// Skill discovery and installation tools // Skill discovery and installation tools
find_skills_enable := cfg.Tools.IsToolEnabled("find_skills")
install_skills_enable := cfg.Tools.IsToolEnabled("install_skill")
if find_skills_enable || install_skills_enable {
registryMgr := skills.NewRegistryManagerFromConfig(skills.RegistryConfig{ registryMgr := skills.NewRegistryManagerFromConfig(skills.RegistryConfig{
MaxConcurrentSearches: cfg.Tools.Skills.MaxConcurrentSearches, MaxConcurrentSearches: cfg.Tools.Skills.MaxConcurrentSearches,
ClawHub: skills.ClawHubConfig(cfg.Tools.Skills.Registries.ClawHub), ClawHub: skills.ClawHubConfig(cfg.Tools.Skills.Registries.ClawHub),
}) })
if find_skills_enable {
searchCache := skills.NewSearchCache( searchCache := skills.NewSearchCache(
cfg.Tools.Skills.SearchCache.MaxSize, cfg.Tools.Skills.SearchCache.MaxSize,
time.Duration(cfg.Tools.Skills.SearchCache.TTLSeconds)*time.Second, time.Duration(cfg.Tools.Skills.SearchCache.TTLSeconds)*time.Second,
) )
agent.Tools.Register(tools.NewFindSkillsTool(registryMgr, searchCache)) agent.Tools.Register(tools.NewFindSkillsTool(registryMgr, searchCache))
}
if install_skills_enable {
agent.Tools.Register(tools.NewInstallSkillTool(registryMgr, agent.Workspace)) agent.Tools.Register(tools.NewInstallSkillTool(registryMgr, agent.Workspace))
}
}
// Spawn tool with allowlist checker // Spawn tool with allowlist checker
if cfg.Tools.IsToolEnabled("spawn") {
if cfg.Tools.IsToolEnabled("subagent") {
subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus) subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus)
subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature) subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature)
spawnTool := tools.NewSpawnTool(subagentManager) spawnTool := tools.NewSpawnTool(subagentManager)
@ -178,6 +200,10 @@ func registerSharedTools(
return registry.CanSpawnSubagent(currentAgentID, targetAgentID) return registry.CanSpawnSubagent(currentAgentID, targetAgentID)
}) })
agent.Tools.Register(spawnTool) agent.Tools.Register(spawnTool)
} else {
logger.WarnCF("agent", "spawn tool requires subagent to be enabled", nil)
}
}
} }
} }
@ -185,7 +211,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
al.running.Store(true) al.running.Store(true)
// Initialize MCP servers for all agents // Initialize MCP servers for all agents
if al.cfg.Tools.MCP.Enabled { if al.cfg.Tools.IsToolEnabled("mcp") {
mcpManager := mcp.NewManager() mcpManager := mcp.NewManager()
// Ensure MCP connections are cleaned up on exit, regardless of initialization success // Ensure MCP connections are cleaned up on exit, regardless of initialization success
// This fixes resource leak when LoadFromMCPConfig partially succeeds then fails // This fixes resource leak when LoadFromMCPConfig partially succeeds then fails
@ -227,6 +253,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
if !ok { if !ok {
continue continue
} }
mcpTool := tools.NewMCPTool(mcpManager, serverName, tool) mcpTool := tools.NewMCPTool(mcpManager, serverName, tool)
agent.Tools.Register(mcpTool) agent.Tools.Register(mcpTool)
totalRegistrations++ totalRegistrations++

View file

@ -227,16 +227,11 @@ func TestAgentLoop_GetStartupInfo(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
cfg := &config.Config{ cfg := config.DefaultConfig()
Agents: config.AgentsConfig{ cfg.Agents.Defaults.Workspace = tmpDir
Defaults: config.AgentDefaults{ cfg.Agents.Defaults.Model = "test-model"
Workspace: tmpDir, cfg.Agents.Defaults.MaxTokens = 4096
Model: "test-model", cfg.Agents.Defaults.MaxToolIterations = 10
MaxTokens: 4096,
MaxToolIterations: 10,
},
},
}
msgBus := bus.NewMessageBus() msgBus := bus.NewMessageBus()
provider := &mockProvider{} provider := &mockProvider{}

View file

@ -526,6 +526,10 @@ type GatewayConfig struct {
Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"` Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"`
} }
type ToolConfig struct {
Enabled bool `json:"enabled" env:"ENABLED"`
}
type BraveConfig struct { type BraveConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"` Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"`
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"` APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"`
@ -561,11 +565,12 @@ type GLMSearchConfig struct {
} }
type WebToolsConfig struct { type WebToolsConfig struct {
Brave BraveConfig `json:"brave"` ToolConfig ` envPrefix:"PICOCLAW_TOOLS_WEB_"`
Tavily TavilyConfig `json:"tavily"` Brave BraveConfig ` json:"brave"`
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"` Tavily TavilyConfig ` json:"tavily"`
Perplexity PerplexityConfig `json:"perplexity"` DuckDuckGo DuckDuckGoConfig ` json:"duckduckgo"`
GLMSearch GLMSearchConfig `json:"glm_search"` Perplexity PerplexityConfig ` json:"perplexity"`
GLMSearch GLMSearchConfig ` json:"glm_search"`
// Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h). // Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h).
// For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config. // For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config.
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"` Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
@ -573,19 +578,28 @@ type WebToolsConfig struct {
} }
type CronToolsConfig struct { type CronToolsConfig struct {
ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"` // 0 means no timeout ToolConfig ` envPrefix:"PICOCLAW_TOOLS_CRON_"`
ExecTimeoutMinutes int ` env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES" json:"exec_timeout_minutes"` // 0 means no timeout
} }
type ExecConfig struct { type ExecConfig struct {
EnableDenyPatterns bool `json:"enable_deny_patterns" env:"PICOCLAW_TOOLS_EXEC_ENABLE_DENY_PATTERNS"` ToolConfig ` envPrefix:"PICOCLAW_TOOLS_EXEC_"`
CustomDenyPatterns []string `json:"custom_deny_patterns" env:"PICOCLAW_TOOLS_EXEC_CUSTOM_DENY_PATTERNS"` EnableDenyPatterns bool ` env:"PICOCLAW_TOOLS_EXEC_ENABLE_DENY_PATTERNS" json:"enable_deny_patterns"`
CustomAllowPatterns []string `json:"custom_allow_patterns" env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS"` CustomDenyPatterns []string ` env:"PICOCLAW_TOOLS_EXEC_CUSTOM_DENY_PATTERNS" json:"custom_deny_patterns"`
CustomAllowPatterns []string ` env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS" json:"custom_allow_patterns"`
}
type SkillsToolsConfig struct {
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_SKILLS_"`
Registries SkillsRegistriesConfig ` json:"registries"`
MaxConcurrentSearches int ` json:"max_concurrent_searches" env:"PICOCLAW_TOOLS_SKILLS_MAX_CONCURRENT_SEARCHES"`
SearchCache SearchCacheConfig ` json:"search_cache"`
} }
type MediaCleanupConfig struct { type MediaCleanupConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_MEDIA_CLEANUP_ENABLED"` ToolConfig ` envPrefix:"PICOCLAW_MEDIA_CLEANUP_"`
MaxAge int `json:"max_age_minutes" env:"PICOCLAW_MEDIA_CLEANUP_MAX_AGE"` MaxAge int ` env:"PICOCLAW_MEDIA_CLEANUP_MAX_AGE" json:"max_age_minutes"`
Interval int `json:"interval_minutes" env:"PICOCLAW_MEDIA_CLEANUP_INTERVAL"` Interval int ` env:"PICOCLAW_MEDIA_CLEANUP_INTERVAL" json:"interval_minutes"`
} }
type ToolsConfig struct { type ToolsConfig struct {
@ -597,12 +611,19 @@ type ToolsConfig struct {
Skills SkillsToolsConfig `json:"skills"` Skills SkillsToolsConfig `json:"skills"`
MediaCleanup MediaCleanupConfig `json:"media_cleanup"` MediaCleanup MediaCleanupConfig `json:"media_cleanup"`
MCP MCPConfig `json:"mcp"` MCP MCPConfig `json:"mcp"`
} AppendFile ToolConfig `json:"append_file" envPrefix:"PICOCLAW_TOOLS_APPEND_FILE_"`
EditFile ToolConfig `json:"edit_file" envPrefix:"PICOCLAW_TOOLS_EDIT_FILE_"`
type SkillsToolsConfig struct { FindSkills ToolConfig `json:"find_skills" envPrefix:"PICOCLAW_TOOLS_FIND_SKILLS_"`
Registries SkillsRegistriesConfig `json:"registries"` I2C ToolConfig `json:"i2c" envPrefix:"PICOCLAW_TOOLS_I2C_"`
MaxConcurrentSearches int `json:"max_concurrent_searches" env:"PICOCLAW_SKILLS_MAX_CONCURRENT_SEARCHES"` InstallSkill ToolConfig `json:"install_skill" envPrefix:"PICOCLAW_TOOLS_INSTALL_SKILL_"`
SearchCache SearchCacheConfig `json:"search_cache"` ListDir ToolConfig `json:"list_dir" envPrefix:"PICOCLAW_TOOLS_LIST_DIR_"`
Message ToolConfig `json:"message" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"`
ReadFile ToolConfig `json:"read_file" envPrefix:"PICOCLAW_TOOLS_READ_FILE_"`
Spawn ToolConfig `json:"spawn" envPrefix:"PICOCLAW_TOOLS_SPAWN_"`
SPI ToolConfig `json:"spi" envPrefix:"PICOCLAW_TOOLS_SPI_"`
Subagent ToolConfig `json:"subagent" envPrefix:"PICOCLAW_TOOLS_SUBAGENT_"`
WebFetch ToolConfig `json:"web_fetch" envPrefix:"PICOCLAW_TOOLS_WEB_FETCH_"`
WriteFile ToolConfig `json:"write_file" envPrefix:"PICOCLAW_TOOLS_WRITE_FILE_"`
} }
type SearchCacheConfig struct { type SearchCacheConfig struct {
@ -648,8 +669,7 @@ type MCPServerConfig struct {
// MCPConfig defines configuration for all MCP servers // MCPConfig defines configuration for all MCP servers
type MCPConfig struct { type MCPConfig struct {
// Enabled globally enables/disables MCP integration ToolConfig `envPrefix:"PICOCLAW_TOOLS_MCP_"`
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_MCP_ENABLED"`
// Servers is a map of server name to server configuration // Servers is a map of server name to server configuration
Servers map[string]MCPServerConfig `json:"servers,omitempty"` Servers map[string]MCPServerConfig `json:"servers,omitempty"`
} }
@ -835,3 +855,48 @@ func (c *Config) ValidateModelList() error {
} }
return nil return nil
} }
func (t *ToolsConfig) IsToolEnabled(name string) bool {
switch name {
case "web":
return t.Web.Enabled
case "cron":
return t.Cron.Enabled
case "exec":
return t.Exec.Enabled
case "skills":
return t.Skills.Enabled
case "media_cleanup":
return t.MediaCleanup.Enabled
case "append_file":
return t.AppendFile.Enabled
case "edit_file":
return t.EditFile.Enabled
case "find_skills":
return t.FindSkills.Enabled
case "i2c":
return t.I2C.Enabled
case "install_skill":
return t.InstallSkill.Enabled
case "list_dir":
return t.ListDir.Enabled
case "message":
return t.Message.Enabled
case "read_file":
return t.ReadFile.Enabled
case "spawn":
return t.Spawn.Enabled
case "spi":
return t.SPI.Enabled
case "subagent":
return t.Subagent.Enabled
case "web_fetch":
return t.WebFetch.Enabled
case "write_file":
return t.WriteFile.Enabled
case "mcp":
return t.MCP.Enabled
default:
return true
}
}

View file

@ -336,11 +336,16 @@ func DefaultConfig() *Config {
}, },
Tools: ToolsConfig{ Tools: ToolsConfig{
MediaCleanup: MediaCleanupConfig{ MediaCleanup: MediaCleanupConfig{
ToolConfig: ToolConfig{
Enabled: true, Enabled: true,
},
MaxAge: 30, MaxAge: 30,
Interval: 5, Interval: 5,
}, },
Web: WebToolsConfig{ Web: WebToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
Proxy: "", Proxy: "",
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
Brave: BraveConfig{ Brave: BraveConfig{
@ -366,12 +371,21 @@ func DefaultConfig() *Config {
}, },
}, },
Cron: CronToolsConfig{ Cron: CronToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
ExecTimeoutMinutes: 5, ExecTimeoutMinutes: 5,
}, },
Exec: ExecConfig{ Exec: ExecConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
EnableDenyPatterns: true, EnableDenyPatterns: true,
}, },
Skills: SkillsToolsConfig{ Skills: SkillsToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
Registries: SkillsRegistriesConfig{ Registries: SkillsRegistriesConfig{
ClawHub: ClawHubRegistryConfig{ ClawHub: ClawHubRegistryConfig{
Enabled: true, Enabled: true,
@ -385,9 +399,50 @@ func DefaultConfig() *Config {
}, },
}, },
MCP: MCPConfig{ MCP: MCPConfig{
ToolConfig: ToolConfig{
Enabled: false, Enabled: false,
},
Servers: map[string]MCPServerConfig{}, Servers: map[string]MCPServerConfig{},
}, },
AppendFile: ToolConfig{
Enabled: true,
},
EditFile: ToolConfig{
Enabled: true,
},
FindSkills: ToolConfig{
Enabled: true,
},
I2C: ToolConfig{
Enabled: false, // Hardware tool - Linux only
},
InstallSkill: ToolConfig{
Enabled: true,
},
ListDir: ToolConfig{
Enabled: true,
},
Message: ToolConfig{
Enabled: true,
},
ReadFile: ToolConfig{
Enabled: true,
},
Spawn: ToolConfig{
Enabled: true,
},
SPI: ToolConfig{
Enabled: false, // Hardware tool - Linux only
},
Subagent: ToolConfig{
Enabled: true,
},
WebFetch: ToolConfig{
Enabled: true,
},
WriteFile: ToolConfig{
Enabled: true,
},
}, },
Heartbeat: HeartbeatConfig{ Heartbeat: HeartbeatConfig{
Enabled: true, Enabled: true,

View file

@ -194,7 +194,9 @@ func TestLoadFromMCPConfig_EmptyWorkspaceWithRelativeEnvFile(t *testing.T) {
mgr := NewManager() mgr := NewManager()
mcpCfg := config.MCPConfig{ mcpCfg := config.MCPConfig{
ToolConfig: config.ToolConfig{
Enabled: true, Enabled: true,
},
Servers: map[string]config.MCPServerConfig{ Servers: map[string]config.MCPServerConfig{
"test-server": { "test-server": {
Enabled: true, Enabled: true,
@ -228,12 +230,20 @@ func TestNewManager_InitialState(t *testing.T) {
func TestLoadFromMCPConfig_DisabledOrEmptyServers(t *testing.T) { func TestLoadFromMCPConfig_DisabledOrEmptyServers(t *testing.T) {
mgr := NewManager() mgr := NewManager()
err := mgr.LoadFromMCPConfig(context.Background(), config.MCPConfig{Enabled: false}, "/tmp") err := mgr.LoadFromMCPConfig(
context.Background(),
config.MCPConfig{ToolConfig: config.ToolConfig{Enabled: false}},
"/tmp",
)
if err != nil { if err != nil {
t.Fatalf("expected nil error when MCP disabled, got: %v", err) t.Fatalf("expected nil error when MCP disabled, got: %v", err)
} }
err = mgr.LoadFromMCPConfig(context.Background(), config.MCPConfig{Enabled: true}, "/tmp") err = mgr.LoadFromMCPConfig(
context.Background(),
config.MCPConfig{ToolConfig: config.ToolConfig{Enabled: true}},
"/tmp",
)
if err != nil { if err != nil {
t.Fatalf("expected nil error when no servers configured, got: %v", err) t.Fatalf("expected nil error when no servers configured, got: %v", err)
} }