Skip to content

Commit 37e0754

Browse files
sd2kgitdoluquita
andauthored
feat: add title annotations to all tools (#130)
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]>
1 parent e5c2b5b commit 37e0754

File tree

11 files changed

+57
-5
lines changed

11 files changed

+57
-5
lines changed

tools.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ func (t *Tool) Register(mcp *server.MCPServer) {
3636

3737
// MustTool creates a new Tool from the given name, description, and toolHandler.
3838
// It panics if the tool cannot be created.
39-
func MustTool[T any, R any](name, description string, toolHandler ToolHandlerFunc[T, R]) Tool {
40-
tool, handler, err := ConvertTool(name, description, toolHandler)
39+
func MustTool[T any, R any](
40+
name, description string,
41+
toolHandler ToolHandlerFunc[T, R],
42+
options ...mcp.ToolOption,
43+
) Tool {
44+
tool, handler, err := ConvertTool(name, description, toolHandler, options...)
4145
if err != nil {
4246
panic(err)
4347
}
@@ -53,7 +57,7 @@ type ToolHandlerFunc[T any, R any] = func(ctx context.Context, request T) (R, er
5357
// to be used as the parameters for the tool. The second argument must not be a pointer,
5458
// should be marshalable to JSON, and the fields should have a `jsonschema` tag with the
5559
// description of the parameter.
56-
func ConvertTool[T any, R any](name, description string, toolHandler ToolHandlerFunc[T, R]) (mcp.Tool, server.ToolHandlerFunc, error) {
60+
func ConvertTool[T any, R any](name, description string, toolHandler ToolHandlerFunc[T, R], options ...mcp.ToolOption) (mcp.Tool, server.ToolHandlerFunc, error) {
5761
zero := mcp.Tool{}
5862
handlerValue := reflect.ValueOf(toolHandler)
5963
handlerType := handlerValue.Type()
@@ -183,11 +187,15 @@ func ConvertTool[T any, R any](name, description string, toolHandler ToolHandler
183187
Required: jsonSchema.Required,
184188
}
185189

186-
return mcp.Tool{
190+
t := mcp.Tool{
187191
Name: name,
188192
Description: description,
189193
InputSchema: inputSchema,
190-
}, handler, nil
194+
}
195+
for _, option := range options {
196+
option(&t)
197+
}
198+
return t, handler, nil
191199
}
192200

193201
// Creates a full JSON schema from a user provided handler by introspecting the arguments

tools/alerting.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/grafana/grafana-openapi-client-go/client/provisioning"
88
"github.com/grafana/grafana-openapi-client-go/models"
9+
"github.com/mark3labs/mcp-go/mcp"
910
"github.com/mark3labs/mcp-go/server"
1011

1112
mcpgrafana "github.com/grafana/mcp-grafana"
@@ -148,6 +149,7 @@ var ListAlertRules = mcpgrafana.MustTool(
148149
"list_alert_rules",
149150
"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",
150151
listAlertRules,
152+
mcp.WithTitleAnnotation("List alert rules"),
151153
)
152154

153155
type GetAlertRuleByUIDParams struct {
@@ -179,6 +181,7 @@ var GetAlertRuleByUID = mcpgrafana.MustTool(
179181
"get_alert_rule_by_uid",
180182
"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.",
181183
getAlertRuleByUID,
184+
mcp.WithTitleAnnotation("Get alert rule details"),
182185
)
183186

184187
type ListContactPointsParams struct {
@@ -252,6 +255,7 @@ var ListContactPoints = mcpgrafana.MustTool(
252255
"list_contact_points",
253256
"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.",
254257
listContactPoints,
258+
mcp.WithTitleAnnotation("List notification contact points"),
255259
)
256260

257261
func AddAlertingTools(mcp *server.MCPServer) {

tools/asserts.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
mcpgrafana "github.com/grafana/mcp-grafana"
14+
"github.com/mark3labs/mcp-go/mcp"
1415
"github.com/mark3labs/mcp-go/server"
1516
)
1617

@@ -134,6 +135,7 @@ var GetAssertions = mcpgrafana.MustTool(
134135
"get_assertions",
135136
"Get assertion summary for a given entity with its type, name, env, site, namespace, and a time range",
136137
getAssertions,
138+
mcp.WithTitleAnnotation("Get assertions summary"),
137139
)
138140

139141
func AddAssertsTools(mcp *server.MCPServer) {

tools/dashboard.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/mark3labs/mcp-go/mcp"
78
"github.com/mark3labs/mcp-go/server"
89

910
"github.com/grafana/grafana-openapi-client-go/models"
@@ -54,12 +55,14 @@ var GetDashboardByUID = mcpgrafana.MustTool(
5455
"get_dashboard_by_uid",
5556
"Retrieves the complete dashboard, including panels, variables, and settings, for a specific dashboard identified by its UID.",
5657
getDashboardByUID,
58+
mcp.WithTitleAnnotation("Get dashboard details"),
5759
)
5860

5961
var UpdateDashboard = mcpgrafana.MustTool(
6062
"update_dashboard",
6163
"Create or update a dashboard",
6264
updateDashboard,
65+
mcp.WithTitleAnnotation("Create or update dashboard"),
6366
)
6467

6568
type DashboardPanelQueriesParams struct {
@@ -140,6 +143,7 @@ var GetDashboardPanelQueries = mcpgrafana.MustTool(
140143
"get_dashboard_panel_queries",
141144
"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).",
142145
GetDashboardPanelQueriesTool,
146+
mcp.WithTitleAnnotation("Get dashboard panel queries"),
143147
)
144148

145149
func AddDashboardTools(mcp *server.MCPServer) {

tools/datasources.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"strings"
77

8+
"github.com/mark3labs/mcp-go/mcp"
89
"github.com/mark3labs/mcp-go/server"
910

1011
"github.com/grafana/grafana-openapi-client-go/models"
@@ -67,6 +68,7 @@ var ListDatasources = mcpgrafana.MustTool(
6768
"list_datasources",
6869
"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.",
6970
listDatasources,
71+
mcp.WithTitleAnnotation("List datasources"),
7072
)
7173

7274
type GetDatasourceByUIDParams struct {
@@ -90,6 +92,7 @@ var GetDatasourceByUID = mcpgrafana.MustTool(
9092
"get_datasource_by_uid",
9193
"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.",
9294
getDatasourceByUID,
95+
mcp.WithTitleAnnotation("Get datasource by UID"),
9396
)
9497

9598
type GetDatasourceByNameParams struct {
@@ -109,6 +112,7 @@ var GetDatasourceByName = mcpgrafana.MustTool(
109112
"get_datasource_by_name",
110113
"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.",
111114
getDatasourceByName,
115+
mcp.WithTitleAnnotation("Get datasource by name"),
112116
)
113117

114118
func AddDatasourceTools(mcp *server.MCPServer) {

tools/incident.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/grafana/incident-go"
88
mcpgrafana "github.com/grafana/mcp-grafana"
9+
"github.com/mark3labs/mcp-go/mcp"
910
"github.com/mark3labs/mcp-go/server"
1011
)
1112

@@ -49,6 +50,7 @@ var ListIncidents = mcpgrafana.MustTool(
4950
"list_incidents",
5051
"List Grafana incidents. Allows filtering by status ('active', 'resolved') and optionally including drill incidents. Returns a preview list with basic details.",
5152
listIncidents,
53+
mcp.WithTitleAnnotation("List incidents"),
5254
)
5355

5456
type CreateIncidentParams struct {
@@ -85,6 +87,7 @@ var CreateIncident = mcpgrafana.MustTool(
8587
"create_incident",
8688
"Create a new Grafana incident. Requires title, severity, and room prefix. Allows setting status and labels. This tool should be used judiciously and sparingly, and only after confirmation from the user, as it may notify or alarm lots of people.",
8789
createIncident,
90+
mcp.WithTitleAnnotation("Create incident"),
8891
)
8992

9093
type AddActivityToIncidentParams struct {
@@ -112,6 +115,7 @@ var AddActivityToIncident = mcpgrafana.MustTool(
112115
"add_activity_to_incident",
113116
"Add a note (userNote activity) to an existing incident's timeline using its ID. The note body can include URLs which will be attached as context. Use this to add context to an incident.",
114117
addActivityToIncident,
118+
mcp.WithTitleAnnotation("Add activity to incident"),
115119
)
116120

117121
func AddIncidentTools(mcp *server.MCPServer) {
@@ -143,4 +147,5 @@ var GetIncident = mcpgrafana.MustTool(
143147
"get_incident",
144148
"Get a single incident by ID. Returns the full incident details including title, status, severity, labels, timestamps, and other metadata.",
145149
getIncident,
150+
mcp.WithTitleAnnotation("Get incident details"),
146151
)

tools/loki.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
mcpgrafana "github.com/grafana/mcp-grafana"
16+
"github.com/mark3labs/mcp-go/mcp"
1617
"github.com/mark3labs/mcp-go/server"
1718
)
1819

@@ -222,6 +223,7 @@ var ListLokiLabelNames = mcpgrafana.MustTool(
222223
"list_loki_label_names",
223224
"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.",
224225
listLokiLabelNames,
226+
mcp.WithTitleAnnotation("List Loki label names"),
225227
)
226228

227229
// ListLokiLabelValuesParams defines the parameters for listing Loki label values
@@ -260,6 +262,7 @@ var ListLokiLabelValues = mcpgrafana.MustTool(
260262
"list_loki_label_values",
261263
"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.",
262264
listLokiLabelValues,
265+
mcp.WithTitleAnnotation("List Loki label values"),
263266
)
264267

265268
// LogStream represents a stream of log entries from Loki
@@ -467,6 +470,7 @@ var QueryLokiLogs = mcpgrafana.MustTool(
467470
"query_loki_logs",
468471
"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.",
469472
queryLokiLogs,
473+
mcp.WithTitleAnnotation("Query Loki logs"),
470474
)
471475

472476
// fetchStats is a method to fetch stats data from Loki API
@@ -524,6 +528,7 @@ var QueryLokiStats = mcpgrafana.MustTool(
524528
"query_loki_stats",
525529
"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.",
526530
queryLokiStats,
531+
mcp.WithTitleAnnotation("Get Loki log statistics"),
527532
)
528533

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

tools/oncall.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
aapi "github.com/grafana/amixr-api-go-client"
1111
mcpgrafana "github.com/grafana/mcp-grafana"
12+
"github.com/mark3labs/mcp-go/mcp"
1213
"github.com/mark3labs/mcp-go/server"
1314
)
1415

@@ -192,6 +193,7 @@ var ListOnCallSchedules = mcpgrafana.MustTool(
192193
"list_oncall_schedules",
193194
"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.",
194195
listOnCallSchedules,
196+
mcp.WithTitleAnnotation("List OnCall schedules"),
195197
)
196198

197199
type GetOnCallShiftParams struct {
@@ -216,6 +218,7 @@ var GetOnCallShift = mcpgrafana.MustTool(
216218
"get_oncall_shift",
217219
"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.",
218220
getOnCallShift,
221+
mcp.WithTitleAnnotation("Get OnCall shift"),
219222
)
220223

221224
// CurrentOnCallUsers represents the currently on-call users for a schedule
@@ -276,6 +279,7 @@ var GetCurrentOnCallUsers = mcpgrafana.MustTool(
276279
"get_current_oncall_users",
277280
"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.",
278281
getCurrentOnCallUsers,
282+
mcp.WithTitleAnnotation("Get current on-call users"),
279283
)
280284

281285
type ListOnCallTeamsParams struct {
@@ -305,6 +309,7 @@ var ListOnCallTeams = mcpgrafana.MustTool(
305309
"list_oncall_teams",
306310
"List teams configured in Grafana OnCall. Returns a list of team objects with their details. Supports pagination.",
307311
listOnCallTeams,
312+
mcp.WithTitleAnnotation("List OnCall teams"),
308313
)
309314

310315
type ListOnCallUsersParams struct {
@@ -348,6 +353,7 @@ var ListOnCallUsers = mcpgrafana.MustTool(
348353
"list_oncall_users",
349354
"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.",
350355
listOnCallUsers,
356+
mcp.WithTitleAnnotation("List OnCall users"),
351357
)
352358

353359
func AddOnCallTools(mcp *server.MCPServer) {

tools/prometheus.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
mcpgrafana "github.com/grafana/mcp-grafana"
11+
"github.com/mark3labs/mcp-go/mcp"
1112
"github.com/mark3labs/mcp-go/server"
1213
"github.com/prometheus/client_golang/api"
1314
promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
@@ -96,6 +97,7 @@ var ListPrometheusMetricMetadata = mcpgrafana.MustTool(
9697
"list_prometheus_metric_metadata",
9798
"List Prometheus metric metadata. Returns metadata about metrics currently scraped from targets. Note: This endpoint is experimental.",
9899
listPrometheusMetricMetadata,
100+
mcp.WithTitleAnnotation("List Prometheus metric metadata"),
99101
)
100102

101103
type QueryPrometheusParams struct {
@@ -158,6 +160,7 @@ var QueryPrometheus = mcpgrafana.MustTool(
158160
"query_prometheus",
159161
"Query Prometheus using a PromQL expression. Supports both instant queries (at a single point in time) and range queries (over a time range).",
160162
queryPrometheus,
163+
mcp.WithTitleAnnotation("Query Prometheus metrics"),
161164
)
162165

163166
type ListPrometheusMetricNamesParams struct {
@@ -225,6 +228,7 @@ var ListPrometheusMetricNames = mcpgrafana.MustTool(
225228
"list_prometheus_metric_names",
226229
"List metric names in a Prometheus datasource. Retrieves all metric names and then filters them locally using the provided regex. Supports pagination.",
227230
listPrometheusMetricNames,
231+
mcp.WithTitleAnnotation("List Prometheus metric names"),
228232
)
229233

230234
type LabelMatcher struct {
@@ -327,6 +331,7 @@ var ListPrometheusLabelNames = mcpgrafana.MustTool(
327331
"list_prometheus_label_names",
328332
"List label names in a Prometheus datasource. Allows filtering by series selectors and time range.",
329333
listPrometheusLabelNames,
334+
mcp.WithTitleAnnotation("List Prometheus label names"),
330335
)
331336

332337
type ListPrometheusLabelValuesParams struct {
@@ -383,6 +388,7 @@ var ListPrometheusLabelValues = mcpgrafana.MustTool(
383388
"list_prometheus_label_values",
384389
"Get the values for a specific label name in Prometheus. Allows filtering by series selectors and time range.",
385390
listPrometheusLabelValues,
391+
mcp.WithTitleAnnotation("List Prometheus label values"),
386392
)
387393

388394
func AddPrometheusTools(mcp *server.MCPServer) {

tools/search.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/mark3labs/mcp-go/mcp"
78
"github.com/mark3labs/mcp-go/server"
89

910
"github.com/grafana/grafana-openapi-client-go/client/search"
@@ -35,6 +36,7 @@ var SearchDashboards = mcpgrafana.MustTool(
3536
"search_dashboards",
3637
"Search for Grafana dashboards by a query string. Returns a list of matching dashboards with details like title, UID, folder, tags, and URL.",
3738
searchDashboards,
39+
mcp.WithTitleAnnotation("Search dashboards"),
3840
)
3941

4042
func AddSearchTools(mcp *server.MCPServer) {

0 commit comments

Comments
 (0)