Skip to content

Commit d00d240

Browse files
authored
Merge pull request #70 from tjhop/feat/prom-management-endpoints
feat/prom management endpoints
2 parents ff624ac + b9155f1 commit d00d240

File tree

6 files changed

+508
-30
lines changed

6 files changed

+508
-30
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,18 @@ Please see [Flags](#command-line-flags) for more information on the available fl
6060
| `docs_search` | Search the markdown files containing official Prometheus documentation from the prometheus/docs repo |
6161
| `exemplars_query` | Performs a query for exemplars by the given query and time range |
6262
| `flags` | Get runtime flags |
63+
| `healthy` | Management API endpoint that can be used to check Prometheus health |
6364
| `label_names` | Returns the unique label names present in the block in sorted order by given time range and matchers |
6465
| `label_values` | Performs a query for the values of the given label, time range and matchers |
6566
| `list_alerts` | List all active alerts |
6667
| `list_rules` | List all alerting and recording rules that are loaded |
6768
| `list_targets` | Get overview of Prometheus target discovery |
6869
| `metric_metadata` | Returns metadata about metrics currently scraped by the metric name |
6970
| `query` | Execute an instant query against the Prometheus datasource |
71+
| `quit` | Management API endpoint that can be used to trigger a graceful shutdown of Prometheus |
7072
| `range_query` | Execute a range query against the Prometheus datasource |
73+
| `ready` | Management API endpoint that can be used to check Prometheus is ready to serve traffic (i.e. respond to queries |
74+
| `reload` | Management API endpoint that can be used to trigger a reload of the Prometheus configuration and rule files |
7175
| `runtime_info` | Get Prometheus runtime information |
7276
| `series` | Finds series by label matchers |
7377
| `targets_metadata` | Returns metadata about metrics currently scraped by the target |
@@ -144,6 +148,8 @@ Qualifications and support criteria are still under consideration, please open a
144148
| [`thanos`](https://thanos.io/) | `config` | remove | Thanos does not use a centralized config, so it doesn't implement the endpoint and the tool returns a `404`. |
145149
| [`thanos`](https://thanos.io/) | `wal_replay_status` | remove | Thanos does not implement the endpoint and the tool returns a `404`. |
146150
| [`thanos`](https://thanos.io/) | `list_stores` | add | Thanos provides an additional endpoint to list store API servers. |
151+
| [`thanos`](https://thanos.io/) | `reload` | remove | Thanos does not implement the endpoint and the tool returns a `404`. |
152+
| [`thanos`](https://thanos.io/) | `quit` | remove | Thanos does not implement the endpoint and the tool returns a `404`. |
147153

148154
### Resources
149155

pkg/mcp/promapi.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ package mcp
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
76
"net/http"
87
"strings"
98
"time"
109

11-
"github.com/alpkeskin/gotoon"
1210
promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
1311
"github.com/prometheus/client_golang/prometheus"
1412

@@ -57,25 +55,6 @@ func NewAPIClient(prometheusUrl string, rt http.RoundTripper) (promv1.API, error
5755
return client, nil
5856
}
5957

60-
func toonOrJsonOutput(ctx context.Context, data any) (string, error) {
61-
toonEnabled := getToonOutputFromContext(ctx)
62-
if toonEnabled {
63-
toonEncodedData, err := gotoon.Encode(data)
64-
if err != nil {
65-
return "", fmt.Errorf("error TOON encoding data: %w", err)
66-
}
67-
68-
return toonEncodedData, nil
69-
}
70-
71-
jsonEncodedData, err := json.Marshal(data)
72-
if err != nil {
73-
return "", fmt.Errorf("error marshaling to JSON: %w", err)
74-
}
75-
76-
return string(jsonEncodedData), nil
77-
}
78-
7958
func alertmanagersApiCall(ctx context.Context) (string, error) {
8059
client, err := getApiClientFromContext(ctx)
8160
if err != nil {

pkg/mcp/prometheus_tools.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"math"
7+
"net/http"
78
"path"
89
"strings"
910
"time"
@@ -206,6 +207,23 @@ var (
206207
),
207208
)
208209

210+
// Tools for Prometheus Management API.
211+
prometheusHealthyTool = mcp.NewTool("healthy",
212+
mcp.WithDescription("Management API endpoint that can be used to check Prometheus health."),
213+
)
214+
215+
prometheusReadyTool = mcp.NewTool("ready",
216+
mcp.WithDescription("Management API endpoint that can be used to check Prometheus is ready to serve traffic (i.e. respond to queries.)"),
217+
)
218+
219+
prometheusReloadTool = mcp.NewTool("reload",
220+
mcp.WithDescription("Management API endpoint that can be used to trigger a reload of the Prometheus configuration and rule files."),
221+
)
222+
223+
prometheusQuitTool = mcp.NewTool("quit",
224+
mcp.WithDescription("Management API endpoint that can be used to trigger a graceful shutdown of Prometheus."),
225+
)
226+
209227
// Tools for Prometheus documentation.
210228
prometheusDocsListTool = mcp.NewTool("docs_list",
211229
mcp.WithDescription("List of Official Prometheus Documentation Files."),
@@ -700,3 +718,43 @@ func prometheusDocsSearchToolHandler(ctx context.Context, request mcp.CallToolRe
700718

701719
return toolRes, nil
702720
}
721+
722+
func doPrometheusManagementApiCall(ctx context.Context, method, path string) (*mcp.CallToolResult, error) {
723+
client, err := getApiClientFromContext(ctx)
724+
if err != nil {
725+
return mcp.NewToolResultError("error getting prometheus api client from context: " + err.Error()), nil
726+
}
727+
ctx, cancel := context.WithTimeout(ctx, apiTimeout)
728+
defer cancel()
729+
730+
data, err := doHttpRequest(ctx, method, client.roundtripper, client.url, path, false)
731+
if err != nil {
732+
return mcp.NewToolResultError(fmt.Sprintf("error making Prometheus Management API call to %s: %s", path, err.Error())), nil
733+
}
734+
735+
return mcp.NewToolResultText(strings.Trim(data, "\\n\"")), nil
736+
}
737+
738+
const (
739+
mgmtApiEndpointPrefix = "/-/"
740+
mgmtApiHealthyEndpoint = mgmtApiEndpointPrefix + "healthy"
741+
mgmtApiReadyEndpoint = mgmtApiEndpointPrefix + "ready"
742+
mgmtApiReloadEndpoint = mgmtApiEndpointPrefix + "reload"
743+
mgmtApiQuitEndpoint = mgmtApiEndpointPrefix + "quit"
744+
)
745+
746+
func prometheusHealthyToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
747+
return doPrometheusManagementApiCall(ctx, http.MethodGet, mgmtApiHealthyEndpoint)
748+
}
749+
750+
func prometheusReadyToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
751+
return doPrometheusManagementApiCall(ctx, http.MethodGet, mgmtApiReadyEndpoint)
752+
}
753+
754+
func prometheusReloadToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
755+
return doPrometheusManagementApiCall(ctx, http.MethodPost, mgmtApiReloadEndpoint)
756+
}
757+
758+
func prometheusQuitToolHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
759+
return doPrometheusManagementApiCall(ctx, http.MethodPost, mgmtApiQuitEndpoint)
760+
}

0 commit comments

Comments
 (0)