Skip to content

Commit 496db99

Browse files
committed
WIP: Assign copilot to issues
1 parent da2df71 commit 496db99

File tree

11 files changed

+278
-1
lines changed

11 files changed

+278
-1
lines changed

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ require (
2525
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
2626
github.com/rogpeppe/go-internal v1.13.1 // indirect
2727
github.com/sagikazarmark/locafero v0.9.0 // indirect
28+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
29+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
2830
github.com/sourcegraph/conc v0.3.0 // indirect
2931
github.com/spf13/afero v1.14.0 // indirect
3032
github.com/spf13/cast v1.7.1 // indirect
3133
github.com/spf13/pflag v1.0.6 // indirect
3234
github.com/subosito/gotenv v1.6.0 // indirect
3335
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
3436
go.uber.org/multierr v1.11.0 // indirect
37+
golang.org/x/oauth2 v0.30.0
3538
golang.org/x/sys v0.31.0 // indirect
3639
golang.org/x/text v0.23.0 // indirect
3740
golang.org/x/time v0.5.0 // indirect

go.sum

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
4545
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
4646
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
4747
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
48+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
49+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
50+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
51+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
4852
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
4953
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
5054
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -69,6 +73,8 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI
6973
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
7074
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
7175
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
76+
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
77+
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
7278
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7379
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
7480
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

internal/ghmcp/server.go

+12
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import (
1515
gogithub "github.com/google/go-github/v69/github"
1616
"github.com/mark3labs/mcp-go/mcp"
1717
"github.com/mark3labs/mcp-go/server"
18+
"github.com/shurcooL/githubv4"
1819
"github.com/sirupsen/logrus"
20+
"golang.org/x/oauth2"
1921
)
2022

2123
type MCPServerConfig struct {
@@ -86,11 +88,21 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
8688
return ghClient, nil // closing over client
8789
}
8890

91+
getGQLClient := func(_ context.Context) (*githubv4.Client, error) {
92+
// TODO: Enterprise support
93+
src := oauth2.StaticTokenSource(
94+
&oauth2.Token{AccessToken: cfg.Token},
95+
)
96+
httpClient := oauth2.NewClient(context.Background(), src)
97+
return githubv4.NewClient(httpClient), nil
98+
}
99+
89100
// Create default toolsets
90101
toolsets, err := github.InitToolsets(
91102
enabledToolsets,
92103
cfg.ReadOnly,
93104
getClient,
105+
getGQLClient,
94106
cfg.Translator,
95107
)
96108
if err != nil {

pkg/github/issues.go

+174
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import (
55
"encoding/json"
66
"fmt"
77
"io"
8+
"math"
89
"net/http"
910
"time"
1011

1112
"github.com/github/github-mcp-server/pkg/translations"
1213
"github.com/google/go-github/v69/github"
1314
"github.com/mark3labs/mcp-go/mcp"
1415
"github.com/mark3labs/mcp-go/server"
16+
"github.com/shurcooL/githubv4"
1517
)
1618

1719
// GetIssue creates a tool to get details of a specific issue in a GitHub repository.
@@ -711,6 +713,178 @@ func GetIssueComments(getClient GetClientFn, t translations.TranslationHelperFun
711713
}
712714
}
713715

716+
// AssignCopilotToIssue creates a tool to assign a Copilot to an issue.
717+
// Note that this tool will not work on GHES where this feature is unsupported. In future, we should not expose this
718+
// tool if the configured host does not support it.
719+
func AssignCopilotToIssue(getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
720+
return mcp.NewTool("assign_copilot_to_issue",
721+
mcp.WithDescription(t("TOOL_ASSIGN_COPILOT_TO_ISSUE_DESCRIPTION", "Assign a Copilot to a specific issue in a GitHub repository.")),
722+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
723+
Title: t("TOOL_ASSIGN_COPILOT_TO_ISSUE_USER_TITLE", "Assign Copilot to issue"),
724+
ReadOnlyHint: toBoolPtr(false),
725+
}),
726+
mcp.WithString("owner",
727+
mcp.Required(),
728+
mcp.Description("Repository owner"),
729+
),
730+
mcp.WithString("repo",
731+
mcp.Required(),
732+
mcp.Description("Repository name"),
733+
),
734+
mcp.WithNumber("issueNumber",
735+
mcp.Required(),
736+
mcp.Description("Issue number"),
737+
),
738+
),
739+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
740+
owner, err := requiredParam[string](request, "owner")
741+
if err != nil {
742+
return mcp.NewToolResultError(err.Error()), nil
743+
}
744+
745+
repo, err := requiredParam[string](request, "repo")
746+
if err != nil {
747+
return mcp.NewToolResultError(err.Error()), nil
748+
}
749+
750+
issueNumber, err := RequiredInt(request, "issueNumber")
751+
if err != nil {
752+
return mcp.NewToolResultError(err.Error()), nil
753+
}
754+
if issueNumber < math.MinInt32 || issueNumber > math.MaxInt32 {
755+
return mcp.NewToolResultError(fmt.Sprintf("issueNumber %d overflows int32", issueNumber)), nil
756+
}
757+
758+
client, err := getGQLClient(ctx)
759+
if err != nil {
760+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
761+
}
762+
763+
// First we need to get a list of assignable actors
764+
type botAssignee struct {
765+
ID githubv4.ID
766+
Login string
767+
TypeName string `graphql:"__typename"`
768+
}
769+
770+
type suggestedActorsQuery struct {
771+
Repository struct {
772+
SuggestedActors struct {
773+
Nodes []struct {
774+
Bot botAssignee `graphql:"... on Bot"`
775+
}
776+
PageInfo struct {
777+
HasNextPage bool
778+
EndCursor string
779+
}
780+
} `graphql:"suggestedActors(first: 100, after: $endCursor, capabilities: CAN_BE_ASSIGNED)"`
781+
} `graphql:"repository(owner: $owner, name: $name)"`
782+
}
783+
784+
variables := map[string]any{
785+
"owner": githubv4.String(owner),
786+
"name": githubv4.String(repo),
787+
"endCursor": (*githubv4.String)(nil),
788+
}
789+
790+
var copilotAssignee *botAssignee
791+
for {
792+
var query suggestedActorsQuery
793+
err := client.Query(ctx, &query, variables)
794+
if err != nil {
795+
return nil, err
796+
}
797+
798+
for _, node := range query.Repository.SuggestedActors.Nodes {
799+
if node.Bot.Login == "copilot-swe-agent" {
800+
copilotAssignee = &node.Bot
801+
break
802+
}
803+
}
804+
805+
if !query.Repository.SuggestedActors.PageInfo.HasNextPage {
806+
break
807+
}
808+
variables["endCursor"] = githubv4.String(query.Repository.SuggestedActors.PageInfo.EndCursor)
809+
}
810+
811+
if copilotAssignee == nil {
812+
return mcp.NewToolResultError("Copilot was not found as a suggested assignee"), nil
813+
}
814+
815+
// Next let's get the GQL Node ID and current assignees for this issue
816+
// because the only way to assign copilot is to use replaceActorsForAssignable.
817+
var getIssueQuery struct {
818+
Repository struct {
819+
Issue struct {
820+
ID githubv4.ID
821+
Assignees struct {
822+
Nodes []struct {
823+
ID githubv4.ID
824+
}
825+
} `graphql:"assignees(first: 100)"`
826+
} `graphql:"issue(number: $number)"`
827+
} `graphql:"repository(owner: $owner, name: $name)"`
828+
}
829+
830+
variables = map[string]any{
831+
"owner": githubv4.String(owner),
832+
"name": githubv4.String(repo),
833+
"number": githubv4.Int(issueNumber), //nolint:gosec // G115: issueNumber is guaranteed to fit into int32
834+
}
835+
836+
if err := client.Query(ctx, &getIssueQuery, variables); err != nil {
837+
return mcp.NewToolResultError(fmt.Sprintf("failed to get issue ID: %v", err)), nil
838+
}
839+
840+
// Then, get all the current assignees because the only way to assign copilot is to use replaceActorsForAssignable
841+
// which replaces all assignees.
842+
var assignCopilotMutation struct {
843+
ReplaceActorsForAssignable struct {
844+
Assignable struct {
845+
ID githubv4.ID
846+
Title string
847+
Assignees struct {
848+
Nodes []struct {
849+
Login string
850+
}
851+
} `graphql:"assignees(first: 10)"`
852+
} `graphql:"... on Issue"`
853+
} `graphql:"replaceActorsForAssignable(input: $input)"`
854+
}
855+
856+
type ReplaceActorsForAssignableInput struct {
857+
AssignableID githubv4.ID `json:"assignableId"`
858+
ActorIDs []githubv4.ID `json:"actorIds"`
859+
}
860+
861+
actorIDs := make([]githubv4.ID, len(getIssueQuery.Repository.Issue.Assignees.Nodes)+1)
862+
for i, node := range getIssueQuery.Repository.Issue.Assignees.Nodes {
863+
actorIDs[i] = node.ID
864+
}
865+
actorIDs[len(getIssueQuery.Repository.Issue.Assignees.Nodes)] = copilotAssignee.ID
866+
867+
if err := client.Mutate(
868+
ctx,
869+
&assignCopilotMutation,
870+
ReplaceActorsForAssignableInput{
871+
AssignableID: getIssueQuery.Repository.Issue.ID,
872+
ActorIDs: actorIDs,
873+
},
874+
nil,
875+
); err != nil {
876+
return nil, fmt.Errorf("failed to replace actors for assignable: %w", err)
877+
}
878+
879+
r, err := json.Marshal(assignCopilotMutation.ReplaceActorsForAssignable.Assignable)
880+
if err != nil {
881+
return nil, fmt.Errorf("failed to marshal response: %w", err)
882+
}
883+
884+
return mcp.NewToolResultText(string(r)), nil
885+
}
886+
}
887+
714888
// parseISOTimestamp parses an ISO 8601 timestamp string into a time.Time object.
715889
// Returns the parsed time or an error if parsing fails.
716890
// Example formats supported: "2023-01-15T14:30:00Z", "2023-01-15"

