@@ -69,8 +69,9 @@ type MCPEvent struct {
// MCPCounts number of available tools, prompts, etc.
type MCPCounts struct {
- Tools int
- Prompts int
+ Tools int
+ Prompts int
+ Resources int
}
// MCPClientInfo holds information about an MCP client's state
@@ -84,14 +85,16 @@ type MCPClientInfo struct {
}
var (
- mcpToolsOnce sync.Once
- mcpTools = csync.NewMap[string, tools.BaseTool]()
- mcpClient2Tools = csync.NewMap[string, []tools.BaseTool]()
- mcpClients = csync.NewMap[string, *mcp.ClientSession]()
- mcpStates = csync.NewMap[string, MCPClientInfo]()
- mcpBroker = pubsub.NewBroker[MCPEvent]()
- mcpPrompts = csync.NewMap[string, *mcp.Prompt]()
- mcpClient2Prompts = csync.NewMap[string, []*mcp.Prompt]()
+ mcpToolsOnce sync.Once
+ mcpTools = csync.NewMap[string, tools.BaseTool]()
+ mcpClient2Tools = csync.NewMap[string, []tools.BaseTool]()
+ mcpClients = csync.NewMap[string, *mcp.ClientSession]()
+ mcpStates = csync.NewMap[string, MCPClientInfo]()
+ mcpBroker = pubsub.NewBroker[MCPEvent]()
+ mcpPrompts = csync.NewMap[string, *mcp.Prompt]()
+ mcpClient2Prompts = csync.NewMap[string, []*mcp.Prompt]()
+ mcpResources = csync.NewMap[string, *mcp.Resource]()
+ mcpClient2Resources = csync.NewMap[string, []*mcp.Resource]()
)
type McpTool struct {
@@ -327,12 +330,22 @@ func doGetMCPTools(ctx context.Context, permissions permission.Service, cfg *con
return
}
+ resources, err := getResources(ctx, c)
+ if err != nil {
+ slog.Error("error listing resources", "error", err)
+ updateMCPState(name, MCPStateError, err, nil, MCPCounts{})
+ c.Close()
+ return
+ }
+
updateMcpTools(name, tools)
updateMcpPrompts(name, prompts)
+ updateMcpResources(name, resources)
mcpClients.Set(name, c)
counts := MCPCounts{
- Tools: len(tools),
- Prompts: len(prompts),
+ Tools: len(tools),
+ Prompts: len(prompts),
+ Resources: len(resources),
}
updateMCPState(name, MCPStateConnected, nil, c, counts)
}(name, m)
@@ -496,6 +509,32 @@ func updateMcpPrompts(mcpName string, prompts []*mcp.Prompt) {
}
}
+func getResources(ctx context.Context, c *mcp.ClientSession) ([]*mcp.Resource, error) {
+ if c.InitializeResult().Capabilities.Resources == nil {
+ return nil, nil
+ }
+ result, err := c.ListResources(ctx, &mcp.ListResourcesParams{})
+ if err != nil {
+ return nil, err
+ }
+ return result.Resources, nil
+}
+
+// updateMcpResources updates the global mcpResources and mcpClient2Resources maps.
+func updateMcpResources(mcpName string, resources []*mcp.Resource) {
+ if len(resources) == 0 {
+ mcpClient2Resources.Del(mcpName)
+ } else {
+ mcpClient2Resources.Set(mcpName, resources)
+ }
+ for clientName, resources := range mcpClient2Resources.Seq2() {
+ for _, p := range resources {
+ key := clientName + ":" + p.Name
+ mcpResources.Set(key, p)
+ }
+ }
+}
+
// GetMCPPrompts returns all available MCP prompts.
func GetMCPPrompts() map[string]*mcp.Prompt {
return maps.Collect(mcpPrompts.Seq2())
@@ -74,6 +74,9 @@ func RenderMCPList(opts RenderOptions) []string {
if count := state.Counts.Prompts; count > 0 {
extraContent = append(extraContent, t.S().Subtle.Render(fmt.Sprintf("%d prompts", count)))
}
+ if count := state.Counts.Resources; count > 0 {
+ extraContent = append(extraContent, t.S().Subtle.Render(fmt.Sprintf("%d resources", count)))
+ }
case agent.MCPStateError:
icon = t.ItemErrorIcon
if state.Error != nil {