@@ -5,6 +5,7 @@ import (
@@ -65,11 +66,13 @@ type agent struct {
65 sessions session.Service
66 sessions session.Service
66 messages message.Service
67 messages message.Service
67 permissions permission.Service
68 permissions permission.Service
68− mcpTools []McpTool
69+ baseTools *csync.Map[string, tools.BaseTool]
70+ mcpTools *csync.Map[string, tools.BaseTool]
71+ lspClients *csync.Map[string, *lsp.Client]
70− tools *csync.LazySlice[tools.BaseTool]
71 // We need this to be able to update it when model changes
73 // We need this to be able to update it when model changes
72− agentToolFn func() (tools.BaseTool, error)
74+ agentToolFn func() (tools.BaseTool, error)
75+ cleanupFuncs []func()
74 provider provider.Provider
77 provider provider.Provider
75 providerID string
78 providerID string
@@ -171,14 +174,16 @@ func NewAgent(
171 return nil, err
174 return nil, err
174− toolFn := func() []tools.BaseTool {
177+ baseToolsFn := func() map[string]tools.BaseTool {
175− slog.Info("Initializing agent tools", "agent", agentCfg.ID)
178+ slog.Info("Initializing agent base tools", "agent", agentCfg.ID)
176 defer func() {
179 defer func() {
177− slog.Info("Initialized agent tools", "agent", agentCfg.ID)
180+ slog.Info("Initialized agent base tools", "agent", agentCfg.ID)
183+ // Base tools available to all agents
180 cwd := cfg.WorkingDir()
184 cwd := cfg.WorkingDir()
185+ result := make(map[string]tools.BaseTool)
181− allTools := []tools.BaseTool{
186+ for _, tool := range []tools.BaseTool{
182 tools.NewBashTool(permissions, cwd, cfg.Options.Attribution),
187 tools.NewBashTool(permissions, cwd, cfg.Options.Attribution),
183 tools.NewDownloadTool(permissions, cwd),
188 tools.NewDownloadTool(permissions, cwd),
184 tools.NewEditTool(lspClients, permissions, history, cwd),
189 tools.NewEditTool(lspClients, permissions, history, cwd),
@@ -190,36 +195,25 @@ func NewAgent(
190 tools.NewSourcegraphTool(),
195 tools.NewSourcegraphTool(),
191 tools.NewViewTool(lspClients, permissions, cwd),
196 tools.NewViewTool(lspClients, permissions, cwd),
192 tools.NewWriteTool(lspClients, permissions, history, cwd),
197 tools.NewWriteTool(lspClients, permissions, history, cwd),
199+ result[tool.Name()] = tool
203+ mcpToolsFn := func() map[string]tools.BaseTool {
204+ slog.Info("Initializing agent mcp tools", "agent", agentCfg.ID)
206+ slog.Info("Initialized agent mcp tools", "agent", agentCfg.ID)
195 mcpToolsOnce.Do(func() {
209 mcpToolsOnce.Do(func() {
196− mcpTools = doGetMCPTools(ctx, permissions, cfg)
210+ doGetMCPTools(ctx, permissions, cfg)
199− withCoderTools := func(t []tools.BaseTool) []tools.BaseTool {
213+ return maps.Collect(mcpTools.Seq2())
200− if agentCfg.ID == "coder" {
201− t = append(t, mcpTools...)
202− if lspClients.Len() > 0 {
203− t = append(t, tools.NewDiagnosticsTool(lspClients))
209− if agentCfg.AllowedTools == nil {
210− return withCoderTools(allTools)
213− var filteredTools []tools.BaseTool
214− for _, tool := range allTools {
215− if slices.Contains(agentCfg.AllowedTools, tool.Name()) {
216− filteredTools = append(filteredTools, tool)
219− return withCoderTools(filteredTools)
222− return &agent{
216+ a := &agent{
223 Broker: pubsub.NewBroker[AgentEvent](),
217 Broker: pubsub.NewBroker[AgentEvent](),
224 agentCfg: agentCfg,
218 agentCfg: agentCfg,
225 provider: agentProvider,
219 provider: agentProvider,
@@ -231,10 +225,14 @@ func NewAgent(
231 summarizeProviderID: string(providerCfg.ID),
225 summarizeProviderID: string(providerCfg.ID),
232 agentToolFn: agentToolFn,
226 agentToolFn: agentToolFn,
233 activeRequests: csync.NewMap[string, context.CancelFunc](),
227 activeRequests: csync.NewMap[string, context.CancelFunc](),
234− tools: csync.NewLazySlice(toolFn),
228+ mcpTools: csync.NewLazyMap(mcpToolsFn),
229+ baseTools: csync.NewLazyMap(baseToolsFn),
235 promptQueue: csync.NewMap[string, []string](),
230 promptQueue: csync.NewMap[string, []string](),
236 permissions: permissions,
231 permissions: permissions,
232+ lspClients: lspClients,
237− }, nil
235+ return a, nil
240func (a *agent) Model() catwalk.Model {
238func (a *agent) Model() catwalk.Model {
@@ -517,7 +515,18 @@ func (a *agent) createUserMessage(ctx context.Context, sessionID, content string
519func (a *agent) getAllTools() ([]tools.BaseTool, error) {
517func (a *agent) getAllTools() ([]tools.BaseTool, error) {
520− allTools := slices.Collect(a.tools.Seq())
518+ var allTools []tools.BaseTool
519+ for tool := range a.baseTools.Seq() {
520+ if a.agentCfg.AllowedTools == nil || slices.Contains(a.agentCfg.AllowedTools, tool.Name()) {
521+ allTools = append(allTools, tool)
524+ if a.agentCfg.ID == "coder" {
525+ allTools = slices.AppendSeq(allTools, a.mcpTools.Seq())
526+ if a.lspClients.Len() > 0 {
527+ allTools = append(allTools, tools.NewDiagnosticsTool(a.lspClients))
521 if a.agentToolFn != nil {
530 if a.agentToolFn != nil {
522 agentTool, agentToolErr := a.agentToolFn()
531 agentTool, agentToolErr := a.agentToolFn()
523 if agentToolErr != nil {
532 if agentToolErr != nil {
@@ -591,7 +600,7 @@ loop:
592 // Continue processing
601 // Continue processing
593 var tool tools.BaseTool
602 var tool tools.BaseTool
594− allTools, _ := a.getAllTools()
603+ allTools, _ = a.getAllTools()
595 for _, availableTool := range allTools {
604 for _, availableTool := range allTools {
596 if availableTool.Info().Name == toolCall.Name {
605 if availableTool.Info().Name == toolCall.Name {
597 tool = availableTool
606 tool = availableTool
@@ -960,6 +969,12 @@ func (a *agent) CancelAll() {
960 a.Cancel(key) // key is sessionID
969 a.Cancel(key) // key is sessionID
972+ for _, cleanup := range a.cleanupFuncs {
963 timeout := time.After(5 * time.Second)
978 timeout := time.After(5 * time.Second)
964 for a.IsBusy() {
979 for a.IsBusy() {
@@ -1071,3 +1086,48 @@ func (a *agent) UpdateModel() error {
1072 return nil
1087 return nil
1090+func (a *agent) setupEvents(ctx context.Context) {
1091+ ctx, cancel := context.WithCancel(ctx)
1094+ subCh := SubscribeMCPEvents(ctx)
1098+ case event, ok := <-subCh:
1100+ slog.Debug("MCPEvents subscription channel closed")
1103+ switch event.Payload.Type {
1104+ case MCPEventToolsListChanged:
1105+ name := event.Payload.Name
1106+ c, ok := mcpClients.Get(name)
1108+ slog.Warn("MCP client not found for tools update", "name", name)
1111+ cfg := config.Get()
1112+ tools, err := getTools(ctx, name, a.permissions, c, cfg.WorkingDir())
1114+ slog.Error("error listing tools", "error", err)
1115+ updateMCPState(name, MCPStateError, err, nil, 0)
1119+ updateMcpTools(name, tools)
1120+ a.mcpTools.Reset(maps.Collect(mcpTools.Seq2()))
1121+ updateMCPState(name, MCPStateConnected, nil, c, a.mcpTools.Len())
1126+ slog.Debug("MCPEvents subscription cancelled")
1132+ a.cleanupFuncs = append(a.cleanupFuncs, cancel)