@@ -159,105 +159,194 @@ type MinimalSearchUsersResult struct {
159159 Items []MinimalUser `json:"items"`
160160}
161161
162- // SearchUsers creates a tool to search for GitHub users.
163- func SearchUsers (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
164- return mcp .NewTool ("search_users" ,
165- mcp .WithDescription (t ("TOOL_SEARCH_USERS_DESCRIPTION" , "Search for GitHub users" )),
166- mcp .WithToolAnnotation (mcp.ToolAnnotation {
167- Title : t ("TOOL_SEARCH_USERS_USER_TITLE" , "Search users" ),
168- ReadOnlyHint : toBoolPtr (true ),
169- }),
170- mcp .WithString ("q" ,
171- mcp .Required (),
172- mcp .Description ("Search query using GitHub users search syntax" ),
173- ),
174- mcp .WithString ("sort" ,
175- mcp .Description ("Sort field by category" ),
176- mcp .Enum ("followers" , "repositories" , "joined" ),
177- ),
178- mcp .WithString ("order" ,
179- mcp .Description ("Sort order" ),
180- mcp .Enum ("asc" , "desc" ),
181- ),
182- WithPagination (),
183- ),
184- func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
185- query , err := requiredParam [string ](request , "q" )
186- if err != nil {
187- return mcp .NewToolResultError (err .Error ()), nil
188- }
189- sort , err := OptionalParam [string ](request , "sort" )
190- if err != nil {
191- return mcp .NewToolResultError (err .Error ()), nil
162+ // shared handler for user/org search
163+ func minimalAccountSearchHandler (accountType string , query string , getClient GetClientFn , ctx context.Context , sort , order string , pagination PaginationParams ) (* MinimalSearchUsersResult , error ) {
164+ opts := & github.SearchOptions {
165+ Sort : sort ,
166+ Order : order ,
167+ ListOptions : github.ListOptions {
168+ PerPage : pagination .perPage ,
169+ Page : pagination .page ,
170+ },
171+ }
172+
173+ client , err := getClient (ctx )
174+ if err != nil {
175+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
176+ }
177+
178+ searchQuery := "type:" + accountType + " " + query
179+ result , resp , err := client .Search .Users (ctx , searchQuery , opts )
180+ if err != nil {
181+ return nil , fmt .Errorf ("failed to search %ss: %w" , accountType , err )
182+ }
183+ defer func () { _ = resp .Body .Close () }()
184+
185+ if resp .StatusCode != 200 {
186+ body , err := io .ReadAll (resp .Body )
187+ if err != nil {
188+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
189+ }
190+ return nil , fmt .Errorf ("failed to search %ss: %s" , accountType , string (body ))
191+ }
192+
193+ minimalUsers := make ([]MinimalUser , 0 , len (result .Users ))
194+ for _ , user := range result .Users {
195+ if user .Login != nil {
196+ mu := MinimalUser {Login : * user .Login }
197+ if user .ID != nil {
198+ mu .ID = * user .ID
192199 }
193- order , err := OptionalParam [string ](request , "order" )
194- if err != nil {
195- return mcp .NewToolResultError (err .Error ()), nil
200+ if user .HTMLURL != nil {
201+ mu .ProfileURL = * user .HTMLURL
196202 }
197- pagination , err := OptionalPaginationParams (request )
198- if err != nil {
199- return mcp .NewToolResultError (err .Error ()), nil
203+ if user .AvatarURL != nil {
204+ mu .AvatarURL = * user .AvatarURL
200205 }
206+ minimalUsers = append (minimalUsers , mu )
207+ }
208+ }
209+ minimalResp := & MinimalSearchUsersResult {
210+ TotalCount : result .GetTotal (),
211+ IncompleteResults : result .GetIncompleteResults (),
212+ Items : minimalUsers ,
213+ }
214+ if result .Total != nil {
215+ minimalResp .TotalCount = * result .Total
216+ }
217+ if result .IncompleteResults != nil {
218+ minimalResp .IncompleteResults = * result .IncompleteResults
219+ }
220+ return minimalResp , nil
221+ }
201222
202- opts := & github.SearchOptions {
203- Sort : sort ,
204- Order : order ,
205- ListOptions : github.ListOptions {
206- PerPage : pagination .perPage ,
207- Page : pagination .page ,
208- },
209- }
223+ // SearchUsers creates a tool to search for GitHub users.
224+ func userOrOrgHandler (accountType string , getClient GetClientFn ) server.ToolHandlerFunc {
225+ return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
226+ query , err := requiredParam [string ](request , "q" )
227+ if err != nil {
228+ return mcp .NewToolResultError (err .Error ()), nil
229+ }
230+ sort , err := OptionalParam [string ](request , "sort" )
231+ if err != nil {
232+ return mcp .NewToolResultError (err .Error ()), nil
233+ }
234+ order , err := OptionalParam [string ](request , "order" )
235+ if err != nil {
236+ return mcp .NewToolResultError (err .Error ()), nil
237+ }
238+ pagination , err := OptionalPaginationParams (request )
239+ if err != nil {
240+ return mcp .NewToolResultError (err .Error ()), nil
241+ }
210242
211- client , err := getClient (ctx )
212- if err != nil {
213- return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
214- }
243+ opts := & github.SearchOptions {
244+ Sort : sort ,
245+ Order : order ,
246+ ListOptions : github.ListOptions {
247+ PerPage : pagination .perPage ,
248+ Page : pagination .page ,
249+ },
250+ }
251+
252+ client , err := getClient (ctx )
253+ if err != nil {
254+ return nil , fmt .Errorf ("failed to get GitHub client: %w" , err )
255+ }
215256
216- result , resp , err := client .Search .Users (ctx , "type:user " + query , opts )
257+ searchQuery := "type:" + accountType + " " + query
258+ result , resp , err := client .Search .Users (ctx , searchQuery , opts )
259+ if err != nil {
260+ return nil , fmt .Errorf ("failed to search %ss: %w" , accountType , err )
261+ }
262+ defer func () { _ = resp .Body .Close () }()
263+
264+ if resp .StatusCode != 200 {
265+ body , err := io .ReadAll (resp .Body )
217266 if err != nil {
218- return nil , fmt .Errorf ("failed to search users : %w" , err )
267+ return nil , fmt .Errorf ("failed to read response body : %w" , err )
219268 }
220- defer func () { _ = resp .Body .Close () }()
269+ return mcp .NewToolResultError (fmt .Sprintf ("failed to search %ss: %s" , accountType , string (body ))), nil
270+ }
221271
222- if resp .StatusCode != 200 {
223- body , err := io .ReadAll (resp .Body )
224- if err != nil {
225- return nil , fmt .Errorf ("failed to read response body: %w" , err )
272+ minimalUsers := make ([]MinimalUser , 0 , len (result .Users ))
273+ for _ , user := range result .Users {
274+ if user .Login != nil {
275+ mu := MinimalUser {Login : * user .Login }
276+ if user .ID != nil {
277+ mu .ID = * user .ID
226278 }
227- return mcp .NewToolResultError (fmt .Sprintf ("failed to search users: %s" , string (body ))), nil
228- }
229-
230- minimalUsers := make ([]MinimalUser , 0 , len (result .Users ))
231- for _ , user := range result .Users {
232- if user .Login != nil {
233- mu := MinimalUser {Login : * user .Login }
234- if user .ID != nil {
235- mu .ID = * user .ID
236- }
237- if user .HTMLURL != nil {
238- mu .ProfileURL = * user .HTMLURL
239- }
240- if user .AvatarURL != nil {
241- mu .AvatarURL = * user .AvatarURL
242- }
243- minimalUsers = append (minimalUsers , mu )
279+ if user .HTMLURL != nil {
280+ mu .ProfileURL = * user .HTMLURL
244281 }
282+ if user .AvatarURL != nil {
283+ mu .AvatarURL = * user .AvatarURL
284+ }
285+ minimalUsers = append (minimalUsers , mu )
245286 }
246- minimalResp := MinimalSearchUsersResult {
247- TotalCount : result .GetTotal (),
248- IncompleteResults : result .GetIncompleteResults (),
249- Items : minimalUsers ,
250- }
251- if result .Total != nil {
252- minimalResp .TotalCount = * result .Total
253- }
254- if result .IncompleteResults != nil {
255- minimalResp .IncompleteResults = * result .IncompleteResults
256- }
257- r , err := json .Marshal (minimalResp )
258- if err != nil {
259- return nil , fmt .Errorf ("failed to marshal response: %w" , err )
260- }
261- return mcp .NewToolResultText (string (r )), nil
262287 }
288+ minimalResp := & MinimalSearchUsersResult {
289+ TotalCount : result .GetTotal (),
290+ IncompleteResults : result .GetIncompleteResults (),
291+ Items : minimalUsers ,
292+ }
293+ if result .Total != nil {
294+ minimalResp .TotalCount = * result .Total
295+ }
296+ if result .IncompleteResults != nil {
297+ minimalResp .IncompleteResults = * result .IncompleteResults
298+ }
299+ r , err := json .Marshal (minimalResp )
300+ if err != nil {
301+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
302+ }
303+ return mcp .NewToolResultText (string (r )), nil
304+ }
305+ }
306+
307+ func SearchUsers (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
308+ return mcp .NewTool ("search_users" ,
309+ mcp .WithDescription (t ("TOOL_SEARCH_USERS_DESCRIPTION" , "Search for GitHub users exlusively" )),
310+ mcp .WithToolAnnotation (mcp.ToolAnnotation {
311+ Title : t ("TOOL_SEARCH_USERS_USER_TITLE" , "Search users" ),
312+ ReadOnlyHint : toBoolPtr (true ),
313+ }),
314+ mcp .WithString ("q" ,
315+ mcp .Required (),
316+ mcp .Description ("Search query using GitHub users search syntax scoped to type:user" ),
317+ ),
318+ mcp .WithString ("sort" ,
319+ mcp .Description ("Sort field by category" ),
320+ mcp .Enum ("followers" , "repositories" , "joined" ),
321+ ),
322+ mcp .WithString ("order" ,
323+ mcp .Description ("Sort order" ),
324+ mcp .Enum ("asc" , "desc" ),
325+ ),
326+ WithPagination (),
327+ ), userOrOrgHandler ("user" , getClient )
328+ }
329+
330+ // SearchOrgs creates a tool to search for GitHub organizations.
331+ func SearchOrgs (getClient GetClientFn , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
332+ return mcp .NewTool ("search_orgs" ,
333+ mcp .WithDescription (t ("TOOL_SEARCH_ORGS_DESCRIPTION" , "Search for GitHub organizations exclusively" )),
334+ mcp .WithToolAnnotation (mcp.ToolAnnotation {
335+ Title : t ("TOOL_SEARCH_ORGS_USER_TITLE" , "Search organizations" ),
336+ ReadOnlyHint : toBoolPtr (true ),
337+ }),
338+ mcp .WithString ("q" ,
339+ mcp .Required (),
340+ mcp .Description ("Search query using GitHub organizations search syntax scoped to type:org" ),
341+ ),
342+ mcp .WithString ("sort" ,
343+ mcp .Description ("Sort field by category" ),
344+ mcp .Enum ("followers" , "repositories" , "joined" ),
345+ ),
346+ mcp .WithString ("order" ,
347+ mcp .Description ("Sort order" ),
348+ mcp .Enum ("asc" , "desc" ),
349+ ),
350+ WithPagination (),
351+ ), userOrOrgHandler ("org" , getClient )
263352}
0 commit comments