@@ -2,22 +2,43 @@ 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"
1314)
1415
16+ const (
17+ defaultOffset = 0
18+
19+ // 0 = no limit.
20+ defaultLimit = 0
21+ )
22+
1523// listClustersInput defines the input parameters for list_clusters tool.
16- type listClustersInput struct {}
24+ type listClustersInput struct {
25+ Offset int `json:"offset,omitempty"`
26+ Limit int `json:"limit,omitempty"`
27+ }
28+
29+ // ClusterInfo represents information about a single cluster.
30+ type ClusterInfo struct {
31+ ID string `json:"id"`
32+ Name string `json:"name"`
33+ Type string `json:"type"`
34+ }
1735
1836// listClustersOutput defines the output structure for list_clusters tool.
1937type listClustersOutput struct {
20- Clusters []string `json:"clusters"`
38+ Clusters []ClusterInfo `json:"clusters"`
39+ TotalCount int `json:"totalCount"`
40+ Offset int `json:"offset"`
41+ Limit int `json:"limit"`
2142}
2243
2344// listClustersTool implements the list_clusters tool.
@@ -48,63 +69,107 @@ func (t *listClustersTool) GetName() string {
4869func (t * listClustersTool ) GetTool () * mcp.Tool {
4970 return & mcp.Tool {
5071 Name : t .name ,
51- Description : "List all clusters managed by StackRox Central with their IDs, names, and types" ,
72+ Description : "List all clusters managed by StackRox with their IDs, names, and types" ,
73+ InputSchema : listClustersInputSchema (),
74+ }
75+ }
76+
77+ func listClustersInputSchema () * jsonschema.Schema {
78+ schema , err := jsonschema.For [listClustersInput ](nil )
79+ if err != nil {
80+ logging .Fatal ("Could not get jsonschema for list_clusters input" , err )
81+
82+ return nil
5283 }
84+
85+ schema .Properties ["offset" ].Minimum = jsonschema .Ptr (0.0 )
86+ schema .Properties ["offset" ].Default = toolsets .MustJSONMarshal (defaultOffset )
87+ schema .Properties ["offset" ].Description = "Starting index for pagination (0-based)"
88+
89+ schema .Properties ["limit" ].Minimum = jsonschema .Ptr (0.0 )
90+ schema .Properties ["limit" ].Default = toolsets .MustJSONMarshal (defaultLimit )
91+ schema .Properties ["limit" ].Description = "Maximum number of clusters to return (default: 0 - unlimited)"
92+
93+ return schema
5394}
5495
5596// RegisterWith registers the list_clusters tool handler with the MCP server.
5697func (t * listClustersTool ) RegisterWith (server * mcp.Server ) {
5798 mcp .AddTool (server , t .GetTool (), t .handle )
5899}
59100
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 ) {
101+ func (t * listClustersTool ) getClusters (ctx context.Context , req * mcp.CallToolRequest ) ([]ClusterInfo , error ) {
66102 conn , err := t .client .ReadyConn (ctx )
67103 if err != nil {
68- return nil , nil , errors .Wrap (err , "unable to connect to server" )
104+ return nil , errors .Wrap (err , "unable to connect to server" )
69105 }
70106
71107 callCtx := auth .WithMCPRequestContext (ctx , req )
72108
73109 // Create ClustersService client
74110 clustersClient := v1 .NewClustersServiceClient (conn )
75111
76- // Call GetClusters
112+ // Call GetClusters to fetch all clusters
77113 resp , err := clustersClient .GetClusters (callCtx , & v1.GetClustersRequest {})
78114 if err != nil {
79115 // Convert gRPC error to client error
80116 clientErr := client .NewError (err , "GetClusters" )
81117
82- return nil , nil , clientErr
118+ return nil , clientErr
83119 }
84120
85- // Extract cluster information
86- clusters := make ([]string , 0 , len (resp .GetClusters ()))
121+ // Convert all clusters to ClusterInfo objects
122+ allClusters := make ([]ClusterInfo , 0 , len (resp .GetClusters ()))
87123 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 )
124+ clusterInfo := ClusterInfo {
125+ ID : cluster . GetId () ,
126+ Name : cluster .GetName (),
127+ Type : cluster .GetType (). String (),
128+ }
129+ allClusters = append (allClusters , clusterInfo )
94130 }
95131
96- output := & listClustersOutput {
97- Clusters : clusters ,
132+ return allClusters , nil
133+ }
134+
135+ // handle is the handler for list_clusters tool.
136+ func (t * listClustersTool ) handle (
137+ ctx context.Context ,
138+ req * mcp.CallToolRequest ,
139+ input listClustersInput ,
140+ ) (* mcp.CallToolResult , * listClustersOutput , error ) {
141+ clusters , err := t .getClusters (ctx , req )
142+ if err != nil {
143+ return nil , nil , err
144+ }
145+
146+ totalCount := len (clusters )
147+
148+ // 0 = unlimited.
149+ limit := input .Limit
150+ if limit == 0 {
151+ limit = totalCount
152+ }
153+
154+ // Apply client-side pagination.
155+ var paginatedClusters []ClusterInfo
156+ if input .Offset >= totalCount {
157+ paginatedClusters = []ClusterInfo {}
158+ } else {
159+ end := min (input .Offset + limit , totalCount )
160+ if end < 0 {
161+ end = totalCount
162+ }
163+
164+ paginatedClusters = clusters [input .Offset :end ]
98165 }
99166
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- },
167+ output := & listClustersOutput {
168+ Clusters : paginatedClusters ,
169+ TotalCount : totalCount ,
170+ Offset : input .Offset ,
171+ Limit : input .Limit ,
107172 }
108173
109- return result , output , nil
174+ return nil , output , nil
110175}
0 commit comments