RefreshTools re-fetches tool lists from all connected servers in parallel and rebuilds the cache. On partial failure, tools from servers that responded successfully are merged with the existing cached tools for servers that failed, so a single dead server doesn't block updates from healthy ones.
(ctx context.Context)
| 773 | // existing cached tools for servers that failed, so a single |
| 774 | // dead server doesn't block updates from healthy ones. |
| 775 | func (m *Manager) RefreshTools(ctx context.Context) error { |
| 776 | logger := m.logger.With(agentchat.Fields(ctx)...) |
| 777 | |
| 778 | // Snapshot servers under read lock. |
| 779 | m.mu.RLock() |
| 780 | servers := make(map[string]*serverEntry, len(m.servers)) |
| 781 | for k, v := range m.servers { |
| 782 | servers[k] = v |
| 783 | } |
| 784 | gen := m.serverGen |
| 785 | m.mu.RUnlock() |
| 786 | |
| 787 | // Fetch tool lists in parallel without holding any lock. |
| 788 | type serverTools struct { |
| 789 | name string |
| 790 | tools []workspacesdk.MCPToolInfo |
| 791 | } |
| 792 | var ( |
| 793 | mu sync.Mutex |
| 794 | results []serverTools |
| 795 | failed []string |
| 796 | errs []error |
| 797 | ) |
| 798 | var eg errgroup.Group |
| 799 | for name, entry := range servers { |
| 800 | eg.Go(func() error { |
| 801 | listCtx, cancel := context.WithTimeout(ctx, connectTimeout) |
| 802 | result, err := entry.client.ListTools(listCtx, mcp.ListToolsRequest{}) |
| 803 | cancel() |
| 804 | if err != nil { |
| 805 | logger.Warn(ctx, "failed to list tools from MCP server", |
| 806 | slog.F("server", name), |
| 807 | slog.Error(err), |
| 808 | ) |
| 809 | mu.Lock() |
| 810 | errs = append(errs, xerrors.Errorf("list tools from %q: %w", name, err)) |
| 811 | failed = append(failed, name) |
| 812 | mu.Unlock() |
| 813 | return nil |
| 814 | } |
| 815 | var tools []workspacesdk.MCPToolInfo |
| 816 | for _, tool := range result.Tools { |
| 817 | tools = append(tools, workspacesdk.MCPToolInfo{ |
| 818 | ServerName: name, |
| 819 | Name: name + ToolNameSep + tool.Name, |
| 820 | Description: tool.Description, |
| 821 | Schema: tool.InputSchema.Properties, |
| 822 | Required: tool.InputSchema.Required, |
| 823 | }) |
| 824 | } |
| 825 | mu.Lock() |
| 826 | results = append(results, serverTools{name: name, tools: tools}) |
| 827 | mu.Unlock() |
| 828 | return nil |
| 829 | }) |
| 830 | } |
| 831 | _ = eg.Wait() |
| 832 |