@@ -2,22 +2,44 @@ package config
22
33import (
44 "context"
5- "fmt"
65
6+ "github.com/google/jsonschema-go/jsonschema"
77 "github.com/modelcontextprotocol/go-sdk/mcp"
88 "github.com/pkg/errors"
99 v1 "github.com/stackrox/rox/generated/api/v1"
1010 "github.com/stackrox/stackrox-mcp/internal/client"
1111 "github.com/stackrox/stackrox-mcp/internal/client/auth"
12+ "github.com/stackrox/stackrox-mcp/internal/logging"
1213 "github.com/stackrox/stackrox-mcp/internal/toolsets"
14+ "github.com/stackrox/stackrox-mcp/internal/util"
15+ )
16+
17+ const (
18+ defaultOffset = 0
19+
20+ // 0 = no limit.
21+ defaultLimit = 0
1322)
1423
1524// listClustersInput defines the input parameters for list_clusters tool.
16- type listClustersInput struct {}
25+ type listClustersInput struct {
26+ Offset int `json:"offset,omitempty"`
27+ Limit int `json:"limit,omitempty"`
28+ }
29+
30+ // ClusterInfo represents information about a single cluster.
31+ type ClusterInfo struct {
32+ ID string `json:"id"`
33+ Name string `json:"name"`
34+ Type string `json:"type"`
35+ }
1736
1837// listClustersOutput defines the output structure for list_clusters tool.
1938type listClustersOutput struct {
20- Clusters []string `json:"clusters"`
39+ Clusters []ClusterInfo `json:"clusters"`
40+ TotalCount int `json:"totalCount"`
41+ Offset int `json:"offset"`
42+ Limit int `json:"limit"`
2143}
2244
2345// listClustersTool implements the list_clusters tool.
@@ -48,63 +70,107 @@ func (t *listClustersTool) GetName() string {
4870func (t * listClustersTool ) GetTool () * mcp.Tool {
4971 return & mcp.Tool {
5072 Name : t .name ,
51- Description : "List all clusters managed by StackRox Central with their IDs, names, and types" ,
73+ Description : "List all clusters managed by StackRox with their IDs, names, and types" ,
74+ InputSchema : listClustersInputSchema (),
75+ }
76+ }
77+
78+ func listClustersInputSchema () * jsonschema.Schema {
79+ schema , err := jsonschema.For [listClustersInput ](nil )
80+ if err != nil {
81+ logging .Fatal ("Could not get jsonschema for list_clusters input" , err )
82+
83+ return nil
5284 }
85+
86+ schema .Properties ["offset" ].Minimum = jsonschema .Ptr (0.0 )
87+ schema .Properties ["offset" ].Default = util .MustMarshal (defaultOffset )
88+ schema .Properties ["offset" ].Description = "Starting index for pagination (0-based)"
89+
90+ schema .Properties ["limit" ].Minimum = jsonschema .Ptr (0.0 )
91+ schema .Properties ["limit" ].Default = util .MustMarshal (defaultLimit )
92+ schema .Properties ["limit" ].Description = "Maximum number of clusters to return (default: 0 - unlimited)"
93+
94+ return schema
5395}
5496
5597// RegisterWith registers the list_clusters tool handler with the MCP server.
5698func (t * listClustersTool ) RegisterWith (server * mcp.Server ) {
5799 mcp .AddTool (server , t .GetTool (), t .handle )
58100}
59101
60- // handle is the placeholder handler for list_clusters tool.
61- func (t * listClustersTool ) handle (
62- ctx context.Context ,
63- req * mcp.CallToolRequest ,
64- _ listClustersInput ,
65- ) (* mcp.CallToolResult , * listClustersOutput , error ) {
102+ func (t * listClustersTool ) getClusters (ctx context.Context , req * mcp.CallToolRequest ) ([]ClusterInfo , error ) {
66103 conn , err := t .client .ReadyConn (ctx )
67104 if err != nil {
68- return nil , nil , errors .Wrap (err , "unable to connect to server" )
105+ return nil , errors .Wrap (err , "unable to connect to server" )
69106 }
70107
71108 callCtx := auth .WithMCPRequestContext (ctx , req )
72109
73110 // Create ClustersService client
74111 clustersClient := v1 .NewClustersServiceClient (conn )
75112
76- // Call GetClusters
113+ // Call GetClusters to fetch all clusters
77114 resp , err := clustersClient .GetClusters (callCtx , & v1.GetClustersRequest {})
78115 if err != nil {
79116 // Convert gRPC error to client error
80117 clientErr := client .NewError (err , "GetClusters" )
81118
82- return nil , nil , clientErr
119+ return nil , clientErr
83120 }
84121
85- // Extract cluster information
86- clusters := make ([]string , 0 , len (resp .GetClusters ()))
122+ // Convert all clusters to ClusterInfo objects
123+ allClusters := make ([]ClusterInfo , 0 , len (resp .GetClusters ()))
87124 for _ , cluster := range resp .GetClusters () {
88- // Format: "ID: <id>, Name: <name>, Type: <type>"
89- clusterInfo := fmt . Sprintf ( " ID: %s, Name: %s, Type: %s" ,
90- cluster .GetId (),
91- cluster .GetName (),
92- cluster . GetType (). String ())
93- clusters = append (clusters , clusterInfo )
125+ clusterInfo := ClusterInfo {
126+ ID : cluster . GetId () ,
127+ Name : cluster .GetName (),
128+ Type : cluster .GetType (). String (),
129+ }
130+ allClusters = append (allClusters , clusterInfo )
94131 }
95132
96- output := & listClustersOutput {
97- Clusters : clusters ,
133+ return allClusters , nil
134+ }
135+
136+ // handle is the handler for list_clusters tool.
137+ func (t * listClustersTool ) handle (
138+ ctx context.Context ,
139+ req * mcp.CallToolRequest ,
140+ input listClustersInput ,
141+ ) (* mcp.CallToolResult , * listClustersOutput , error ) {
142+ clusters , err := t .getClusters (ctx , req )
143+ if err != nil {
144+ return nil , nil , err
145+ }
146+
147+ totalCount := len (clusters )
148+
149+ // 0 = unlimited.
150+ limit := input .Limit
151+ if limit == 0 {
152+ limit = totalCount
153+ }
154+
155+ // Apply client-side pagination.
156+ var paginatedClusters []ClusterInfo
157+ if input .Offset >= totalCount {
158+ paginatedClusters = []ClusterInfo {}
159+ } else {
160+ end := min (input .Offset + limit , totalCount )
161+ if end < 0 {
162+ end = totalCount
163+ }
164+
165+ paginatedClusters = clusters [input .Offset :end ]
98166 }
99167
100- // Return result with text content
101- result := & mcp.CallToolResult {
102- Content : []mcp.Content {
103- & mcp.TextContent {
104- Text : fmt .Sprintf ("Found %d cluster(s)" , len (clusters )),
105- },
106- },
168+ output := & listClustersOutput {
169+ Clusters : paginatedClusters ,
170+ TotalCount : totalCount ,
171+ Offset : input .Offset ,
172+ Limit : input .Limit ,
107173 }
108174
109- return result , output , nil
175+ return nil , output , nil
110176}
0 commit comments