Skip to content

Commit acba284

Browse files
cleanup search_users response (#486)
1 parent 23b16cf commit acba284

File tree

4 files changed

+261
-93
lines changed

4 files changed

+261
-93
lines changed

pkg/github/__toolsnaps__/search_users.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"title": "Search users",
44
"readOnlyHint": true
55
},
6-
"description": "Search for GitHub users",
6+
"description": "Search for GitHub users exclusively",
77
"inputSchema": {
88
"properties": {
99
"order": {
@@ -25,8 +25,8 @@
2525
"minimum": 1,
2626
"type": "number"
2727
},
28-
"q": {
29-
"description": "Search query using GitHub users search syntax",
28+
"query": {
29+
"description": "Search query using GitHub users search syntax scoped to type:user",
3030
"type": "string"
3131
},
3232
"sort": {
@@ -40,7 +40,7 @@
4040
}
4141
},
4242
"required": [
43-
"q"
43+
"query"
4444
],
4545
"type": "object"
4646
},

pkg/github/search.go

Lines changed: 123 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -168,100 +168,139 @@ type MinimalSearchUsersResult struct {
168168
Items []MinimalUser `json:"items"`
169169
}
170170

171-
// SearchUsers creates a tool to search for GitHub users.
172-
func SearchUsers(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
173-
return mcp.NewTool("search_users",
174-
mcp.WithDescription(t("TOOL_SEARCH_USERS_DESCRIPTION", "Search for GitHub users")),
175-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
176-
Title: t("TOOL_SEARCH_USERS_USER_TITLE", "Search users"),
177-
ReadOnlyHint: ToBoolPtr(true),
178-
}),
179-
mcp.WithString("q",
180-
mcp.Required(),
181-
mcp.Description("Search query using GitHub users search syntax"),
182-
),
183-
mcp.WithString("sort",
184-
mcp.Description("Sort field by category"),
185-
mcp.Enum("followers", "repositories", "joined"),
186-
),
187-
mcp.WithString("order",
188-
mcp.Description("Sort order"),
189-
mcp.Enum("asc", "desc"),
190-
),
191-
WithPagination(),
192-
),
193-
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
194-
query, err := RequiredParam[string](request, "q")
195-
if err != nil {
196-
return mcp.NewToolResultError(err.Error()), nil
197-
}
198-
sort, err := OptionalParam[string](request, "sort")
199-
if err != nil {
200-
return mcp.NewToolResultError(err.Error()), nil
201-
}
202-
order, err := OptionalParam[string](request, "order")
203-
if err != nil {
204-
return mcp.NewToolResultError(err.Error()), nil
205-
}
206-
pagination, err := OptionalPaginationParams(request)
207-
if err != nil {
208-
return mcp.NewToolResultError(err.Error()), nil
209-
}
171+
func userOrOrgHandler(accountType string, getClient GetClientFn) server.ToolHandlerFunc {
172+
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
173+
query, err := RequiredParam[string](request, "query")
174+
if err != nil {
175+
return mcp.NewToolResultError(err.Error()), nil
176+
}
177+
sort, err := OptionalParam[string](request, "sort")
178+
if err != nil {
179+
return mcp.NewToolResultError(err.Error()), nil
180+
}
181+
order, err := OptionalParam[string](request, "order")
182+
if err != nil {
183+
return mcp.NewToolResultError(err.Error()), nil
184+
}
185+
pagination, err := OptionalPaginationParams(request)
186+
if err != nil {
187+
return mcp.NewToolResultError(err.Error()), nil
188+
}
210189

211-
opts := &github.SearchOptions{
212-
Sort: sort,
213-
Order: order,
214-
ListOptions: github.ListOptions{
215-
PerPage: pagination.perPage,
216-
Page: pagination.page,
217-
},
218-
}
190+
opts := &github.SearchOptions{
191+
Sort: sort,
192+
Order: order,
193+
ListOptions: github.ListOptions{
194+
PerPage: pagination.perPage,
195+
Page: pagination.page,
196+
},
197+
}
219198

220-
client, err := getClient(ctx)
221-
if err != nil {
222-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
223-
}
199+
client, err := getClient(ctx)
200+
if err != nil {
201+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
202+
}
224203

225-
result, resp, err := client.Search.Users(ctx, "type:user "+query, opts)
204+
searchQuery := "type:" + accountType + " " + query
205+
result, resp, err := client.Search.Users(ctx, searchQuery, opts)
206+
if err != nil {
207+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
208+
fmt.Sprintf("failed to search %ss with query '%s'", accountType, query),
209+
resp,
210+
err,
211+
), nil
212+
}
213+
defer func() { _ = resp.Body.Close() }()
214+
215+
if resp.StatusCode != 200 {
216+
body, err := io.ReadAll(resp.Body)
226217
if err != nil {
227-
return ghErrors.NewGitHubAPIErrorResponse(ctx,
228-
fmt.Sprintf("failed to search users with query '%s'", query),
229-
resp,
230-
err,
231-
), nil
218+
return nil, fmt.Errorf("failed to read response body: %w", err)
232219
}
233-
defer func() { _ = resp.Body.Close() }()
220+
return mcp.NewToolResultError(fmt.Sprintf("failed to search %ss: %s", accountType, string(body))), nil
221+
}
234222

