Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8966fd0

Browse files
committed
Create org invitation tool
1 parent99acea6 commit8966fd0

File tree

4 files changed

+462
-0
lines changed

4 files changed

+462
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"annotations": {
3+
"title":"Create Organization Invitation",
4+
"readOnlyHint":false
5+
},
6+
"description":"Invite a user to join an organization by GitHub user ID or email address. Requires organization owner permissions. This endpoint triggers notifications and may be subject to rate limiting.",
7+
"inputSchema": {
8+
"properties": {
9+
"email": {
10+
"description":"Email address of the person you are inviting. Required unless invitee_id is provided.",
11+
"type":"string"
12+
},
13+
"invitee_id": {
14+
"description":"GitHub user ID for the person you are inviting. Required unless email is provided.",
15+
"type":"number"
16+
},
17+
"org": {
18+
"description":"The organization name (not case sensitive)",
19+
"type":"string"
20+
},
21+
"role": {
22+
"default":"direct_member",
23+
"description":"The role for the new member",
24+
"enum": [
25+
"admin",
26+
"direct_member",
27+
"billing_manager",
28+
"reinstate"
29+
],
30+
"type":"string"
31+
},
32+
"team_ids": {
33+
"description":"Team IDs to invite new members to",
34+
"items": {
35+
"type":"number"
36+
},
37+
"type":"array"
38+
}
39+
},
40+
"required": [
41+
"org"
42+
],
43+
"type":"object"
44+
},
45+
"name":"create_org_invitation"
46+
}

