66 "time"
77
88 ghErrors "github.com/github/github-mcp-server/pkg/errors"
9+ "github.com/github/github-mcp-server/pkg/toolsets"
910 "github.com/github/github-mcp-server/pkg/translations"
1011 "github.com/github/github-mcp-server/pkg/utils"
1112 "github.com/google/jsonschema-go/jsonschema"
@@ -36,8 +37,9 @@ type UserDetails struct {
3637}
3738
3839// GetMe creates a tool to get details of the authenticated user.
39- func GetMe (getClient GetClientFn , t translations.TranslationHelperFunc ) (mcp.Tool , mcp.ToolHandlerFor [map [string ]any , any ]) {
40- return mcp.Tool {
40+ func GetMe (t translations.TranslationHelperFunc ) toolsets.ServerTool {
41+ return NewTool (
42+ mcp.Tool {
4143 Name : "get_me" ,
4244 Description : t ("TOOL_GET_ME_DESCRIPTION" , "Get details of the authenticated GitHub user. Use this when a request is about the user's own profile for GitHub. Or when information is missing to build other tool calls." ),
4345 Annotations : & mcp.ToolAnnotations {
@@ -48,50 +50,53 @@ func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Too
4850 // OpenAI strict mode requires the properties field to be present.
4951 InputSchema : json .RawMessage (`{"type":"object","properties":{}}` ),
5052 },
51- mcp.ToolHandlerFor [map [string ]any , any ](func (ctx context.Context , _ * mcp.CallToolRequest , _ map [string ]any ) (* mcp.CallToolResult , any , error ) {
52- client , err := getClient (ctx )
53- if err != nil {
54- return utils .NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
55- }
53+ func (deps ToolDependencies ) mcp.ToolHandlerFor [map [string ]any , any ] {
54+ return func (ctx context.Context , _ * mcp.CallToolRequest , _ map [string ]any ) (* mcp.CallToolResult , any , error ) {
55+ client , err := deps .GetClient (ctx )
56+ if err != nil {
57+ return utils .NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
58+ }
5659
57- user , res , err := client .Users .Get (ctx , "" )
58- if err != nil {
59- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
60- "failed to get user" ,
61- res ,
62- err ,
63- ), nil , err
64- }
60+ user , res , err := client .Users .Get (ctx , "" )
61+ if err != nil {
62+ return ghErrors .NewGitHubAPIErrorResponse (ctx ,
63+ "failed to get user" ,
64+ res ,
65+ err ,
66+ ), nil , nil
67+ }
6568
66- // Create minimal user representation instead of returning full user object
67- minimalUser := MinimalUser {
68- Login : user .GetLogin (),
69- ID : user .GetID (),
70- ProfileURL : user .GetHTMLURL (),
71- AvatarURL : user .GetAvatarURL (),
72- Details : & UserDetails {
73- Name : user .GetName (),
74- Company : user .GetCompany (),
75- Blog : user .GetBlog (),
76- Location : user .GetLocation (),
77- Email : user .GetEmail (),
78- Hireable : user .GetHireable (),
79- Bio : user .GetBio (),
80- TwitterUsername : user .GetTwitterUsername (),
81- PublicRepos : user .GetPublicRepos (),
82- PublicGists : user .GetPublicGists (),
83- Followers : user .GetFollowers (),
84- Following : user .GetFollowing (),
85- CreatedAt : user .GetCreatedAt ().Time ,
86- UpdatedAt : user .GetUpdatedAt ().Time ,
87- PrivateGists : user .GetPrivateGists (),
88- TotalPrivateRepos : user .GetTotalPrivateRepos (),
89- OwnedPrivateRepos : user .GetOwnedPrivateRepos (),
90- },
91- }
69+ // Create minimal user representation instead of returning full user object
70+ minimalUser := MinimalUser {
71+ Login : user .GetLogin (),
72+ ID : user .GetID (),
73+ ProfileURL : user .GetHTMLURL (),
74+ AvatarURL : user .GetAvatarURL (),
75+ Details : & UserDetails {
76+ Name : user .GetName (),
77+ Company : user .GetCompany (),
78+ Blog : user .GetBlog (),
79+ Location : user .GetLocation (),
80+ Email : user .GetEmail (),
81+ Hireable : user .GetHireable (),
82+ Bio : user .GetBio (),
83+ TwitterUsername : user .GetTwitterUsername (),
84+ PublicRepos : user .GetPublicRepos (),
85+ PublicGists : user .GetPublicGists (),
86+ Followers : user .GetFollowers (),
87+ Following : user .GetFollowing (),
88+ CreatedAt : user .GetCreatedAt ().Time ,
89+ UpdatedAt : user .GetUpdatedAt ().Time ,
90+ PrivateGists : user .GetPrivateGists (),
91+ TotalPrivateRepos : user .GetTotalPrivateRepos (),
92+ OwnedPrivateRepos : user .GetOwnedPrivateRepos (),
93+ },
94+ }
9295
93- return MarshalledTextResult (minimalUser ), nil , nil
94- })
96+ return MarshalledTextResult (minimalUser ), nil , nil
97+ }
98+ },
99+ )
95100}
96101
97102type TeamInfo struct {
@@ -105,8 +110,9 @@ type OrganizationTeams struct {
105110 Teams []TeamInfo `json:"teams"`
106111}
107112
108- func GetTeams (getClient GetClientFn , getGQLClient GetGQLClientFn , t translations.TranslationHelperFunc ) (mcp.Tool , mcp.ToolHandlerFor [map [string ]any , any ]) {
109- return mcp.Tool {
113+ func GetTeams (t translations.TranslationHelperFunc ) toolsets.ServerTool {
114+ return NewTool (
115+ mcp.Tool {
110116 Name : "get_teams" ,
111117 Description : t ("TOOL_GET_TEAMS_DESCRIPTION" , "Get details of the teams the user is a member of. Limited to organizations accessible with current credentials" ),
112118 Annotations : & mcp.ToolAnnotations {
@@ -123,84 +129,88 @@ func GetTeams(getClient GetClientFn, getGQLClient GetGQLClientFn, t translations
123129 },
124130 },
125131 },
126- func (ctx context.Context , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
127- user , err := OptionalParam [string ](args , "user" )
128- if err != nil {
129- return utils .NewToolResultError (err .Error ()), nil , nil
130- }
131-
132- var username string
133- if user != "" {
134- username = user
135- } else {
136- client , err := getClient (ctx )
132+ func (deps ToolDependencies ) mcp.ToolHandlerFor [map [string ]any , any ] {
133+ return func (ctx context.Context , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
134+ user , err := OptionalParam [string ](args , "user" )
137135 if err != nil {
138- return utils .NewToolResultErrorFromErr ( "failed to get GitHub client" , err ), nil , nil
136+ return utils .NewToolResultError ( err . Error () ), nil , nil
139137 }
140138
141- userResp , res , err := client .Users .Get (ctx , "" )
139+ var username string
140+ if user != "" {
141+ username = user
142+ } else {
143+ client , err := deps .GetClient (ctx )
144+ if err != nil {
145+ return utils .NewToolResultErrorFromErr ("failed to get GitHub client" , err ), nil , nil
146+ }
147+
148+ userResp , res , err := client .Users .Get (ctx , "" )
149+ if err != nil {
150+ return ghErrors .NewGitHubAPIErrorResponse (ctx ,
151+ "failed to get user" ,
152+ res ,
153+ err ,
154+ ), nil , nil
155+ }
156+ username = userResp .GetLogin ()
157+ }
158+
159+ gqlClient , err := deps .GetGQLClient (ctx )
142160 if err != nil {
143- return ghErrors .NewGitHubAPIErrorResponse (ctx ,
144- "failed to get user" ,
145- res ,
146- err ,
147- ), nil , nil
161+ return utils .NewToolResultErrorFromErr ("failed to get GitHub GQL client" , err ), nil , nil
148162 }
149- username = userResp .GetLogin ()
150- }
151163
152- gqlClient , err := getGQLClient (ctx )
153- if err != nil {
154- return utils .NewToolResultErrorFromErr ("failed to get GitHub GQL client" , err ), nil , nil
155- }
164+ var q struct {
165+ User struct {
166+ Organizations struct {
167+ Nodes []struct {
168+ Login githubv4.String
169+ Teams struct {
170+ Nodes []struct {
171+ Name githubv4.String
172+ Slug githubv4.String
173+ Description githubv4.String
174+ }
175+ } `graphql:"teams(first: 100, userLogins: [$login])"`
176+ }
177+ } `graphql:"organizations(first: 100)"`
178+ } `graphql:"user(login: $login)"`
179+ }
180+ vars := map [string ]interface {}{
181+ "login" : githubv4 .String (username ),
182+ }
183+ if err := gqlClient .Query (ctx , & q , vars ); err != nil {
184+ return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to find teams" , err ), nil , nil
185+ }
156186
157- var q struct {
158- User struct {
159- Organizations struct {
160- Nodes []struct {
161- Login githubv4.String
162- Teams struct {
163- Nodes []struct {
164- Name githubv4.String
165- Slug githubv4.String
166- Description githubv4.String
167- }
168- } `graphql:"teams(first: 100, userLogins: [$login])"`
169- }
170- } `graphql:"organizations(first: 100)"`
171- } `graphql:"user(login: $login)"`
172- }
173- vars := map [string ]interface {}{
174- "login" : githubv4 .String (username ),
175- }
176- if err := gqlClient .Query (ctx , & q , vars ); err != nil {
177- return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to find teams" , err ), nil , nil
178- }
187+ var organizations []OrganizationTeams
188+ for _ , org := range q .User .Organizations .Nodes {
189+ orgTeams := OrganizationTeams {
190+ Org : string (org .Login ),
191+ Teams : make ([]TeamInfo , 0 , len (org .Teams .Nodes )),
192+ }
179193
180- var organizations []OrganizationTeams
181- for _ , org := range q .User .Organizations .Nodes {
182- orgTeams := OrganizationTeams {
183- Org : string (org .Login ),
184- Teams : make ([]TeamInfo , 0 , len (org .Teams .Nodes )),
185- }
194+ for _ , team := range org .Teams .Nodes {
195+ orgTeams .Teams = append (orgTeams .Teams , TeamInfo {
196+ Name : string (team .Name ),
197+ Slug : string (team .Slug ),
198+ Description : string (team .Description ),
199+ })
200+ }
186201
187- for _ , team := range org .Teams .Nodes {
188- orgTeams .Teams = append (orgTeams .Teams , TeamInfo {
189- Name : string (team .Name ),
190- Slug : string (team .Slug ),
191- Description : string (team .Description ),
192- })
202+ organizations = append (organizations , orgTeams )
193203 }
194204
195- organizations = append (organizations , orgTeams )
205+ return MarshalledTextResult (organizations ), nil , nil
196206 }
197-
198- return MarshalledTextResult (organizations ), nil , nil
199- }
207+ },
208+ )
200209}
201210
202- func GetTeamMembers (getGQLClient GetGQLClientFn , t translations.TranslationHelperFunc ) (mcp.Tool , mcp.ToolHandlerFor [map [string ]any , any ]) {
203- return mcp.Tool {
211+ func GetTeamMembers (t translations.TranslationHelperFunc ) toolsets.ServerTool {
212+ return NewTool (
213+ mcp.Tool {
204214 Name : "get_team_members" ,
205215 Description : t ("TOOL_GET_TEAM_MEMBERS_DESCRIPTION" , "Get member usernames of a specific team in an organization. Limited to organizations accessible with current credentials" ),
206216 Annotations : & mcp.ToolAnnotations {
@@ -222,46 +232,49 @@ func GetTeamMembers(getGQLClient GetGQLClientFn, t translations.TranslationHelpe
222232 Required : []string {"org" , "team_slug" },
223233 },
224234 },
225- func (ctx context.Context , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
226- org , err := RequiredParam [string ](args , "org" )
227- if err != nil {
228- return utils .NewToolResultError (err .Error ()), nil , nil
229- }
235+ func (deps ToolDependencies ) mcp.ToolHandlerFor [map [string ]any , any ] {
236+ return func (ctx context.Context , _ * mcp.CallToolRequest , args map [string ]any ) (* mcp.CallToolResult , any , error ) {
237+ org , err := RequiredParam [string ](args , "org" )
238+ if err != nil {
239+ return utils .NewToolResultError (err .Error ()), nil , nil
240+ }
230241
231- teamSlug , err := RequiredParam [string ](args , "team_slug" )
232- if err != nil {
233- return utils .NewToolResultError (err .Error ()), nil , nil
234- }
242+ teamSlug , err := RequiredParam [string ](args , "team_slug" )
243+ if err != nil {
244+ return utils .NewToolResultError (err .Error ()), nil , nil
245+ }
235246
236- gqlClient , err := getGQLClient (ctx )
237- if err != nil {
238- return utils .NewToolResultErrorFromErr ("failed to get GitHub GQL client" , err ), nil , nil
239- }
247+ gqlClient , err := deps . GetGQLClient (ctx )
248+ if err != nil {
249+ return utils .NewToolResultErrorFromErr ("failed to get GitHub GQL client" , err ), nil , nil
250+ }
240251
241- var q struct {
242- Organization struct {
243- Team struct {
244- Members struct {
245- Nodes []struct {
246- Login githubv4.String
247- }
248- } `graphql:"members(first: 100)"`
249- } `graphql:"team(slug: $teamSlug)"`
250- } `graphql:"organization(login: $org)"`
251- }
252- vars := map [string ]interface {}{
253- "org" : githubv4 .String (org ),
254- "teamSlug" : githubv4 .String (teamSlug ),
255- }
256- if err := gqlClient .Query (ctx , & q , vars ); err != nil {
257- return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to get team members" , err ), nil , nil
258- }
252+ var q struct {
253+ Organization struct {
254+ Team struct {
255+ Members struct {
256+ Nodes []struct {
257+ Login githubv4.String
258+ }
259+ } `graphql:"members(first: 100)"`
260+ } `graphql:"team(slug: $teamSlug)"`
261+ } `graphql:"organization(login: $org)"`
262+ }
263+ vars := map [string ]interface {}{
264+ "org" : githubv4 .String (org ),
265+ "teamSlug" : githubv4 .String (teamSlug ),
266+ }
267+ if err := gqlClient .Query (ctx , & q , vars ); err != nil {
268+ return ghErrors .NewGitHubGraphQLErrorResponse (ctx , "Failed to get team members" , err ), nil , nil
269+ }
259270
260- var members []string
261- for _ , member := range q .Organization .Team .Members .Nodes {
262- members = append (members , string (member .Login ))
263- }
271+ var members []string
272+ for _ , member := range q .Organization .Team .Members .Nodes {
273+ members = append (members , string (member .Login ))
274+ }
264275
265- return MarshalledTextResult (members ), nil , nil
266- }
276+ return MarshalledTextResult (members ), nil , nil
277+ }
278+ },
279+ )
267280}
0 commit comments