Skip to content

Commit aae4ce8

Browse files
committed
feat(tools): add tool for getting label values
Signed-off-by: TJ Hoplock <[email protected]>
1 parent f425f87 commit aae4ce8

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

pkg/mcp/promapi.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,30 @@ func labelNamesApiCall(ctx context.Context, matchers []string, start, end time.T
187187
return string(jsonBytes), nil
188188
}
189189

190+
func labelValuesApiCall(ctx context.Context, label string, matchers []string, start, end time.Time) (string, error) {
191+
result, warnings, err := apiV1Client.LabelValues(ctx, label, matchers, start, end)
192+
if err != nil {
193+
return "", fmt.Errorf("error getting label values: %w", err)
194+
}
195+
196+
lvals := make([]string, len(result))
197+
for i, lval := range result {
198+
lvals[i] = string(lval)
199+
}
200+
201+
res := queryApiResponse{
202+
Result: strings.Join(lvals, "\n"),
203+
Warnings: warnings,
204+
}
205+
206+
jsonBytes, err := json.Marshal(res)
207+
if err != nil {
208+
return "", fmt.Errorf("error converting label values response to JSON: %w", err)
209+
}
210+
211+
return string(jsonBytes), nil
212+
}
213+
190214
func buildinfoApiCall(ctx context.Context) (string, error) {
191215
ctx, cancel := context.WithTimeout(ctx, apiTimeout)
192216
defer cancel()

pkg/mcp/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func NewServer(logger *slog.Logger) *server.MCPServer {
2929
mcpServer.AddTool(rangeQueryTool, rangeQueryToolHandler)
3030
mcpServer.AddTool(seriesTool, seriesToolHandler)
3131
mcpServer.AddTool(labelNamesTool, labelNamesToolHandler)
32+
mcpServer.AddTool(labelValuesTool, labelValuesToolHandler)
3233
mcpServer.AddTool(tsdbStatsTool, tsdbStatsToolHandler)
3334
mcpServer.AddTool(listAlertsTool, listAlertsToolHandler)
3435
mcpServer.AddTool(alertmanagersTool, alertmanagersToolHandler)

pkg/mcp/tools.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ var (
3636
" Must be either Unix timestamp or RFC3339. Defaults to 5m ago."),
3737
),
3838
mcp.WithString("end_time",
39-
mcp.Required(),
4039
mcp.Description("[Optional] End timestamp for the query to be executed at."+
4140
" Must be either Unix timestamp or RFC3339. Defaults to current time."),
4241
),
@@ -57,7 +56,6 @@ var (
5756
" Must be either Unix timestamp or RFC3339. Defaults to 5m ago."),
5857
),
5958
mcp.WithString("end_time",
60-
mcp.Required(),
6159
mcp.Description("[Optional] End timestamp for the query to be executed at."+
6260
" Must be either Unix timestamp or RFC3339. Defaults to current time."),
6361
),
@@ -74,7 +72,26 @@ var (
7472
" Must be either Unix timestamp or RFC3339. Defaults to 5m ago."),
7573
),
7674
mcp.WithString("end_time",
75+
mcp.Description("[Optional] End timestamp for the query to be executed at."+
76+
" Must be either Unix timestamp or RFC3339. Defaults to current time."),
77+
),
78+
)
79+
80+
labelValuesTool = mcp.NewTool("label_values",
81+
mcp.WithDescription("Performs a query for the values of the given label, time range and matchers"),
82+
mcp.WithString("label",
7783
mcp.Required(),
84+
mcp.Description("The label to query values for"),
85+
),
86+
mcp.WithArray("matchers",
87+
mcp.Required(),
88+
mcp.Description("Label matchers"),
89+
),
90+
mcp.WithString("start_time",
91+
mcp.Description("[Optional] Start timestamp for the query to be executed at."+
92+
" Must be either Unix timestamp or RFC3339. Defaults to 5m ago."),
93+
),
94+
mcp.WithString("end_time",
7895
mcp.Description("[Optional] End timestamp for the query to be executed at."+
7996
" Must be either Unix timestamp or RFC3339. Defaults to current time."),
8097
),
@@ -261,6 +278,49 @@ func labelNamesToolHandler(ctx context.Context, request mcp.CallToolRequest) (*m
261278
return mcp.NewToolResultText(data), err
262279
}
263280

281+
// LabelValues performs a query for the values of the given label, time range and matchers.
282+
func labelValuesToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
283+
arguments := request.Params.Arguments
284+
label, ok := arguments["label"].(string)
285+
if !ok {
286+
return nil, errors.New("label must be a string")
287+
}
288+
289+
argMatchers, ok := arguments["matchers"].([]any)
290+
if !ok {
291+
return nil, errors.New("matchers must be an array")
292+
}
293+
294+
matchers := make([]string, len(argMatchers))
295+
for i, m := range argMatchers {
296+
matchers[i] = m.(string)
297+
}
298+
299+
endTs := time.Now()
300+
startTs := endTs.Add(DefaultLookbackDelta)
301+
302+
if argEndTime, ok := arguments["end_time"].(string); ok {
303+
parsedEndTime, err := mcpProm.ParseTimestamp(argEndTime)
304+
if err != nil {
305+
return nil, fmt.Errorf("failed to parse end_time %s from args: %w", argEndTime, err)
306+
}
307+
308+
endTs = parsedEndTime
309+
}
310+
311+
if argStartTime, ok := arguments["start_time"].(string); ok {
312+
parsedStartTime, err := mcpProm.ParseTimestamp(argStartTime)
313+
if err != nil {
314+
return nil, fmt.Errorf("failed to parse start_time %s from args: %w", argStartTime, err)
315+
}
316+
317+
startTs = parsedStartTime
318+
}
319+
320+
data, err := labelValuesApiCall(ctx, label, matchers, startTs, endTs)
321+
return mcp.NewToolResultText(data), err
322+
}
323+
264324
func listAlertsToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
265325
data, err := listAlertsApiCall(ctx)
266326
return mcp.NewToolResultText(data), err

0 commit comments

Comments
 (0)