‎pkg/github/orgs.go‎

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"strconv"
10+
11+
"github.com/google/go-github/v74/github"
12+
"github.com/mark3labs/mcp-go/mcp"
13+
"github.com/mark3labs/mcp-go/server"
14+
15+
"github.com/github/github-mcp-server/pkg/translations"
16+
)
17+
18+
// CreateOrgInvitation creates a new invitation for a user to join an organization
19+
funcCreateOrgInvitation(getClientGetClientFn,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
20+
returnmcp.NewTool("create_org_invitation",
21+
mcp.WithDescription(t("TOOL_CREATE_ORG_INVITATION_DESCRIPTION","Invite a user to join an organization by GitHub user ID or email address. Requires organization owner permissions. This endpoint triggers notifications and may be subject to rate limiting.")),
22+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
23+
Title:t("TOOL_CREATE_ORG_INVITATION","Create Organization Invitation"),
24+
ReadOnlyHint:ToBoolPtr(false),
25+
}),
26+
mcp.WithString("org",
27+
mcp.Required(),
28+
mcp.Description("The organization name (not case sensitive)"),
29+
),
30+
mcp.WithNumber("invitee_id",
31+
mcp.Description("GitHub user ID for the person you are inviting. Required unless email is provided."),
32+
),
33+
mcp.WithString("email",
34+
mcp.Description("Email address of the person you are inviting. Required unless invitee_id is provided."),
35+
),
36+
mcp.WithString("role",
37+
mcp.Description("The role for the new member"),
38+
mcp.Enum("admin","direct_member","billing_manager","reinstate"),
39+
mcp.DefaultString("direct_member"),
40+
),
41+
mcp.WithArray("team_ids",
42+
mcp.Description("Team IDs to invite new members to"),
43+
mcp.Items(map[string]any{
44+
"type":"number",
45+
}),
46+
),
47+
),
48+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
49+
org,err:=RequiredParam[string](request,"org")
50+
iferr!=nil {
51+
returnmcp.NewToolResultError(err.Error()),nil
52+
}
53+
54+
inviteeID,err:=OptionalParam[float64](request,"invitee_id")
55+
iferr!=nil {
56+
returnmcp.NewToolResultError(err.Error()),nil
57+
}
58+
59+
email,err:=OptionalParam[string](request,"email")
60+
iferr!=nil {
61+
returnmcp.NewToolResultError(err.Error()),nil
62+
}
63+
64+
// Validate that at least one of invitee_id or email is provided
65+
ifinviteeID==0&&email=="" {
66+
returnmcp.NewToolResultError("either invitee_id or email must be provided"),nil
67+
}
68+
69+
role,err:=OptionalParam[string](request,"role")
70+
iferr!=nil {
71+
returnmcp.NewToolResultError(err.Error()),nil
72+
}
73+
ifrole=="" {
74+
role="direct_member"
75+
}
76+
77+
varteamIDs []int64
78+
ifrawTeamIDs,ok:=request.GetArguments()["team_ids"];ok {
79+
switchv:=rawTeamIDs.(type) {
80+
casenil:
81+
// nothing to do
82+
case []any:
83+
for_,item:=rangev {
84+
id,parseErr:=parseTeamID(item)
85+
ifparseErr!=nil {
86+
returnmcp.NewToolResultError(parseErr.Error()),nil
87+
}
88+
teamIDs=append(teamIDs,id)
89+
}
90+
case []float64:
91+
for_,item:=rangev {
92+
teamIDs=append(teamIDs,int64(item))
93+
}
94+
default:
95+
returnmcp.NewToolResultError("team_ids must be an array of numbers"),nil
96+
}
97+
}
98+
99+
client,err:=getClient(ctx)
100+
iferr!=nil {
101+
returnnil,fmt.Errorf("failed to get GitHub client: %w",err)
102+
}
103+
104+
// Create the invitation request
105+
invitation:=&github.CreateOrgInvitationOptions{
106+
Role:github.Ptr(role),
107+
TeamID:teamIDs,
108+
}
109+
110+
ifinviteeID!=0 {
111+
invitation.InviteeID=github.Ptr(int64(inviteeID))
112+
}
113+
114+
ifemail!="" {
115+
invitation.Email=github.Ptr(email)
116+
}
117+
118+
createdInvitation,resp,err:=client.Organizations.CreateOrgInvitation(ctx,org,invitation)
119+
iferr!=nil {
120+
returnnil,fmt.Errorf("failed to create organization invitation: %w",err)
121+
}
122+
deferfunc() {_=resp.Body.Close() }()
123+
124+
ifresp.StatusCode!=http.StatusCreated {
125+
body,err:=io.ReadAll(resp.Body)
126+
iferr!=nil {
127+
returnnil,fmt.Errorf("failed to read response body: %w",err)
128+
}
129+
returnmcp.NewToolResultError(fmt.Sprintf("failed to create organization invitation: %s",string(body))),nil
130+
}
131+
132+
// Return a minimal response with relevant information
133+
typeInvitationResponsestruct {
134+
IDint64`json:"id"`
135+
Loginstring`json:"login,omitempty"`
136+
Emailstring`json:"email,omitempty"`
137+
Rolestring`json:"role"`
138+
InvitationTeamsURLstring`json:"invitation_teams_url"`
139+
CreatedAtstring`json:"created_at"`
140+
InviterLoginstring`json:"inviter_login,omitempty"`
141+
}
142+
143+
response:=InvitationResponse{
144+
ID:createdInvitation.GetID(),
145+
Login:createdInvitation.GetLogin(),
146+
Email:createdInvitation.GetEmail(),
147+
Role:createdInvitation.GetRole(),
148+
InvitationTeamsURL:createdInvitation.GetInvitationTeamURL(),
149+
CreatedAt:createdInvitation.GetCreatedAt().Format("2006-01-02T15:04:05Z07:00"),
150+
}
151+
152+
ifcreatedInvitation.Inviter!=nil {
153+
response.InviterLogin=createdInvitation.Inviter.GetLogin()
154+
}
155+
156+
r,err:=json.Marshal(response)
157+
iferr!=nil {
158+
returnnil,fmt.Errorf("failed to marshal response: %w",err)
159+
}
160+
161+
returnmcp.NewToolResultText(string(r)),nil
162+
}
163+
}
164+
165+
funcparseTeamID(valueany) (int64,error) {
166+
switchv:=value.(type) {
167+
casefloat64:
168+
// JSON numbers decode to float64; ensure they are whole numbers
169+
ifv!=float64(int64(v)) {
170+
return0,fmt.Errorf("team_id must be an integer value")
171+
}
172+
returnint64(v),nil
173+
caseint:
174+
returnint64(v),nil
175+
caseint64:
176+
returnv,nil
177+
casestring:
178+
id,err:=strconv.ParseInt(v,10,64)
179+
iferr!=nil {
180+
return0,fmt.Errorf("invalid team_id: %s",v)
181+
}
182+
returnid,nil
183+
default:
184+
return0,fmt.Errorf("team_id must be a number")
185+
}
186+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp