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

feat: Add create org invitation tool#1226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
issei-m wants to merge1 commit intogithub:main
base:main
Choose a base branch
Loading
fromissei-m:org
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletionspkg/github/__toolsnaps__/create_org_invitation.snap
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
{
"annotations": {
"title": "Create Organization Invitation",
"readOnlyHint": false
},
"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.",
"inputSchema": {
"properties": {
"email": {
"description": "Email address of the person you are inviting. Required unless invitee_id is provided.",
"type": "string"
},
"invitee_id": {
"description": "GitHub user ID for the person you are inviting. Required unless email is provided.",
"type": "number"
},
"org": {
"description": "The organization name (not case sensitive)",
"type": "string"
},
"role": {
"default": "direct_member",
"description": "The role for the new member",
"enum": [
"admin",
"direct_member",
"billing_manager",
"reinstate"
],
"type": "string"
},
"team_ids": {
"description": "Team IDs to invite new members to",
"items": {
"type": "number"
},
"type": "array"
}
},
"required": [
"org"
],
"type": "object"
},
"name": "create_org_invitation"
}
186 changes: 186 additions & 0 deletionspkg/github/orgs.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
package github

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"

"github.com/google/go-github/v74/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"

"github.com/github/github-mcp-server/pkg/translations"
)

// CreateOrgInvitation creates a new invitation for a user to join an organization
func CreateOrgInvitation(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("create_org_invitation",
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.")),
mcp.WithToolAnnotation(mcp.ToolAnnotation{
Title: t("TOOL_CREATE_ORG_INVITATION", "Create Organization Invitation"),
ReadOnlyHint: ToBoolPtr(false),
}),
mcp.WithString("org",
mcp.Required(),
mcp.Description("The organization name (not case sensitive)"),
),
mcp.WithNumber("invitee_id",
mcp.Description("GitHub user ID for the person you are inviting. Required unless email is provided."),
),
mcp.WithString("email",
mcp.Description("Email address of the person you are inviting. Required unless invitee_id is provided."),
),
mcp.WithString("role",
mcp.Description("The role for the new member"),
mcp.Enum("admin", "direct_member", "billing_manager", "reinstate"),
mcp.DefaultString("direct_member"),
),
mcp.WithArray("team_ids",
mcp.Description("Team IDs to invite new members to"),
mcp.Items(map[string]any{
"type": "number",
}),
),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
org, err := RequiredParam[string](request, "org")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

inviteeID, err := OptionalParam[float64](request, "invitee_id")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

email, err := OptionalParam[string](request, "email")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

// Validate that at least one of invitee_id or email is provided
if inviteeID == 0 && email == "" {
return mcp.NewToolResultError("either invitee_id or email must be provided"), nil
}

role, err := OptionalParam[string](request, "role")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
if role == "" {
role = "direct_member"
}

var teamIDs []int64
if rawTeamIDs, ok := request.GetArguments()["team_ids"]; ok {
switch v := rawTeamIDs.(type) {
case nil:
// nothing to do
case []any:
for _, item := range v {
id, parseErr := parseTeamID(item)
if parseErr != nil {
return mcp.NewToolResultError(parseErr.Error()), nil
}
teamIDs = append(teamIDs, id)
}
case []float64:
for _, item := range v {
teamIDs = append(teamIDs, int64(item))
}
default:
return mcp.NewToolResultError("team_ids must be an array of numbers"), nil
}
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}

// Create the invitation request
invitation := &github.CreateOrgInvitationOptions{
Role: github.Ptr(role),
TeamID: teamIDs,
}

if inviteeID != 0 {
invitation.InviteeID = github.Ptr(int64(inviteeID))
}

if email != "" {
invitation.Email = github.Ptr(email)
}

createdInvitation, resp, err := client.Organizations.CreateOrgInvitation(ctx, org, invitation)
if err != nil {
return nil, fmt.Errorf("failed to create organization invitation: %w", err)
}
defer func() { _ = resp.Body.Close() }()

if resp.StatusCode != http.StatusCreated {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
return mcp.NewToolResultError(fmt.Sprintf("failed to create organization invitation: %s", string(body))), nil
}

// Return a minimal response with relevant information
type InvitationResponse struct {
ID int64 `json:"id"`
Login string `json:"login,omitempty"`
Email string `json:"email,omitempty"`
Role string `json:"role"`
InvitationTeamsURL string `json:"invitation_teams_url"`
CreatedAt string `json:"created_at"`
InviterLogin string `json:"inviter_login,omitempty"`
}

response := InvitationResponse{
ID: createdInvitation.GetID(),
Login: createdInvitation.GetLogin(),
Email: createdInvitation.GetEmail(),
Role: createdInvitation.GetRole(),
InvitationTeamsURL: createdInvitation.GetInvitationTeamURL(),
CreatedAt: createdInvitation.GetCreatedAt().Format("2006-01-02T15:04:05Z07:00"),
}

if createdInvitation.Inviter != nil {
response.InviterLogin = createdInvitation.Inviter.GetLogin()
}

r, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("failed to marshal response: %w", err)
}

return mcp.NewToolResultText(string(r)), nil
}
}

func parseTeamID(value any) (int64, error) {
switch v := value.(type) {
case float64:
// JSON numbers decode to float64; ensure they are whole numbers
if v != float64(int64(v)) {
return 0, fmt.Errorf("team_id must be an integer value")
}
return int64(v), nil
case int:
return int64(v), nil
case int64:
return v, nil
case string:
id, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0, fmt.Errorf("invalid team_id")
}
return id, nil
default:
return 0, fmt.Errorf("invalid team_id")
}
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp