Skip to content

Commit b66737e

Browse files
authored
Merge pull request #58 from tjhop/feat/prometheus-backends-and-thanos-support
feat: allow customizing tools for select backends, add thanos support
2 parents 63b77fe + ffde197 commit b66737e

File tree

4 files changed

+109
-2
lines changed

4 files changed

+109
-2
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,33 @@ Would result in the following tools being loaded:
109109
- `runtime_info`
110110
- `series`
111111

112+
#### Prometheus Compatible Backends
113+
114+
There are many Prometheus compatible backends that can be used to extend prometheus in a variety of ways, often with the goals of offering long term storage or query aggregation from multiple prometheus instances.
115+
Some examples can be found in the [Remote Storage](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage) of prometheus' docs.
116+
117+
Many of those services also offer a "prometheus compatible" API that can be used to query/interact with the data using native promQL.
118+
In general, this MCP server should at a minimum work for other prometheus API compatible services to execute queries and interact with the series/labels/metadata endpoints for metric and label discovery.
119+
Beyond that, there may be API differences as the different systems implement different parts/extensions of the API for their needs.
120+
121+
Examples:
122+
- Thanos does not use a centralized config, so the config endpoint is not implemented and thus the config tool fails.
123+
- Mimir and Cortex implement extra endpoints to manage/add/remove rules
124+
125+
To workaround this and provide a better experience on some of the commonly used Prometheus compatible systems, this project may add direct support for select systems to provide different/more tools.
126+
Choosing a specific prometheus backend implementation can be done with the [`--prometheus.backend` flag](#command-line-flags).
127+
The list of available backend implementations on a given release of the MCP server can be found in the output of the [`--help` flag](#command-line-flags).
128+
Qualifications and support criteria are still under consideration, please open an issue to request support/features for a specific backend for further discussion.
129+
130+
##### Prometheus Backend Implementation Differences
131+
132+
| Backend | Tool | Add/Remove/Change | Notes |
133+
| --- | --- | --- | --- |
134+
| `prometheus` | n/a | none | Standard prometheus tools. Functionally equivalent to `--mcp.tools="all"`. The default MCP server toolset. |
135+
| [`thanos`](https://thanos.io/) | `alertmanagers` | remove | Thanos does not implement the endpoint and the tool returns a `404`. |
136+
| [`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`. |
137+
| [`thanos`](https://thanos.io/) | `wal_replay_status` | remove | Thanos does not implement the endpoint and the tool returns a `404`. |
138+
112139
### Resources
113140

114141
| Resource Name | Resource URI | Description |

cmd/prometheus-mcp-server/main.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os"
1313
"os/signal"
1414
"runtime"
15+
"strings"
1516
"syscall"
1617
"time"
1718

@@ -51,6 +52,12 @@ var (
5152
" Please see project README for more information and the full list of tools.",
5253
).Default("all").Strings()
5354

55+
flagPrometheusBackend = kingpin.Flag(
56+
"prometheus.backend",
57+
"Customize the toolset for a specific Prometheus API compatible backend."+
58+
" Supported backends include: "+strings.Join(mcp.PrometheusBackends, ","),
59+
).String()
60+
5461
flagPrometheusUrl = kingpin.Flag(
5562
"prometheus.url",
5663
"URL of the Prometheus instance to connect to",
@@ -134,7 +141,7 @@ func main() {
134141
docsFs = docs
135142
}
136143

137-
mcpServer := mcp.NewServer(ctx, logger, *flagPrometheusUrl, rt, *flagEnableTsdbAdminTools, *flagMcpTools, docsFs)
144+
mcpServer := mcp.NewServer(ctx, logger, *flagPrometheusUrl, *flagPrometheusBackend, rt, *flagEnableTsdbAdminTools, *flagMcpTools, docsFs)
138145
srv := setupServer(logger)
139146

140147
var g run.Group

pkg/mcp/server.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,14 @@ func (m *telemetryMiddleware) ResourceMiddleware(next server.ResourceHandlerFunc
213213
}
214214
}
215215

216-
func NewServer(ctx context.Context, logger *slog.Logger, promUrl string, promRt http.RoundTripper, enableTsdbAdminTools bool, enabledTools []string, docs fs.FS) *server.MCPServer {
216+
func NewServer(ctx context.Context, logger *slog.Logger,
217+
promUrl string,
218+
prometheusBackend string,
219+
promRt http.RoundTripper,
220+
enableTsdbAdminTools bool,
221+
enabledTools []string,
222+
docs fs.FS,
223+
) *server.MCPServer {
217224
hooks := &server.Hooks{}
218225

219226
hooks.AddAfterInitialize(func(ctx context.Context, id any, message *mcp.InitializeRequest, result *mcp.InitializeResult) {
@@ -319,6 +326,31 @@ func NewServer(ctx context.Context, logger *slog.Logger, promUrl string, promRt
319326
}
320327
}
321328

329+
// If a specific prometheus compatible backend was provided, that
330+
// overrides defined tools, since dynamic tool registration is specific
331+
// to prometheus tools and they may not be compatible with all
332+
// prometheus backends.
333+
backend := strings.ToLower(prometheusBackend)
334+
switch backend {
335+
case "": // If no backend entered, keep loaded toolset.
336+
case "prometheus":
337+
logger.Info("Setting tools based on provided prometheus backend", "backend", backend)
338+
var backendToolset []server.ServerTool
339+
for _, tool := range prometheusToolset {
340+
backendToolset = append(backendToolset, tool)
341+
}
342+
toolset = backendToolset
343+
case "thanos":
344+
logger.Info("Setting tools based on provided prometheus backend", "backend", backend)
345+
var backendToolset []server.ServerTool
346+
for _, tool := range thanosToolset {
347+
backendToolset = append(backendToolset, tool)
348+
}
349+
toolset = backendToolset
350+
default:
351+
logger.Warn("Prometheus backend does not have custom tool support, keeping the existing loaded toolset", "backend", backend, "toolset", enabledTools)
352+
}
353+
322354
// Add tools.
323355
mcpServer.AddTools(toolset...)
324356

pkg/mcp/tools.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var (
3636
"snapshot",
3737
}
3838

39+
// prometheusToolset contains all the tools to interact with standard prometheus through the HTTP API.
3940
prometheusToolset = map[string]server.ServerTool{
4041
"alertmanagers": {Tool: prometheusAlertmanagersTool, Handler: prometheusAlertmanagersToolHandler},
4142
"build_info": {Tool: prometheusBuildinfoTool, Handler: prometheusBuildinfoToolHandler},
@@ -63,6 +64,46 @@ var (
6364
"wal_replay_status": {Tool: prometheusWalReplayTool, Handler: prometheusWalReplayToolHandler},
6465
}
6566

67+
// thanosToolset contains all the tools to interact with thanos as a
68+
// prometheus HTTP API compatible backend.
69+
//
70+
// Currently, the only difference between thanosToolset and
71+
// prometheusToolset is that thanosToolset has the following tools
72+
// removed because they are not implemented in Thanos and return 404s:
73+
// [alertmanagers, config, wal_replay_status].
74+
thanosToolset = map[string]server.ServerTool{
75+
"build_info": {Tool: prometheusBuildinfoTool, Handler: prometheusBuildinfoToolHandler},
76+
"clean_tombstones": {Tool: prometheusCleanTombstonesTool, Handler: prometheusCleanTombstonesToolHandler},
77+
"delete_series": {Tool: prometheusDeleteSeriesTool, Handler: prometheusDeleteSeriesToolHandler},
78+
"docs_list": {Tool: prometheusDocsListTool, Handler: prometheusDocsListToolHandler},
79+
"docs_read": {Tool: prometheusDocsReadTool, Handler: prometheusDocsReadToolHandler},
80+
"docs_search": {Tool: prometheusDocsSearchTool, Handler: prometheusDocsSearchToolHandler},
81+
"exemplar_query": {Tool: prometheusExemplarQueryTool, Handler: prometheusExemplarQueryToolHandler},
82+
"flags": {Tool: prometheusFlagsTool, Handler: prometheusFlagsToolHandler},
83+
"label_names": {Tool: prometheusLabelNamesTool, Handler: prometheusLabelNamesToolHandler},
84+
"label_values": {Tool: prometheusLabelValuesTool, Handler: prometheusLabelValuesToolHandler},
85+
"list_alerts": {Tool: prometheusListAlertsTool, Handler: prometheusListAlertsToolHandler},
86+
"list_rules": {Tool: prometheusRulesTool, Handler: prometheusRulesToolHandler},
87+
"metric_metadata": {Tool: prometheusMetricMetadataTool, Handler: prometheusMetricMetadataToolHandler},
88+
"query": {Tool: prometheusQueryTool, Handler: prometheusQueryToolHandler},
89+
"range_query": {Tool: prometheusRangeQueryTool, Handler: prometheusRangeQueryToolHandler},
90+
"runtime_info": {Tool: prometheusRuntimeinfoTool, Handler: prometheusRuntimeinfoToolHandler},
91+
"series": {Tool: prometheusSeriesTool, Handler: prometheusSeriesToolHandler},
92+
"snapshot": {Tool: prometheusSnapshotTool, Handler: prometheusSnapshotToolHandler},
93+
"targets_metadata": {Tool: prometheusTargetsMetadataTool, Handler: prometheusTargetsMetadataToolHandler},
94+
"list_targets": {Tool: prometheusTargetsTool, Handler: prometheusTargetsToolHandler},
95+
"tsdb_stats": {Tool: prometheusTsdbStatsTool, Handler: prometheusTsdbStatsToolHandler},
96+
}
97+
98+
// PrometheusBackends is a list of directly supported Prometheus API
99+
// compatible backends. Backends other than prometheus itself may
100+
// expose a different set of tools more tailored to the backend and/or
101+
// change functionality of existing tools.
102+
PrometheusBackends = []string{
103+
"prometheus",
104+
"thanos",
105+
}
106+
66107
// Tools Definitions.
67108

68109
// Tools for Prometheus API.

0 commit comments

Comments
 (0)