Skip to content

Commit f33cf98

Browse files
author
cointem
committed
update: List outside collaborators
update: README.md
1 parent d5105a4 commit f33cf98

File tree

6 files changed

+266
-38
lines changed

6 files changed

+266
-38
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,13 +547,24 @@ The following sets of tools are available:
547547
- **get_me** - Get my user profile
548548
- No parameters required
549549

550+
- **get_org_members** - Get organization members
551+
- `org`: Organization login (owner) to get members for. (string, required)
552+
- `page`: Page number for pagination (number, optional)
553+
- `per_page`: Results per page (max 100) (number, optional)
554+
- `role`: Filter by role: all, admin, member (string, optional)
555+
550556
- **get_team_members** - Get team members
551557
- `org`: Organization login (owner) that contains the team. (string, required)
552558
- `team_slug`: Team slug (string, required)
553559

554560
- **get_teams** - Get teams
555561
- `user`: Username to get teams for. If not provided, uses the authenticated user. (string, optional)
556562

563+
- **list_outside_collaborators** - List outside collaborators
564+
- `org`: The organization name (string, required)
565+
- `page`: Page number for pagination (number, optional)
566+
- `per_page`: Results per page (max 100) (number, optional)
567+
557568
</details>
558569

559570
<details>

pkg/github/__toolsnaps__/get_org_members.snap

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
"org": {
1010
"description": "Organization login (owner) to get members for.",
1111
"type": "string"
12-
}
13-
,"role": {
14-
"description": "Filter by role: all, admin, member",
15-
"type": "string"
12+
},
13+
"page": {
14+
"description": "Page number for pagination",
15+
"type": "number"
1616
},
1717
"per_page": {
1818
"description": "Results per page (max 100)",
1919
"type": "number"
2020
},
21-
"page": {
22-
"description": "Page number for pagination",
23-
"type": "number"
21+
"role": {
22+
"description": "Filter by role: all, admin, member",
23+
"type": "string"
2424
}
2525
},
2626
"required": [
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"annotations": {
3+
"title": "List outside collaborators",
4+
"readOnlyHint": true
5+
},
6+
"description": "List all outside collaborators of an organization (users with access to organization repositories but not members).",
7+
"inputSchema": {
8+
"properties": {
9+
"org": {
10+
"description": "The organization name",
11+
"type": "string"
12+
},
13+
"page": {
14+
"description": "Page number for pagination",
15+
"type": "number"
16+
},
17+
"per_page": {
18+
"description": "Results per page (max 100)",
19+
"type": "number"
20+
}
21+
},
22+
"required": [
23+
"org"
24+
],
25+
"type": "object"
26+
},
27+
"name": "list_outside_collaborators"
28+
}

pkg/github/context_tools.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ func getOrgMembers(getClient GetClientFn, t translations.TranslationHelperFunc)
334334
ID string `json:"id"`
335335
AvatarURL string `json:"avatar_url"`
336336
Type string `json:"type"`
337+
SiteAdmin bool `json:"site_admin"`
337338
}
338339

339340
var members []outUser
@@ -343,9 +344,97 @@ func getOrgMembers(getClient GetClientFn, t translations.TranslationHelperFunc)
343344
ID: fmt.Sprintf("%v", u.GetID()),
344345
AvatarURL: u.GetAvatarURL(),
345346
Type: u.GetType(),
347+
SiteAdmin: u.GetSiteAdmin(),
346348
})
347349
}
348350

349351
return MarshalledTextResult(members), nil
350352
}
351353
}
354+
355+
func listOutsideCollaborators(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
356+
return mcp.NewTool("list_outside_collaborators",
357+
mcp.WithDescription(t("TOOL_LIST_OUTSIDE_COLLABORATORS_DESCRIPTION", "List all outside collaborators of an organization (users with access to organization repositories but not members).")),
358+
mcp.WithString("org",
359+
mcp.Description(t("TOOL_LIST_OUTSIDE_COLLABORATORS_ORG_DESCRIPTION", "The organization name")),
360+
mcp.Required(),
361+
),
362+
mcp.WithNumber("per_page",
363+
mcp.Description("Results per page (max 100)"),
364+
),
365+
mcp.WithNumber("page",
366+
mcp.Description("Page number for pagination"),
367+
),
368+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
369+
Title: t("TOOL_LIST_OUTSIDE_COLLABORATORS_TITLE", "List outside collaborators"),
370+
ReadOnlyHint: ToBoolPtr(true),
371+
}),
372+
),
373+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
374+
// Decode params into struct to support optional numbers
375+
var params struct {
376+
Org string `mapstructure:"org"`
377+
PerPage int32 `mapstructure:"per_page"`
378+
Page int32 `mapstructure:"page"`
379+
}
380+
if err := mapstructure.Decode(request.Params.Arguments, &params); err != nil {
381+
return mcp.NewToolResultError(err.Error()), nil
382+
}
383+
org := params.Org
384+
perPage := params.PerPage
385+
page := params.Page
386+
if org == "" {
387+
return mcp.NewToolResultError("org is required"), nil
388+
}
389+
390+
// Defaults
391+
if perPage <= 0 {
392+
perPage = 30
393+
}
394+
if perPage > 100 {
395+
perPage = 100
396+
}
397+
if page <= 0 {
398+
page = 1
399+
}
400+
401+
client, err := getClient(ctx)
402+
if err != nil {
403+
return mcp.NewToolResultErrorFromErr("failed to get GitHub client", err), nil
404+
}
405+
406+
// Use Organizations.ListOutsideCollaborators with pagination
407+
opts := &github.ListOutsideCollaboratorsOptions{
408+
ListOptions: github.ListOptions{
409+
PerPage: int(perPage),
410+
Page: int(page),
411+
},
412+
}
413+
414+
users, resp, err := client.Organizations.ListOutsideCollaborators(ctx, org, opts)
415+
if err != nil {
416+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "Failed to list outside collaborators", resp, err), nil
417+
}
418+
419+
type outUser struct {
420+
Login string `json:"login"`
421+
ID string `json:"id"`
422+
AvatarURL string `json:"avatar_url"`
423+
Type string `json:"type"`
424+
SiteAdmin bool `json:"site_admin"`
425+
}
426+
427+
var collaborators []outUser
428+
for _, u := range users {
429+
collaborators = append(collaborators, outUser{
430+
Login: u.GetLogin(),
431+
ID: fmt.Sprintf("%v", u.GetID()),
432+
AvatarURL: u.GetAvatarURL(),
433+
Type: u.GetType(),
434+
SiteAdmin: u.GetSiteAdmin(),
435+
})
436+
}
437+
438+
return MarshalledTextResult(collaborators), nil
439+
}
440+
}

0 commit comments

Comments
 (0)