feat: add get_user tool to fetch user by username (#1970)#2246
feat: add get_user tool to fetch user by username (#1970)#2246sierikov wants to merge 1 commit intogithub:mainfrom
Conversation
Add a new `get_user` MCP tool that retrieves a GitHub user's profile
by username using the public `GET /users/{username}` endpoint. Returns
a MinimalUser with UserDetails.
There was a problem hiding this comment.
Pull request overview
Adds a new MCP tool (get_user) to fetch a GitHub user profile by username via GET /users/{username}, returning the existing token-efficient MinimalUser shape with UserDetails.
Changes:
- Added
get_usertool implementation and tool snapshot. - Added unit tests covering success and error paths for
get_user. - Registered the tool in the users toolset and updated docs/README generation output.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/github/users.go | Introduces the get_user tool definition and handler that calls client.Users.Get(ctx, username) and returns MinimalUser + UserDetails. |
| pkg/github/users_test.go | Adds tests validating the tool schema snapshot and handler behavior across success/error scenarios. |
| pkg/github/tools.go | Registers GetUser in AllTools under the users tools grouping. |
| pkg/github/helper_test.go | Renames the existing /user endpoint constant to avoid naming conflict; adds /users/{username} endpoint constant. |
| pkg/github/context_tools_test.go | Updates references to the renamed authenticated-user endpoint constant. |
| pkg/github/toolsnaps/get_user.snap | Adds the generated schema snapshot for the new tool. |
| README.md | Updates tool documentation list to include get_user. |
| ToolsetMetadataUsers, | ||
| mcp.Tool{ | ||
| Name: "get_user", | ||
| Description: t("TOOL_GET_USER_DESCRIPTION", "Get user by username. Use this when you need information about specific GitHub user."), |
There was a problem hiding this comment.
Tool description has grammatical issues (missing article) and reads a bit awkwardly. Consider updating it to something like “Get a user by username. Use this when you need information about a specific GitHub user.” so the generated docs/tooling text is clearer.
| Description: t("TOOL_GET_USER_DESCRIPTION", "Get user by username. Use this when you need information about specific GitHub user."), | |
| Description: t("TOOL_GET_USER_DESCRIPTION", "Get a user by username. Use this when you need information about a specific GitHub user."), |
| minimalUser := MinimalUser{ | ||
| Login: user.GetLogin(), | ||
| ID: user.GetID(), | ||
| ProfileURL: user.GetHTMLURL(), | ||
| AvatarURL: user.GetAvatarURL(), | ||
| Details: &UserDetails{ | ||
| Name: user.GetName(), | ||
| Company: user.GetCompany(), | ||
| Blog: user.GetBlog(), | ||
| Location: user.GetLocation(), | ||
| Email: user.GetEmail(), | ||
| Hireable: user.GetHireable(), | ||
| Bio: user.GetBio(), | ||
| TwitterUsername: user.GetTwitterUsername(), | ||
| PublicRepos: user.GetPublicRepos(), | ||
| PublicGists: user.GetPublicGists(), | ||
| Followers: user.GetFollowers(), | ||
| Following: user.GetFollowing(), | ||
| CreatedAt: user.GetCreatedAt().Time, | ||
| UpdatedAt: user.GetUpdatedAt().Time, | ||
| PrivateGists: user.GetPrivateGists(), | ||
| TotalPrivateRepos: user.GetTotalPrivateRepos(), | ||
| OwnedPrivateRepos: user.GetOwnedPrivateRepos(), | ||
| }, | ||
| } |
There was a problem hiding this comment.
The MinimalUser/UserDetails mapping here duplicates the same field-by-field conversion logic in GetMe (context_tools.go). Since this is now shared behavior across multiple tools, consider extracting a small helper (e.g., buildMinimalUserWithDetails(*github.User)) to avoid future divergence when the shape changes.
| name: "user not found", | ||
| mockedClient: MockHTTPClientWithHandler(badRequestHandler("user not found")), | ||
| requestArgs: map[string]any{ | ||
| "username": "other-non-existent-john-doe", | ||
| }, | ||
| expectError: true, | ||
| expectedErrMsg: "failed to get user", | ||
| }, |
There was a problem hiding this comment.
The “user not found” test case currently uses badRequestHandler(), which returns HTTP 400. The real GET /users/{username} not-found path is a 404, and error formatting can differ by status code. Adjust this test to return http.StatusNotFound (with a typical {"message":"Not Found"} body) so the not-found behavior is actually covered.
Summary
Add a new
get_userMCP tool that retrieves a GitHub user's profile by username using the publicGET /users/{username}endpoint. Returns aMinimalUserwithUserDetailsto keep responses token-efficient for LLM consumers.Why
Fixes #1970
What changed
pkg/github/users.gowithGetUsertool definition andgetUserHandlerimplementationpkg/github/users_test.gowith tests for successful lookup, user not found, and API error casesGetUserin theuserstoolset inpkg/github/tools.goGetUsertest constant toGetAuthenticatedUserinpkg/github/helper_test.goto avoid conflict with the new function name, and addedGetUserByUsernameconstantpkg/github/context_tools_test.goto use renamed constantpkg/github/__toolsnaps__/get_user.snapand updatedREADME.mdviascript/generate-docsMCP impact
get_useraccepts a requiredusernamestring parameter and returns the user's profile as aMinimalUserwithUserDetails. Annotated withReadOnlyHint: true. Registered under theuserstoolset.Prompts tested (tool changes only)
Security / limits
GET /users/{username}endpoint. Scopes arenilsince GitHub returns data based on the token's existing permissions without requiring additional scopes.MinimalUserwithUserDetail rather than the fullgithub.User` object.Tool renaming
Lint & tests
./script/lint./script/testDocs
README.mdregenerated viascript/generate-docs