@@ -3,11 +3,14 @@ package tools
33import (
44 "context"
55 "fmt"
6+ "strconv"
67
8+ "github.com/go-openapi/runtime"
9+ "github.com/go-openapi/strfmt"
710 "github.com/modelcontextprotocol/go-sdk/mcp"
811
912 "github.com/grafana/grafana-openapi-client-go/client/access_control"
10- "github.com/grafana/grafana-openapi-client-go/client/org "
13+ "github.com/grafana/grafana-openapi-client-go/client/orgs "
1114 "github.com/grafana/grafana-openapi-client-go/client/teams"
1215 "github.com/grafana/grafana-openapi-client-go/models"
1316 mcpgrafana "github.com/grafana/mcp-grafana/v2"
@@ -41,22 +44,64 @@ var ListTeams = mcpgrafana.MustTool(
4144 mcpgrafana .WithOpenWorldHintAnnotation (false ),
4245)
4346
44- type ListUsersByOrgParams struct {}
47+ const (
48+ defaultListUsersByOrgLimit = 100
49+ maxListUsersByOrgLimit = 1000
50+ )
51+
52+ type ListUsersByOrgParams struct {
53+ Query string `json:"query,omitempty" jsonschema:"description=Optional search string matched against login\\, email and name"`
54+ Limit int `json:"limit,omitempty" jsonschema:"default=100,description=Maximum number of users to return (max 1000)"`
55+ Page int `json:"page,omitempty" jsonschema:"default=1,description=Page number for pagination (1-indexed)"`
56+ }
4557
46- func listUsersByOrg (ctx context.Context , args ListUsersByOrgParams ) ([] * models.OrgUserDTO , error ) {
58+ func listUsersByOrg (ctx context.Context , args ListUsersByOrgParams ) (* models.SearchOrgUsersQueryResult , error ) {
4759 c := mcpgrafana .GrafanaClientFromContext (ctx )
4860
49- params := org .NewGetOrgUsersForCurrentOrgParamsWithContext (ctx )
50- search , err := c .Org .GetOrgUsersForCurrentOrg (params )
61+ limit := args .Limit
62+ if limit <= 0 {
63+ limit = defaultListUsersByOrgLimit
64+ }
65+ if limit > maxListUsersByOrgLimit {
66+ limit = maxListUsersByOrgLimit
67+ }
68+ page := args .Page
69+ if page <= 0 {
70+ page = 1
71+ }
72+
73+ // The client has no method for the current-org search endpoint
74+ // (GET /api/org/users/search), so reuse the admin search operation, which
75+ // has the same response shape, and point it at the current-org path.
76+ params := orgs .NewSearchOrgUsersParamsWithContext (ctx )
77+ resp , err := c .Orgs .SearchOrgUsersWithParams (params , func (op * runtime.ClientOperation ) {
78+ op .ID = "searchOrgUsersForCurrentOrg"
79+ op .PathPattern = "/org/users/search"
80+ op .Params = runtime .ClientRequestWriterFunc (func (r runtime.ClientRequest , reg strfmt.Registry ) error {
81+ // Keeps the default timeout; the unused org_id path param is ignored.
82+ if err := params .WriteToRequest (r , reg ); err != nil {
83+ return err
84+ }
85+ if args .Query != "" {
86+ if err := r .SetQueryParam ("query" , args .Query ); err != nil {
87+ return err
88+ }
89+ }
90+ if err := r .SetQueryParam ("perpage" , strconv .Itoa (limit )); err != nil {
91+ return err
92+ }
93+ return r .SetQueryParam ("page" , strconv .Itoa (page ))
94+ })
95+ })
5196 if err != nil {
5297 return nil , fmt .Errorf ("search users: %w" , err )
5398 }
54- return search .Payload , nil
99+ return resp .Payload , nil
55100}
56101
57102var ListUsersByOrg = mcpgrafana .MustTool (
58103 "list_users_by_org" ,
59- "List users in the Grafana organization. Returns a list of organization users with details like userid, email, role etc." ,
104+ "List users in the Grafana organization, optionally filtered by a search query. Results are paginated; the response includes totalCount, page and perPage alongside organization users with details like userid, email, role etc." ,
60105 listUsersByOrg ,
61106 mcpgrafana .WithTitleAnnotation ("List users by org" ),
62107 mcpgrafana .WithIdempotentHintAnnotation (true ),
0 commit comments