Skip to content

Commit 162f9bd

Browse files
committed
dynamic toolsets parsing and error handling
1 parent d0152a1 commit 162f9bd

File tree

1 file changed

+24
-15
lines changed

1 file changed

+24
-15
lines changed

cmd/mcp-grafana/main.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,18 @@ func (dt *disabledTools) addTools(s *server.MCPServer) {
115115
func (dt *disabledTools) addToolsDynamically(s *server.MCPServer) *mcpgrafana.DynamicToolManager {
116116
dtm := mcpgrafana.NewDynamicToolManager(s)
117117

118-
enabledTools := strings.Split(dt.enabledTools, ",")
118+
// Split and clean up the enabled tools list (trim whitespace, filter empty strings)
119+
rawTools := strings.Split(dt.enabledTools, ",")
120+
enabledTools := make([]string, 0, len(rawTools))
121+
for _, tool := range rawTools {
122+
trimmed := strings.TrimSpace(tool)
123+
if trimmed != "" {
124+
enabledTools = append(enabledTools, trimmed)
125+
}
126+
}
127+
enableWriteTools := !dt.write
119128

120129
isEnabled := func(toolName string) bool {
121-
// If enabledTools is empty string, no tools should be available
122-
if dt.enabledTools == "" {
123-
return false
124-
}
125130
return slices.Contains(enabledTools, toolName)
126131
}
127132

@@ -134,18 +139,20 @@ func (dt *disabledTools) addToolsDynamically(s *server.MCPServer) *mcpgrafana.Dy
134139
}{
135140
{"search", "Tools for searching dashboards, folders, and other Grafana resources", []string{"search_dashboards", "search_folders"}, tools.AddSearchTools},
136141
{"datasource", "Tools for listing and fetching datasource details", []string{"list_datasources", "get_datasource_by_uid", "get_datasource_by_name"}, tools.AddDatasourceTools},
137-
{"incident", "Tools for managing Grafana Incident (create, update, search incidents)", []string{"list_incidents", "create_incident", "add_activity_to_incident", "get_incident"}, tools.AddIncidentTools},
142+
{"incident", "Tools for managing Grafana Incident (create, update, search incidents)", []string{"list_incidents", "create_incident", "add_activity_to_incident", "get_incident"}, func(mcp *server.MCPServer) { tools.AddIncidentTools(mcp, enableWriteTools) }},
138143
{"prometheus", "Tools for querying Prometheus metrics and metadata", []string{"list_prometheus_metric_metadata", "query_prometheus", "list_prometheus_metric_names", "list_prometheus_label_names", "list_prometheus_label_values"}, tools.AddPrometheusTools},
139144
{"loki", "Tools for querying Loki logs and labels", []string{"list_loki_label_names", "list_loki_label_values", "query_loki_stats", "query_loki_logs"}, tools.AddLokiTools},
140-
{"alerting", "Tools for managing alert rules and notification contact points", []string{"list_alert_rules", "get_alert_rule_by_uid", "list_contact_points", "create_alert_rule", "update_alert_rule", "delete_alert_rule"}, tools.AddAlertingTools},
141-
{"dashboard", "Tools for managing Grafana dashboards (get, update, extract queries)", []string{"get_dashboard_by_uid", "update_dashboard", "get_dashboard_panel_queries", "get_dashboard_property", "get_dashboard_summary"}, tools.AddDashboardTools},
142-
{"folder", "Tools for managing Grafana folders", []string{"create_folder"}, tools.AddFolderTools},
145+
{"alerting", "Tools for managing alert rules and notification contact points", []string{"list_alert_rules", "get_alert_rule_by_uid", "list_contact_points", "create_alert_rule", "update_alert_rule", "delete_alert_rule"}, func(mcp *server.MCPServer) { tools.AddAlertingTools(mcp, enableWriteTools) }},
146+
{"dashboard", "Tools for managing Grafana dashboards (get, update, extract queries)", []string{"get_dashboard_by_uid", "update_dashboard", "get_dashboard_panel_queries", "get_dashboard_property", "get_dashboard_summary"}, func(mcp *server.MCPServer) { tools.AddDashboardTools(mcp, enableWriteTools) }},
147+
{"folder", "Tools for managing Grafana folders", []string{"create_folder"}, func(mcp *server.MCPServer) { tools.AddFolderTools(mcp, enableWriteTools) }},
143148
{"oncall", "Tools for managing OnCall schedules, shifts, teams, and users", []string{"list_oncall_schedules", "get_oncall_shift", "get_current_oncall_users", "list_oncall_teams", "list_oncall_users", "list_alert_groups", "get_alert_group"}, tools.AddOnCallTools},
144149
{"asserts", "Tools for Grafana Asserts cloud functionality", []string{"get_assertions"}, tools.AddAssertsTools},
145-
{"sift", "Tools for Sift investigations (analyze logs/traces, find errors, detect slow requests)", []string{"get_sift_investigation", "get_sift_analysis", "list_sift_investigations", "find_error_pattern_logs", "find_slow_requests"}, tools.AddSiftTools},
150+
{"sift", "Tools for Sift investigations (analyze logs/traces, find errors, detect slow requests)", []string{"get_sift_investigation", "get_sift_analysis", "list_sift_investigations", "find_error_pattern_logs", "find_slow_requests"}, func(mcp *server.MCPServer) { tools.AddSiftTools(mcp, enableWriteTools) }},
146151
{"admin", "Tools for administrative tasks (list teams, manage users)", []string{"list_teams", "list_users_by_org"}, tools.AddAdminTools},
147152
{"pyroscope", "Tools for profiling applications with Pyroscope", []string{"list_pyroscope_label_names", "list_pyroscope_label_values", "list_pyroscope_profile_types", "fetch_pyroscope_profile"}, tools.AddPyroscopeTools},
148153
{"navigation", "Tools for generating deeplink URLs to Grafana resources", []string{"generate_deeplink"}, tools.AddNavigationTools},
154+
{"annotations", "Tools for managing annotations", []string{"get_annotations", "create_annotation", "create_graphite_annotation", "update_annotation", "patch_annotation", "get_annotation_tags"}, func(mcp *server.MCPServer) { tools.AddAnnotationTools(mcp, enableWriteTools) }},
155+
{"rendering", "Tools for rendering dashboard panels as images", []string{"get_panel_image"}, tools.AddRenderingTools},
149156
}
150157

151158
// Only register toolsets that are enabled
@@ -270,11 +277,6 @@ Note that some of these capabilities may be disabled. Do not try to use features
270277
stm = mcpgrafana.NewToolManager(sm, s, mcpgrafana.WithProxiedTools(!dt.proxied))
271278

272279
if dt.dynamicTools {
273-
// Validate that enabled-tools is not empty when using dynamic toolsets
274-
// An empty list would result in a non-functional server with no toolsets to enable
275-
if dt.enabledTools == "" {
276-
return nil, nil, errors.New("--enabled-tools cannot be empty when using --dynamic-toolsets (would result in no toolsets available)")
277-
}
278280
// For dynamic toolsets, start with only discovery tools
279281
// Tools will be added dynamically when toolsets are enabled
280282
dt.addToolsDynamically(s)
@@ -349,6 +351,13 @@ func handleHealthz(w http.ResponseWriter, r *http.Request) {
349351

350352
func run(transport, addr, basePath, endpointPath string, logLevel slog.Level, dt disabledTools, gc mcpgrafana.GrafanaConfig, tls tlsConfig) error {
351353
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: logLevel})))
354+
355+
// Validate that enabled-tools is not empty when using dynamic toolsets
356+
// An empty list would result in a non-functional server with no toolsets to enable
357+
if dt.dynamicTools && dt.enabledTools == "" {
358+
return errors.New("--enabled-tools cannot be empty when using --dynamic-toolsets (would result in no toolsets available)")
359+
}
360+
352361
s, tm := newServer(transport, dt)
353362

354363
// Create a context that will be cancelled on shutdown

0 commit comments

Comments
 (0)