@@ -37,6 +37,9 @@ import {
3737 GitLabNamespaceExistsResponseSchema ,
3838 GitLabProjectSchema ,
3939 GitLabLabelSchema ,
40+ GitLabUserSchema ,
41+ GitLabUsersResponseSchema ,
42+ GetUsersSchema ,
4043 CreateRepositoryOptionsSchema ,
4144 CreateIssueOptionsSchema ,
4245 CreateMergeRequestOptionsSchema ,
@@ -108,6 +111,8 @@ import {
108111 type GitLabNamespaceExistsResponse ,
109112 type GitLabProject ,
110113 type GitLabLabel ,
114+ type GitLabUser ,
115+ type GitLabUsersResponse ,
111116 // Discussion Types
112117 type GitLabDiscussionNote , // Added
113118 type GitLabDiscussion ,
@@ -418,6 +423,11 @@ const allTools = [
418423 "Get the repository tree for a GitLab project (list files and directories)" ,
419424 inputSchema : zodToJsonSchema ( GetRepositoryTreeSchema ) ,
420425 } ,
426+ {
427+ name : "get_users" ,
428+ description : "Get GitLab user details by usernames" ,
429+ inputSchema : zodToJsonSchema ( GetUsersSchema ) ,
430+ } ,
421431] ;
422432
423433// Define which tools are read-only
@@ -440,6 +450,7 @@ const readOnlyTools = [
440450 "list_labels" ,
441451 "get_label" ,
442452 "list_group_projects" ,
453+ "get_users" ,
443454] ;
444455
445456// Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
@@ -2255,6 +2266,65 @@ async function getRepositoryTree(
22552266 return z . array ( GitLabTreeItemSchema ) . parse ( data ) ;
22562267}
22572268
2269+ /**
2270+ * Get a single user from GitLab
2271+ *
2272+ * @param {string } username - The username to look up
2273+ * @returns {Promise<GitLabUser | null> } The user data or null if not found
2274+ */
2275+ async function getUser ( username : string ) : Promise < GitLabUser | null > {
2276+ try {
2277+ const url = new URL ( `${ GITLAB_API_URL } /users` ) ;
2278+ url . searchParams . append ( "username" , username ) ;
2279+
2280+ const response = await fetch ( url . toString ( ) , {
2281+ ...DEFAULT_FETCH_CONFIG ,
2282+ } ) ;
2283+
2284+ await handleGitLabError ( response ) ;
2285+
2286+ const users = await response . json ( ) ;
2287+
2288+ // GitLab returns an array of users that match the username
2289+ if ( Array . isArray ( users ) && users . length > 0 ) {
2290+ // Find exact match for username (case-sensitive)
2291+ const exactMatch = users . find ( user => user . username === username ) ;
2292+ if ( exactMatch ) {
2293+ return GitLabUserSchema . parse ( exactMatch ) ;
2294+ }
2295+ }
2296+
2297+ // No matching user found
2298+ return null ;
2299+ } catch ( error ) {
2300+ console . error ( `Error fetching user by username '${ username } ':` , error ) ;
2301+ return null ;
2302+ }
2303+ }
2304+
2305+ /**
2306+ * Get multiple users from GitLab
2307+ *
2308+ * @param {string[] } usernames - Array of usernames to look up
2309+ * @returns {Promise<GitLabUsersResponse> } Object with usernames as keys and user objects or null as values
2310+ */
2311+ async function getUsers ( usernames : string [ ] ) : Promise < GitLabUsersResponse > {
2312+ const users : Record < string , GitLabUser | null > = { } ;
2313+
2314+ // Process usernames sequentially to avoid rate limiting
2315+ for ( const username of usernames ) {
2316+ try {
2317+ const user = await getUser ( username ) ;
2318+ users [ username ] = user ;
2319+ } catch ( error ) {
2320+ console . error ( `Error processing username '${username } ':`, error ) ;
2321+ users [ username ] = null ;
2322+ }
2323+ }
2324+
2325+ return GitLabUsersResponseSchema . parse ( users ) ;
2326+ }
2327+
22582328server . setRequestHandler ( ListToolsRequestSchema , async ( ) => {
22592329 // Apply read-only filter first
22602330 const tools0 = GITLAB_READ_ONLY_MODE
@@ -2621,6 +2691,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
26212691 content : [ { type : "text" , text : JSON . stringify ( projects , null , 2 ) } ] ,
26222692 } ;
26232693 }
2694+
2695+ case "get_users" : {
2696+ const args = GetUsersSchema . parse ( request . params . arguments ) ;
2697+ const usersMap = await getUsers ( args . usernames ) ;
2698+
2699+ return {
2700+ content : [ { type : "text" , text : JSON . stringify ( usersMap , null , 2 ) } ] ,
2701+ } ;
2702+ }
26242703
26252704 case "create_note" : {
26262705 const args = CreateNoteSchema . parse ( request . params . arguments ) ;
0 commit comments