235-
if resp.StatusCode != 200 {
236-
body, err := io.ReadAll(resp.Body)
237-
if err != nil {
238-
return nil, fmt.Errorf("failed to read response body: %w", err)
239-
}
240-
return mcp.NewToolResultError(fmt.Sprintf("failed to search users: %s", string(body))), nil
241-
}
223+
minimalUsers := make([]MinimalUser, 0, len(result.Users))
242224

243-
minimalUsers := make([]MinimalUser, 0, len(result.Users))
244-
for _, user := range result.Users {
245-
mu := MinimalUser{
246-
Login: user.GetLogin(),
247-
ID: user.GetID(),
248-
ProfileURL: user.GetHTMLURL(),
249-
AvatarURL: user.GetAvatarURL(),
225+
for _, user := range result.Users {
226+
if user.Login != nil {
227+
mu := MinimalUser{Login: *user.Login}
228+
if user.ID != nil {
229+
mu.ID = *user.ID
230+
}
231+
if user.HTMLURL != nil {
232+
mu.ProfileURL = *user.HTMLURL
233+
}
234+
if user.AvatarURL != nil {
235+
mu.AvatarURL = *user.AvatarURL
250236
}
251-
252237
minimalUsers = append(minimalUsers, mu)
253238
}
239+
}
240+
minimalResp := &MinimalSearchUsersResult{
241+
TotalCount: result.GetTotal(),
242+
IncompleteResults: result.GetIncompleteResults(),
243+
Items: minimalUsers,
244+
}
245+
if result.Total != nil {
246+
minimalResp.TotalCount = *result.Total
247+
}
248+
if result.IncompleteResults != nil {
249+
minimalResp.IncompleteResults = *result.IncompleteResults
250+
}
254251

255-
minimalResp := MinimalSearchUsersResult{
256-
TotalCount: result.GetTotal(),
257-
IncompleteResults: result.GetIncompleteResults(),
258-
Items: minimalUsers,
259-
}
260-
261-
r, err := json.Marshal(minimalResp)
262-
if err != nil {
263-
return nil, fmt.Errorf("failed to marshal response: %w", err)
264-
}
265-
return mcp.NewToolResultText(string(r)), nil
252+
r, err := json.Marshal(minimalResp)
253+
if err != nil {
254+
return nil, fmt.Errorf("failed to marshal response: %w", err)
266255
}
256+
return mcp.NewToolResultText(string(r)), nil
257+
}
258+
}
259+
260+
// SearchUsers creates a tool to search for GitHub users.
261+
func SearchUsers(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
262+
return mcp.NewTool("search_users",
263+
mcp.WithDescription(t("TOOL_SEARCH_USERS_DESCRIPTION", "Search for GitHub users exclusively")),
264+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
265+
Title: t("TOOL_SEARCH_USERS_USER_TITLE", "Search users"),
266+
ReadOnlyHint: ToBoolPtr(true),
267+
}),
268+
mcp.WithString("query",
269+
mcp.Required(),
270+
mcp.Description("Search query using GitHub users search syntax scoped to type:user"),
271+
),
272+
mcp.WithString("sort",
273+
mcp.Description("Sort field by category"),
274+
mcp.Enum("followers", "repositories", "joined"),
275+
),
276+
mcp.WithString("order",
277+
mcp.Description("Sort order"),
278+
mcp.Enum("asc", "desc"),
279+
),
280+
WithPagination(),
281+
), userOrOrgHandler("user", getClient)
282+
}
283+
284+
// SearchOrgs creates a tool to search for GitHub organizations.
285+
func SearchOrgs(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
286+
return mcp.NewTool("search_orgs",
287+
mcp.WithDescription(t("TOOL_SEARCH_ORGS_DESCRIPTION", "Search for GitHub organizations exclusively")),
288+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
289+
Title: t("TOOL_SEARCH_ORGS_USER_TITLE", "Search organizations"),
290+
ReadOnlyHint: ToBoolPtr(true),
291+
}),
292+
mcp.WithString("query",
293+
mcp.Required(),
294+
mcp.Description("Search query using GitHub organizations search syntax scoped to type:org"),
295+
),
296+
mcp.WithString("sort",
297+
mcp.Description("Sort field by category"),
298+
mcp.Enum("followers", "repositories", "joined"),
299+
),
300+
mcp.WithString("order",
301+
mcp.Description("Sort order"),
302+
mcp.Enum("asc", "desc"),
303+
),
304+
WithPagination(),
305+
), userOrOrgHandler("org", getClient)
267306
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy