feat: gate create_silence behind --enable-silences flag
The create_silence tool is a write operation that can suppress alerts. Disable it by default and require explicit opt-in via --enable-silences CLI flag (or enableSilences NixOS option) as a safety measure. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,8 @@ func AlertSummary(am *AlertmanagerClient) string {
|
||||
silenced := false
|
||||
inhibited := false
|
||||
alerts, err := am.ListAlerts(context.Background(), AlertFilters{
|
||||
Active: &active,
|
||||
Silenced: &silenced,
|
||||
Active: &active,
|
||||
Silenced: &silenced,
|
||||
Inhibited: &inhibited,
|
||||
})
|
||||
if err != nil || len(alerts) == 0 {
|
||||
@@ -54,8 +54,15 @@ func AlertSummary(am *AlertmanagerClient) string {
|
||||
len(alerts), strings.Join(parts, ", "))
|
||||
}
|
||||
|
||||
// HandlerOptions configures which handlers are registered.
|
||||
type HandlerOptions struct {
|
||||
// EnableSilences enables the create_silence tool, which is a write operation.
|
||||
// Disabled by default as a safety measure.
|
||||
EnableSilences bool
|
||||
}
|
||||
|
||||
// RegisterHandlers registers all monitoring tool handlers on the MCP server.
|
||||
func RegisterHandlers(server *mcp.Server, prom *PrometheusClient, am *AlertmanagerClient) {
|
||||
func RegisterHandlers(server *mcp.Server, prom *PrometheusClient, am *AlertmanagerClient, opts HandlerOptions) {
|
||||
server.RegisterTool(listAlertsTool(), makeListAlertsHandler(am))
|
||||
server.RegisterTool(getAlertTool(), makeGetAlertHandler(am))
|
||||
server.RegisterTool(searchMetricsTool(), makeSearchMetricsHandler(prom))
|
||||
@@ -63,7 +70,9 @@ func RegisterHandlers(server *mcp.Server, prom *PrometheusClient, am *Alertmanag
|
||||
server.RegisterTool(queryTool(), makeQueryHandler(prom))
|
||||
server.RegisterTool(listTargetsTool(), makeListTargetsHandler(prom))
|
||||
server.RegisterTool(listSilencesTool(), makeListSilencesHandler(am))
|
||||
server.RegisterTool(createSilenceTool(), makeCreateSilenceHandler(am))
|
||||
if opts.EnableSilences {
|
||||
server.RegisterTool(createSilenceTool(), makeCreateSilenceHandler(am))
|
||||
}
|
||||
}
|
||||
|
||||
// Tool definitions
|
||||
|
||||
@@ -26,7 +26,7 @@ func setupTestServer(t *testing.T, promHandler, amHandler http.HandlerFunc) (*mc
|
||||
|
||||
prom := NewPrometheusClient(promSrv.URL)
|
||||
am := NewAlertmanagerClient(amSrv.URL)
|
||||
RegisterHandlers(server, prom, am)
|
||||
RegisterHandlers(server, prom, am, HandlerOptions{EnableSilences: true})
|
||||
|
||||
cleanup := func() {
|
||||
promSrv.Close()
|
||||
@@ -304,7 +304,60 @@ func TestHandler_ToolCount(t *testing.T) {
|
||||
)
|
||||
defer cleanup()
|
||||
|
||||
// Send a tools/list request
|
||||
tools := listTools(t, server)
|
||||
if len(tools) != 8 {
|
||||
t.Errorf("expected 8 tools with silences enabled, got %d", len(tools))
|
||||
for _, tool := range tools {
|
||||
t.Logf(" tool: %s", tool.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify create_silence is present
|
||||
found := false
|
||||
for _, tool := range tools {
|
||||
if tool.Name == "create_silence" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected create_silence tool when silences enabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_ToolCountWithoutSilences(t *testing.T) {
|
||||
promSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
amSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer promSrv.Close()
|
||||
defer amSrv.Close()
|
||||
|
||||
logger := log.New(io.Discard, "", 0)
|
||||
config := mcp.DefaultMonitoringConfig()
|
||||
server := mcp.NewGenericServer(logger, config)
|
||||
|
||||
prom := NewPrometheusClient(promSrv.URL)
|
||||
am := NewAlertmanagerClient(amSrv.URL)
|
||||
RegisterHandlers(server, prom, am, HandlerOptions{EnableSilences: false})
|
||||
|
||||
tools := listTools(t, server)
|
||||
if len(tools) != 7 {
|
||||
t.Errorf("expected 7 tools without silences, got %d", len(tools))
|
||||
for _, tool := range tools {
|
||||
t.Logf(" tool: %s", tool.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify create_silence is NOT present
|
||||
for _, tool := range tools {
|
||||
if tool.Name == "create_silence" {
|
||||
t.Error("expected create_silence tool to be absent when silences disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listTools(t *testing.T, server *mcp.Server) []mcp.Tool {
|
||||
t.Helper()
|
||||
|
||||
req := &mcp.Request{
|
||||
JSONRPC: "2.0",
|
||||
ID: 1,
|
||||
@@ -328,12 +381,7 @@ func TestHandler_ToolCount(t *testing.T) {
|
||||
t.Fatalf("failed to unmarshal result: %v", err)
|
||||
}
|
||||
|
||||
if len(listResult.Tools) != 8 {
|
||||
t.Errorf("expected 8 tools, got %d", len(listResult.Tools))
|
||||
for _, tool := range listResult.Tools {
|
||||
t.Logf(" tool: %s", tool.Name)
|
||||
}
|
||||
}
|
||||
return listResult.Tools
|
||||
}
|
||||
|
||||
// callTool is a test helper that calls a tool through the MCP server.
|
||||
|
||||
Reference in New Issue
Block a user