Skip to content

Commit 8558891

Browse files
authored
Add listing all contact points with limits (#81)
1 parent 0550756 commit 8558891

File tree

3 files changed

+178
-1
lines changed

3 files changed

+178
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: 1
2+
3+
contactPoints:
4+
- name: Email1
5+
receivers:
6+
- uid: email1
7+
type: email
8+
settings:
9+
addresses: [email protected]
10+
singleEmail: false
11+
message: my optional message1 to include
12+
- name: Email2
13+
receivers:
14+
- uid: email2
15+
type: email
16+
settings:
17+
addresses: [email protected]
18+
singleEmail: false
19+
message: my optional message2 to include

tools/alerting.go

+77-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/grafana/grafana-openapi-client-go/client/provisioning"
78
"github.com/grafana/grafana-openapi-client-go/models"
89
"github.com/mark3labs/mcp-go/server"
910
"github.com/prometheus/prometheus/model/labels"
@@ -12,7 +13,8 @@ import (
1213
)
1314

1415
const (
15-
DefaultListAlertRulesLimit = 100
16+
DefaultListAlertRulesLimit = 100
17+
DefaultListContactPointsLimit = 100
1618
)
1719

1820
type ListAlertRulesParams struct {
@@ -179,7 +181,81 @@ var GetAlertRuleByUID = mcpgrafana.MustTool(
179181
getAlertRuleByUID,
180182
)
181183

184+
type ListContactPointsParams struct {
185+
Limit int `json:"limit,omitempty" jsonschema:"description=The maximum number of results to return. Default is 100."`
186+
Name *string `json:"name,omitempty" jsonschema:"description=Filter contact points by name"`
187+
}
188+
189+
func (p ListContactPointsParams) validate() error {
190+
if p.Limit < 0 {
191+
return fmt.Errorf("invalid limit: %d, must be greater than 0", p.Limit)
192+
}
193+
return nil
194+
}
195+
196+
type contactPointSummary struct {
197+
UID string `json:"uid"`
198+
Name string `json:"name"`
199+
Type *string `json:"type,omitempty"`
200+
}
201+
202+
func listContactPoints(ctx context.Context, args ListContactPointsParams) ([]contactPointSummary, error) {
203+
if err := args.validate(); err != nil {
204+
return nil, fmt.Errorf("list contact points: %w", err)
205+
}
206+
207+
c := mcpgrafana.GrafanaClientFromContext(ctx)
208+
209+
params := provisioning.NewGetContactpointsParams().WithContext(ctx)
210+
if args.Name != nil {
211+
params.Name = args.Name
212+
}
213+
214+
response, err := c.Provisioning.GetContactpoints(params)
215+
if err != nil {
216+
return nil, fmt.Errorf("list contact points: %w", err)
217+
}
218+
219+
filteredContactPoints, err := applyLimitToContactPoints(response.Payload, args.Limit)
220+
if err != nil {
221+
return nil, fmt.Errorf("list contact points: %w", err)
222+
}
223+
224+
return summarizeContactPoints(filteredContactPoints), nil
225+
}
226+
227+
func summarizeContactPoints(contactPoints []*models.EmbeddedContactPoint) []contactPointSummary {
228+
result := make([]contactPointSummary, 0, len(contactPoints))
229+
for _, cp := range contactPoints {
230+
result = append(result, contactPointSummary{
231+
UID: cp.UID,
232+
Name: cp.Name,
233+
Type: cp.Type,
234+
})
235+
}
236+
return result
237+
}
238+
239+
func applyLimitToContactPoints(items []*models.EmbeddedContactPoint, limit int) ([]*models.EmbeddedContactPoint, error) {
240+
if limit == 0 {
241+
limit = DefaultListContactPointsLimit
242+
}
243+
244+
if limit > len(items) {
245+
return items, nil
246+
}
247+
248+
return items[:limit], nil
249+
}
250+
251+
var ListContactPoints = mcpgrafana.MustTool(
252+
"list_contact_points",
253+
"List contact points",
254+
listContactPoints,
255+
)
256+
182257
func AddAlertingTools(mcp *server.MCPServer) {
183258
ListAlertRules.Register(mcp)
184259
GetAlertRuleByUID.Register(mcp)
260+
ListContactPoints.Register(mcp)
185261
}

tools/alerting_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,85 @@ func TestAlertingTools_GetAlertRuleByUID(t *testing.T) {
399399
require.Contains(t, err.Error(), "getAlertRuleNotFound")
400400
})
401401
}
402+
403+
var (
404+
emailType = "email"
405+
406+
contactPoint1 = contactPointSummary{
407+
UID: "email1",
408+
Name: "Email1",
409+
Type: &emailType,
410+
}
411+
contactPoint2 = contactPointSummary{
412+
UID: "email2",
413+
Name: "Email2",
414+
Type: &emailType,
415+
}
416+
contactPoint3 = contactPointSummary{
417+
UID: "",
418+
Name: "email receiver",
419+
Type: &emailType,
420+
}
421+
allExpectedContactPoints = []contactPointSummary{contactPoint1, contactPoint2, contactPoint3}
422+
)
423+
424+
func TestAlertingTools_ListContactPoints(t *testing.T) {
425+
t.Run("list contact points", func(t *testing.T) {
426+
ctx := newTestContext()
427+
result, err := listContactPoints(ctx, ListContactPointsParams{})
428+
require.NoError(t, err)
429+
require.ElementsMatch(t, allExpectedContactPoints, result)
430+
})
431+
432+
t.Run("list one contact point", func(t *testing.T) {
433+
ctx := newTestContext()
434+
435+
// Get the contact points with limit 1
436+
result1, err := listContactPoints(ctx, ListContactPointsParams{
437+
Limit: 1,
438+
})
439+
require.NoError(t, err)
440+
require.Len(t, result1, 1)
441+
})
442+
443+
t.Run("list contact points with name filter", func(t *testing.T) {
444+
ctx := newTestContext()
445+
name := "Email1"
446+
447+
result, err := listContactPoints(ctx, ListContactPointsParams{
448+
Name: &name,
449+
})
450+
require.NoError(t, err)
451+
require.Len(t, result, 1)
452+
require.Equal(t, "Email1", result[0].Name)
453+
})
454+
455+
t.Run("list contact points with invalid limit parameter", func(t *testing.T) {
456+
ctx := newTestContext()
457+
result, err := listContactPoints(ctx, ListContactPointsParams{
458+
Limit: -1,
459+
})
460+
require.Error(t, err)
461+
require.Empty(t, result)
462+
})
463+
464+
t.Run("list contact points with large limit", func(t *testing.T) {
465+
ctx := newTestContext()
466+
result, err := listContactPoints(ctx, ListContactPointsParams{
467+
Limit: 1000,
468+
})
469+
require.NoError(t, err)
470+
require.NotEmpty(t, result)
471+
})
472+
473+
t.Run("list contact points with non-existent name filter", func(t *testing.T) {
474+
ctx := newTestContext()
475+
name := "NonExistentAlert"
476+
477+
result, err := listContactPoints(ctx, ListContactPointsParams{
478+
Name: &name,
479+
})
480+
require.NoError(t, err)
481+
require.Empty(t, result)
482+
})
483+
}

0 commit comments

Comments
 (0)