Skip to content

Commit 75059c5

Browse files
sd2kgitdoluquita
andauthored
feat: add more annotations to tools (#132)
* feat: add title annotations to all tools This commit adds the ability to customise tools using `mcp.ToolOption`s, then updates all calls to `MustTool` to include a title annotation, which can be displayed in any UIs instead of the non-human-friendly name. Co-authored-by: Luccas Quadros <[email protected]> * feat: add more annotations to tools Add the read-only, idempotent, and destructive hint annotations to tools where appropriate. Co-authored-by: Luccas Quadros <[email protected]> --------- Co-authored-by: Luccas Quadros <[email protected]>
1 parent 01b48ca commit 75059c5

File tree

10 files changed

+61
-0
lines changed

10 files changed

+61
-0
lines changed

tools/alerting.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ var ListAlertRules = mcpgrafana.MustTool(
150150
"Lists Grafana alert rules, returning a summary including UID, title, current state (e.g., 'pending', 'firing', 'inactive'), and labels. Supports filtering by labels using selectors and pagination. Example label selector: `[{'name': 'severity', 'type': '=', 'value': 'critical'}]`. Inactive state means the alert state is normal, not firing",
151151
listAlertRules,
152152
mcp.WithTitleAnnotation("List alert rules"),
153+
mcp.WithIdempotentHintAnnotation(true),
154+
mcp.WithReadOnlyHintAnnotation(true),
153155
)
154156

155157
type GetAlertRuleByUIDParams struct {
@@ -182,6 +184,8 @@ var GetAlertRuleByUID = mcpgrafana.MustTool(
182184
"Retrieves the full configuration and detailed status of a specific Grafana alert rule identified by its unique ID (UID). The response includes fields like title, condition, query data, folder UID, rule group, state settings (no data, error), evaluation interval, annotations, and labels.",
183185
getAlertRuleByUID,
184186
mcp.WithTitleAnnotation("Get alert rule details"),
187+
mcp.WithIdempotentHintAnnotation(true),
188+
mcp.WithReadOnlyHintAnnotation(true),
185189
)
186190

187191
type ListContactPointsParams struct {
@@ -256,6 +260,8 @@ var ListContactPoints = mcpgrafana.MustTool(
256260
"Lists Grafana notification contact points, returning a summary including UID, name, and type for each. Supports filtering by name - exact match - and limiting the number of results.",
257261
listContactPoints,
258262
mcp.WithTitleAnnotation("List notification contact points"),
263+
mcp.WithIdempotentHintAnnotation(true),
264+
mcp.WithReadOnlyHintAnnotation(true),
259265
)
260266

261267
func AddAlertingTools(mcp *server.MCPServer) {

tools/asserts.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ var GetAssertions = mcpgrafana.MustTool(
136136
"Get assertion summary for a given entity with its type, name, env, site, namespace, and a time range",
137137
getAssertions,
138138
mcp.WithTitleAnnotation("Get assertions summary"),
139+
mcp.WithIdempotentHintAnnotation(true),
140+
mcp.WithReadOnlyHintAnnotation(true),
139141
)
140142

141143
func AddAssertsTools(mcp *server.MCPServer) {

tools/dashboard.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ var GetDashboardByUID = mcpgrafana.MustTool(
5656
"Retrieves the complete dashboard, including panels, variables, and settings, for a specific dashboard identified by its UID.",
5757
getDashboardByUID,
5858
mcp.WithTitleAnnotation("Get dashboard details"),
59+
mcp.WithIdempotentHintAnnotation(true),
60+
mcp.WithReadOnlyHintAnnotation(true),
5961
)
6062

6163
var UpdateDashboard = mcpgrafana.MustTool(
6264
"update_dashboard",
6365
"Create or update a dashboard",
6466
updateDashboard,
6567
mcp.WithTitleAnnotation("Create or update dashboard"),
68+
mcp.WithDestructiveHintAnnotation(true),
6669
)
6770

6871
type DashboardPanelQueriesParams struct {
@@ -144,6 +147,8 @@ var GetDashboardPanelQueries = mcpgrafana.MustTool(
144147
"Get the title, query string, and datasource information for each panel in a dashboard. The datasource is an object with fields `uid` (which may be a concrete UID or a template variable like \"$datasource\") and `type`. If the datasource UID is a template variable, it won't be usable directly for queries. Returns an array of objects, each representing a panel, with fields: title, query, and datasource (an object with uid and type).",
145148
GetDashboardPanelQueriesTool,
146149
mcp.WithTitleAnnotation("Get dashboard panel queries"),
150+
mcp.WithIdempotentHintAnnotation(true),
151+
mcp.WithReadOnlyHintAnnotation(true),
147152
)
148153

149154
func AddDashboardTools(mcp *server.MCPServer) {

tools/datasources.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ var ListDatasources = mcpgrafana.MustTool(
6969
"List available Grafana datasources. Optionally filter by datasource type (e.g., 'prometheus', 'loki'). Returns a summary list including ID, UID, name, type, and default status.",
7070
listDatasources,
7171
mcp.WithTitleAnnotation("List datasources"),
72+
mcp.WithIdempotentHintAnnotation(true),
73+
mcp.WithReadOnlyHintAnnotation(true),
7274
)
7375

7476
type GetDatasourceByUIDParams struct {
@@ -93,6 +95,8 @@ var GetDatasourceByUID = mcpgrafana.MustTool(
9395
"Retrieves detailed information about a specific datasource using its UID. Returns the full datasource model, including name, type, URL, access settings, JSON data, and secure JSON field status.",
9496
getDatasourceByUID,
9597
mcp.WithTitleAnnotation("Get datasource by UID"),
98+
mcp.WithIdempotentHintAnnotation(true),
99+
mcp.WithReadOnlyHintAnnotation(true),
96100
)
97101

98102
type GetDatasourceByNameParams struct {
@@ -113,6 +117,8 @@ var GetDatasourceByName = mcpgrafana.MustTool(
113117
"Retrieves detailed information about a specific datasource using its name. Returns the full datasource model, including UID, type, URL, access settings, JSON data, and secure JSON field status.",
114118
getDatasourceByName,
115119
mcp.WithTitleAnnotation("Get datasource by name"),
120+
mcp.WithIdempotentHintAnnotation(true),
121+
mcp.WithReadOnlyHintAnnotation(true),
116122
)
117123

118124
func AddDatasourceTools(mcp *server.MCPServer) {

tools/incident.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ var ListIncidents = mcpgrafana.MustTool(
5151
"List Grafana incidents. Allows filtering by status ('active', 'resolved') and optionally including drill incidents. Returns a preview list with basic details.",
5252
listIncidents,
5353
mcp.WithTitleAnnotation("List incidents"),
54+
mcp.WithIdempotentHintAnnotation(true),
55+
mcp.WithReadOnlyHintAnnotation(true),
5456
)
5557

5658
type CreateIncidentParams struct {
@@ -148,4 +150,6 @@ var GetIncident = mcpgrafana.MustTool(
148150
"Get a single incident by ID. Returns the full incident details including title, status, severity, labels, timestamps, and other metadata.",
149151
getIncident,
150152
mcp.WithTitleAnnotation("Get incident details"),
153+
mcp.WithIdempotentHintAnnotation(true),
154+
mcp.WithReadOnlyHintAnnotation(true),
151155
)

tools/loki.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ var ListLokiLabelNames = mcpgrafana.MustTool(
224224
"Lists all available label names (keys) found in logs within a specified Loki datasource and time range. Returns a list of unique label strings (e.g., `[\"app\", \"env\", \"pod\"]`). If the time range is not provided, it defaults to the last hour.",
225225
listLokiLabelNames,
226226
mcp.WithTitleAnnotation("List Loki label names"),
227+
mcp.WithIdempotentHintAnnotation(true),
228+
mcp.WithReadOnlyHintAnnotation(true),
227229
)
228230

229231
// ListLokiLabelValuesParams defines the parameters for listing Loki label values
@@ -263,6 +265,8 @@ var ListLokiLabelValues = mcpgrafana.MustTool(
263265
"Retrieves all unique values associated with a specific `labelName` within a Loki datasource and time range. Returns a list of string values (e.g., for `labelName=\"env\"`, might return `[\"prod\", \"staging\", \"dev\"]`). Useful for discovering filter options. Defaults to the last hour if the time range is omitted.",
264266
listLokiLabelValues,
265267
mcp.WithTitleAnnotation("List Loki label values"),
268+
mcp.WithIdempotentHintAnnotation(true),
269+
mcp.WithReadOnlyHintAnnotation(true),
266270
)
267271

268272
// LogStream represents a stream of log entries from Loki
@@ -471,6 +475,8 @@ var QueryLokiLogs = mcpgrafana.MustTool(
471475
"Executes a LogQL query against a Loki datasource to retrieve log entries or metric values. Returns a list of results, each containing a timestamp, labels, and either a log line (`line`) or a numeric metric value (`value`). Defaults to the last hour, a limit of 10 entries, and 'backward' direction (newest first). Supports full LogQL syntax for log and metric queries (e.g., `{app=\"foo\"} |= \"error\"`, `rate({app=\"bar\"}[1m])`). Prefer using `query_loki_stats` first to check stream size and `list_loki_label_names` and `list_loki_label_values` to verify labels exist.",
472476
queryLokiLogs,
473477
mcp.WithTitleAnnotation("Query Loki logs"),
478+
mcp.WithIdempotentHintAnnotation(true),
479+
mcp.WithReadOnlyHintAnnotation(true),
474480
)
475481

476482
// fetchStats is a method to fetch stats data from Loki API
@@ -529,6 +535,8 @@ var QueryLokiStats = mcpgrafana.MustTool(
529535
"Retrieves statistics about log streams matching a given LogQL *selector* within a Loki datasource and time range. Returns an object containing the count of streams, chunks, entries, and total bytes (e.g., `{\"streams\": 5, \"chunks\": 50, \"entries\": 10000, \"bytes\": 512000}`). The `logql` parameter **must** be a simple label selector (e.g., `{app=\"nginx\", env=\"prod\"}`) and does not support line filters, parsers, or aggregations. Defaults to the last hour if the time range is omitted.",
530536
queryLokiStats,
531537
mcp.WithTitleAnnotation("Get Loki log statistics"),
538+
mcp.WithIdempotentHintAnnotation(true),
539+
mcp.WithReadOnlyHintAnnotation(true),
532540
)
533541

534542
// AddLokiTools registers all Loki tools with the MCP server

tools/oncall.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ var ListOnCallSchedules = mcpgrafana.MustTool(
194194
"List Grafana OnCall schedules, optionally filtering by team ID. If a specific schedule ID is provided, retrieves details for only that schedule. Returns a list of schedule summaries including ID, name, team ID, timezone, and shift IDs. Supports pagination.",
195195
listOnCallSchedules,
196196
mcp.WithTitleAnnotation("List OnCall schedules"),
197+
mcp.WithIdempotentHintAnnotation(true),
198+
mcp.WithReadOnlyHintAnnotation(true),
197199
)
198200

199201
type GetOnCallShiftParams struct {
@@ -219,6 +221,8 @@ var GetOnCallShift = mcpgrafana.MustTool(
219221
"Get detailed information for a specific Grafana OnCall shift using its ID. A shift represents a designated time period within a schedule when users are actively on-call. Returns the full shift details.",
220222
getOnCallShift,
221223
mcp.WithTitleAnnotation("Get OnCall shift"),
224+
mcp.WithIdempotentHintAnnotation(true),
225+
mcp.WithReadOnlyHintAnnotation(true),
222226
)
223227

224228
// CurrentOnCallUsers represents the currently on-call users for a schedule
@@ -280,6 +284,8 @@ var GetCurrentOnCallUsers = mcpgrafana.MustTool(
280284
"Get the list of users currently on-call for a specific Grafana OnCall schedule ID. Returns the schedule ID, name, and a list of detailed user objects for those currently on call.",
281285
getCurrentOnCallUsers,
282286
mcp.WithTitleAnnotation("Get current on-call users"),
287+
mcp.WithIdempotentHintAnnotation(true),
288+
mcp.WithReadOnlyHintAnnotation(true),
283289
)
284290

285291
type ListOnCallTeamsParams struct {
@@ -310,6 +316,8 @@ var ListOnCallTeams = mcpgrafana.MustTool(
310316
"List teams configured in Grafana OnCall. Returns a list of team objects with their details. Supports pagination.",
311317
listOnCallTeams,
312318
mcp.WithTitleAnnotation("List OnCall teams"),
319+
mcp.WithIdempotentHintAnnotation(true),
320+
mcp.WithReadOnlyHintAnnotation(true),
313321
)
314322

315323
type ListOnCallUsersParams struct {
@@ -354,6 +362,8 @@ var ListOnCallUsers = mcpgrafana.MustTool(
354362
"List users from Grafana OnCall. Can retrieve all users, a specific user by ID, or filter by username. Returns a list of user objects with their details. Supports pagination.",
355363
listOnCallUsers,
356364
mcp.WithTitleAnnotation("List OnCall users"),
365+
mcp.WithIdempotentHintAnnotation(true),
366+
mcp.WithReadOnlyHintAnnotation(true),
357367
)
358368

359369
func AddOnCallTools(mcp *server.MCPServer) {

tools/prometheus.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ var ListPrometheusMetricMetadata = mcpgrafana.MustTool(
9898
"List Prometheus metric metadata. Returns metadata about metrics currently scraped from targets. Note: This endpoint is experimental.",
9999
listPrometheusMetricMetadata,
100100
mcp.WithTitleAnnotation("List Prometheus metric metadata"),
101+
mcp.WithIdempotentHintAnnotation(true),
102+
mcp.WithReadOnlyHintAnnotation(true),
101103
)
102104

103105
type QueryPrometheusParams struct {
@@ -161,6 +163,8 @@ var QueryPrometheus = mcpgrafana.MustTool(
161163
"Query Prometheus using a PromQL expression. Supports both instant queries (at a single point in time) and range queries (over a time range).",
162164
queryPrometheus,
163165
mcp.WithTitleAnnotation("Query Prometheus metrics"),
166+
mcp.WithIdempotentHintAnnotation(true),
167+
mcp.WithReadOnlyHintAnnotation(true),
164168
)
165169

166170
type ListPrometheusMetricNamesParams struct {
@@ -229,6 +233,8 @@ var ListPrometheusMetricNames = mcpgrafana.MustTool(
229233
"List metric names in a Prometheus datasource. Retrieves all metric names and then filters them locally using the provided regex. Supports pagination.",
230234
listPrometheusMetricNames,
231235
mcp.WithTitleAnnotation("List Prometheus metric names"),
236+
mcp.WithIdempotentHintAnnotation(true),
237+
mcp.WithReadOnlyHintAnnotation(true),
232238
)
233239

234240
type LabelMatcher struct {
@@ -332,6 +338,8 @@ var ListPrometheusLabelNames = mcpgrafana.MustTool(
332338
"List label names in a Prometheus datasource. Allows filtering by series selectors and time range.",
333339
listPrometheusLabelNames,
334340
mcp.WithTitleAnnotation("List Prometheus label names"),
341+
mcp.WithIdempotentHintAnnotation(true),
342+
mcp.WithReadOnlyHintAnnotation(true),
335343
)
336344

337345
type ListPrometheusLabelValuesParams struct {
@@ -389,6 +397,8 @@ var ListPrometheusLabelValues = mcpgrafana.MustTool(
389397
"Get the values for a specific label name in Prometheus. Allows filtering by series selectors and time range.",
390398
listPrometheusLabelValues,
391399
mcp.WithTitleAnnotation("List Prometheus label values"),
400+
mcp.WithIdempotentHintAnnotation(true),
401+
mcp.WithReadOnlyHintAnnotation(true),
392402
)
393403

394404
func AddPrometheusTools(mcp *server.MCPServer) {

tools/search.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ var SearchDashboards = mcpgrafana.MustTool(
3737
"Search for Grafana dashboards by a query string. Returns a list of matching dashboards with details like title, UID, folder, tags, and URL.",
3838
searchDashboards,
3939
mcp.WithTitleAnnotation("Search dashboards"),
40+
mcp.WithIdempotentHintAnnotation(true),
41+
mcp.WithReadOnlyHintAnnotation(true),
4042
)
4143

4244
func AddSearchTools(mcp *server.MCPServer) {

tools/sift.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ var GetSiftInvestigation = mcpgrafana.MustTool(
178178
"Retrieves an existing Sift investigation by its UUID. The ID should be provided as a string in UUID format (e.g. '02adab7c-bf5b-45f2-9459-d71a2c29e11b').",
179179
getSiftInvestigation,
180180
mcp.WithTitleAnnotation("Get Sift investigation"),
181+
mcp.WithIdempotentHintAnnotation(true),
182+
mcp.WithReadOnlyHintAnnotation(true),
181183
)
182184

183185
// GetSiftAnalysisParams defines the parameters for retrieving a specific analysis
@@ -218,6 +220,8 @@ var GetSiftAnalysis = mcpgrafana.MustTool(
218220
"Retrieves a specific analysis from an investigation by its UUID. The investigation ID and analysis ID should be provided as strings in UUID format.",
219221
getSiftAnalysis,
220222
mcp.WithTitleAnnotation("Get Sift analysis"),
223+
mcp.WithIdempotentHintAnnotation(true),
224+
mcp.WithReadOnlyHintAnnotation(true),
221225
)
222226

223227
// ListSiftInvestigationsParams defines the parameters for retrieving investigations
@@ -251,6 +255,8 @@ var ListSiftInvestigations = mcpgrafana.MustTool(
251255
"Retrieves a list of Sift investigations with an optional limit. If no limit is specified, defaults to 10 investigations.",
252256
listSiftInvestigations,
253257
mcp.WithTitleAnnotation("List Sift investigations"),
258+
mcp.WithIdempotentHintAnnotation(true),
259+
mcp.WithReadOnlyHintAnnotation(true),
254260
)
255261

256262
// FindErrorPatternLogsParams defines the parameters for running an ErrorPatternLogs check
@@ -336,6 +342,7 @@ var FindErrorPatternLogs = mcpgrafana.MustTool(
336342
"Searches Loki logs for elevated error patterns compared to the last day's average, waits for the analysis to complete, and returns the results including any patterns found.",
337343
findErrorPatternLogs,
338344
mcp.WithTitleAnnotation("Find error patterns in logs"),
345+
mcp.WithReadOnlyHintAnnotation(true),
339346
)
340347

341348
// FindSlowRequestsParams defines the parameters for running an SlowRequests check
@@ -401,6 +408,7 @@ var FindSlowRequests = mcpgrafana.MustTool(
401408
"Searches relevant Tempo datasources for slow requests, waits for the analysis to complete, and returns the results.",
402409
findSlowRequests,
403410
mcp.WithTitleAnnotation("Find slow requests"),
411+
mcp.WithReadOnlyHintAnnotation(true),
404412
)
405413

406414
// AddSiftTools registers all Sift tools with the MCP server

0 commit comments

Comments
 (0)