pkg/github/tools.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"github.com/github/github-mcp-server/pkg/translations"
88
"github.com/google/go-github/v69/github"
99
"github.com/mark3labs/mcp-go/server"
10+
"github.com/shurcooL/githubv4"
1011
)
1112

1213
type GetClientFn func(context.Context) (*github.Client, error)
14+
type GetGQLClientFn func(context.Context) (*githubv4.Client, error)
1315

1416
var DefaultTools = []string{"all"}
1517

16-
func InitToolsets(passedToolsets []string, readOnly bool, getClient GetClientFn, t translations.TranslationHelperFunc) (*toolsets.ToolsetGroup, error) {
18+
func InitToolsets(passedToolsets []string, readOnly bool, getClient GetClientFn, getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (*toolsets.ToolsetGroup, error) {
1719
// Create a new toolset group
1820
tsg := toolsets.NewToolsetGroup(readOnly)
1921

@@ -49,6 +51,8 @@ func InitToolsets(passedToolsets []string, readOnly bool, getClient GetClientFn,
4951
toolsets.NewServerTool(CreateIssue(getClient, t)),
5052
toolsets.NewServerTool(AddIssueComment(getClient, t)),
5153
toolsets.NewServerTool(UpdateIssue(getClient, t)),
54+
55+
toolsets.NewServerTool(AssignCopilotToIssue(getGQLClient, t)),
5256
)
5357
users := toolsets.NewToolset("users", "GitHub User related tools").
5458
AddReadTools(

third-party-licenses.darwin.md

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Some packages may only be included on certain architectures or operating systems
1616
- [github.com/mark3labs/mcp-go](https://pkg.go.dev/github.com/mark3labs/mcp-go) ([MIT](https://github.com/mark3labs/mcp-go/blob/v0.27.0/LICENSE))
1717
- [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.3/LICENSE))
1818
- [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.9.0/LICENSE))
19+
- [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/48295856cce7/LICENSE))
20+
- [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/ed46e5a46466/LICENSE))
1921
- [github.com/sirupsen/logrus](https://pkg.go.dev/github.com/sirupsen/logrus) ([MIT](https://github.com/sirupsen/logrus/blob/v1.9.3/LICENSE))
2022
- [github.com/sourcegraph/conc](https://pkg.go.dev/github.com/sourcegraph/conc) ([MIT](https://github.com/sourcegraph/conc/blob/v0.3.0/LICENSE))
2123
- [github.com/spf13/afero](https://pkg.go.dev/github.com/spf13/afero) ([Apache-2.0](https://github.com/spf13/afero/blob/v1.14.0/LICENSE.txt))
@@ -25,6 +27,7 @@ Some packages may only be included on certain architectures or operating systems
2527
- [github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper) ([MIT](https://github.com/spf13/viper/blob/v1.20.1/LICENSE))
2628
- [github.com/subosito/gotenv](https://pkg.go.dev/github.com/subosito/gotenv) ([MIT](https://github.com/subosito/gotenv/blob/v1.6.0/LICENSE))
2729
- [github.com/yosida95/uritemplate/v3](https://pkg.go.dev/github.com/yosida95/uritemplate/v3) ([BSD-3-Clause](https://github.com/yosida95/uritemplate/blob/v3.0.2/LICENSE))
30+
- [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.30.0:LICENSE))
2831
- [golang.org/x/sys/unix](https://pkg.go.dev/golang.org/x/sys/unix) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.31.0:LICENSE))
2932
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.23.0:LICENSE))
3033
- [gopkg.in/yaml.v3](https://pkg.go.dev/gopkg.in/yaml.v3) ([MIT](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE))

third-party-licenses.linux.md

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Some packages may only be included on certain architectures or operating systems
1616
- [github.com/mark3labs/mcp-go](https://pkg.go.dev/github.com/mark3labs/mcp-go) ([MIT](https://github.com/mark3labs/mcp-go/blob/v0.27.0/LICENSE))
1717
- [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.3/LICENSE))
1818
- [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.9.0/LICENSE))
19+
- [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/48295856cce7/LICENSE))
20+
- [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/ed46e5a46466/LICENSE))
1921
- [github.com/sirupsen/logrus](https://pkg.go.dev/github.com/sirupsen/logrus) ([MIT](https://github.com/sirupsen/logrus/blob/v1.9.3/LICENSE))
2022
- [github.com/sourcegraph/conc](https://pkg.go.dev/github.com/sourcegraph/conc) ([MIT](https://github.com/sourcegraph/conc/blob/v0.3.0/LICENSE))
2123
- [github.com/spf13/afero](https://pkg.go.dev/github.com/spf13/afero) ([Apache-2.0](https://github.com/spf13/afero/blob/v1.14.0/LICENSE.txt))
@@ -25,6 +27,7 @@ Some packages may only be included on certain architectures or operating systems
2527
- [github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper) ([MIT](https://github.com/spf13/viper/blob/v1.20.1/LICENSE))
2628
- [github.com/subosito/gotenv](https://pkg.go.dev/github.com/subosito/gotenv) ([MIT](https://github.com/subosito/gotenv/blob/v1.6.0/LICENSE))
2729
- [github.com/yosida95/uritemplate/v3](https://pkg.go.dev/github.com/yosida95/uritemplate/v3) ([BSD-3-Clause](https://github.com/yosida95/uritemplate/blob/v3.0.2/LICENSE))
30+
- [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.30.0:LICENSE))
2831
- [golang.org/x/sys/unix](https://pkg.go.dev/golang.org/x/sys/unix) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.31.0:LICENSE))
2932
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.23.0:LICENSE))
3033
- [gopkg.in/yaml.v3](https://pkg.go.dev/gopkg.in/yaml.v3) ([MIT](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE))

third-party-licenses.windows.md

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Some packages may only be included on certain architectures or operating systems
1717
- [github.com/mark3labs/mcp-go](https://pkg.go.dev/github.com/mark3labs/mcp-go) ([MIT](https://github.com/mark3labs/mcp-go/blob/v0.27.0/LICENSE))
1818
- [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.3/LICENSE))
1919
- [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.9.0/LICENSE))
20+
- [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/48295856cce7/LICENSE))
21+
- [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/ed46e5a46466/LICENSE))
2022
- [github.com/sirupsen/logrus](https://pkg.go.dev/github.com/sirupsen/logrus) ([MIT](https://github.com/sirupsen/logrus/blob/v1.9.3/LICENSE))
2123
- [github.com/sourcegraph/conc](https://pkg.go.dev/github.com/sourcegraph/conc) ([MIT](https://github.com/sourcegraph/conc/blob/v0.3.0/LICENSE))
2224
- [github.com/spf13/afero](https://pkg.go.dev/github.com/spf13/afero) ([Apache-2.0](https://github.com/spf13/afero/blob/v1.14.0/LICENSE.txt))
@@ -26,6 +28,7 @@ Some packages may only be included on certain architectures or operating systems
2628
- [github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper) ([MIT](https://github.com/spf13/viper/blob/v1.20.1/LICENSE))
2729
- [github.com/subosito/gotenv](https://pkg.go.dev/github.com/subosito/gotenv) ([MIT](https://github.com/subosito/gotenv/blob/v1.6.0/LICENSE))
2830
- [github.com/yosida95/uritemplate/v3](https://pkg.go.dev/github.com/yosida95/uritemplate/v3) ([BSD-3-Clause](https://github.com/yosida95/uritemplate/blob/v3.0.2/LICENSE))
31+
- [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.30.0:LICENSE))
2932
- [golang.org/x/sys/windows](https://pkg.go.dev/golang.org/x/sys/windows) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.31.0:LICENSE))
3033
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.23.0:LICENSE))
3134
- [gopkg.in/yaml.v3](https://pkg.go.dev/gopkg.in/yaml.v3) ([MIT](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Dmitri Shuralyov
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)