feat: add user retrieval functions and schemas for GitLab API integration
This commit is contained in:
79
index.ts
79
index.ts
@ -37,6 +37,9 @@ import {
|
|||||||
GitLabNamespaceExistsResponseSchema,
|
GitLabNamespaceExistsResponseSchema,
|
||||||
GitLabProjectSchema,
|
GitLabProjectSchema,
|
||||||
GitLabLabelSchema,
|
GitLabLabelSchema,
|
||||||
|
GitLabUserSchema,
|
||||||
|
GitLabUsersResponseSchema,
|
||||||
|
GetUsersSchema,
|
||||||
CreateRepositoryOptionsSchema,
|
CreateRepositoryOptionsSchema,
|
||||||
CreateIssueOptionsSchema,
|
CreateIssueOptionsSchema,
|
||||||
CreateMergeRequestOptionsSchema,
|
CreateMergeRequestOptionsSchema,
|
||||||
@ -108,6 +111,8 @@ import {
|
|||||||
type GitLabNamespaceExistsResponse,
|
type GitLabNamespaceExistsResponse,
|
||||||
type GitLabProject,
|
type GitLabProject,
|
||||||
type GitLabLabel,
|
type GitLabLabel,
|
||||||
|
type GitLabUser,
|
||||||
|
type GitLabUsersResponse,
|
||||||
// Discussion Types
|
// Discussion Types
|
||||||
type GitLabDiscussionNote, // Added
|
type GitLabDiscussionNote, // Added
|
||||||
type GitLabDiscussion,
|
type GitLabDiscussion,
|
||||||
@ -418,6 +423,11 @@ const allTools = [
|
|||||||
"Get the repository tree for a GitLab project (list files and directories)",
|
"Get the repository tree for a GitLab project (list files and directories)",
|
||||||
inputSchema: zodToJsonSchema(GetRepositoryTreeSchema),
|
inputSchema: zodToJsonSchema(GetRepositoryTreeSchema),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "get_users",
|
||||||
|
description: "Get GitLab user details by usernames",
|
||||||
|
inputSchema: zodToJsonSchema(GetUsersSchema),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Define which tools are read-only
|
// Define which tools are read-only
|
||||||
@ -440,6 +450,7 @@ const readOnlyTools = [
|
|||||||
"list_labels",
|
"list_labels",
|
||||||
"get_label",
|
"get_label",
|
||||||
"list_group_projects",
|
"list_group_projects",
|
||||||
|
"get_users",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
|
// Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
|
||||||
@ -2255,6 +2266,65 @@ async function getRepositoryTree(
|
|||||||
return z.array(GitLabTreeItemSchema).parse(data);
|
return z.array(GitLabTreeItemSchema).parse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single user from GitLab
|
||||||
|
*
|
||||||
|
* @param {string} username - The username to look up
|
||||||
|
* @returns {Promise<GitLabUser | null>} The user data or null if not found
|
||||||
|
*/
|
||||||
|
async function getUser(username: string): Promise<GitLabUser | null> {
|
||||||
|
try {
|
||||||
|
const url = new URL(`${GITLAB_API_URL}/users`);
|
||||||
|
url.searchParams.append("username", username);
|
||||||
|
|
||||||
|
const response = await fetch(url.toString(), {
|
||||||
|
...DEFAULT_FETCH_CONFIG,
|
||||||
|
});
|
||||||
|
|
||||||
|
await handleGitLabError(response);
|
||||||
|
|
||||||
|
const users = await response.json();
|
||||||
|
|
||||||
|
// GitLab returns an array of users that match the username
|
||||||
|
if (Array.isArray(users) && users.length > 0) {
|
||||||
|
// Find exact match for username (case-sensitive)
|
||||||
|
const exactMatch = users.find(user => user.username === username);
|
||||||
|
if (exactMatch) {
|
||||||
|
return GitLabUserSchema.parse(exactMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matching user found
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching user by username '${username}':`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get multiple users from GitLab
|
||||||
|
*
|
||||||
|
* @param {string[]} usernames - Array of usernames to look up
|
||||||
|
* @returns {Promise<GitLabUsersResponse>} Object with usernames as keys and user objects or null as values
|
||||||
|
*/
|
||||||
|
async function getUsers(usernames: string[]): Promise<GitLabUsersResponse> {
|
||||||
|
const users: Record<string, GitLabUser | null> = {};
|
||||||
|
|
||||||
|
// Process usernames sequentially to avoid rate limiting
|
||||||
|
for (const username of usernames) {
|
||||||
|
try {
|
||||||
|
const user = await getUser(username);
|
||||||
|
users[username] = user;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing username '${username}':`, error);
|
||||||
|
users[username] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GitLabUsersResponseSchema.parse(users);
|
||||||
|
}
|
||||||
|
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
// Apply read-only filter first
|
// Apply read-only filter first
|
||||||
const tools0 = GITLAB_READ_ONLY_MODE
|
const tools0 = GITLAB_READ_ONLY_MODE
|
||||||
@ -2621,6 +2691,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|||||||
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
|
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "get_users": {
|
||||||
|
const args = GetUsersSchema.parse(request.params.arguments);
|
||||||
|
const usersMap = await getUsers(args.usernames);
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(usersMap, null, 2) }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
case "create_note": {
|
case "create_note": {
|
||||||
const args = CreateNoteSchema.parse(request.params.arguments);
|
const args = CreateNoteSchema.parse(request.params.arguments);
|
||||||
|
34
schemas.ts
34
schemas.ts
@ -7,6 +7,30 @@ export const GitLabAuthorSchema = z.object({
|
|||||||
date: z.string(),
|
date: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User schemas
|
||||||
|
export const GitLabUserSchema = z.object({
|
||||||
|
username: z.string(), // Changed from login to match GitLab API
|
||||||
|
id: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
avatar_url: z.string(),
|
||||||
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetUsersSchema = z.object({
|
||||||
|
usernames: z.array(z.string()).describe("Array of usernames to search for"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GitLabUsersResponseSchema = z.record(
|
||||||
|
z.string(),
|
||||||
|
z.object({
|
||||||
|
id: z.number(),
|
||||||
|
username: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
avatar_url: z.string(),
|
||||||
|
web_url: z.string(),
|
||||||
|
}).nullable()
|
||||||
|
);
|
||||||
|
|
||||||
// Namespace related schemas
|
// Namespace related schemas
|
||||||
|
|
||||||
// Base schema for project-related operations
|
// Base schema for project-related operations
|
||||||
@ -283,14 +307,6 @@ export const GitLabLabelSchema = z.object({
|
|||||||
is_project_label: z.boolean().optional(),
|
is_project_label: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitLabUserSchema = z.object({
|
|
||||||
username: z.string(), // Changed from login to match GitLab API
|
|
||||||
id: z.number(),
|
|
||||||
name: z.string(),
|
|
||||||
avatar_url: z.string(),
|
|
||||||
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GitLabMilestoneSchema = z.object({
|
export const GitLabMilestoneSchema = z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
iid: z.number(), // Added to match GitLab API
|
iid: z.number(), // Added to match GitLab API
|
||||||
@ -1103,3 +1119,5 @@ export type GetRepositoryTreeOptions = z.infer<typeof GetRepositoryTreeSchema>;
|
|||||||
export type MergeRequestThreadPosition = z.infer<typeof MergeRequestThreadPositionSchema>;
|
export type MergeRequestThreadPosition = z.infer<typeof MergeRequestThreadPositionSchema>;
|
||||||
export type CreateMergeRequestThreadOptions = z.infer<typeof CreateMergeRequestThreadSchema>;
|
export type CreateMergeRequestThreadOptions = z.infer<typeof CreateMergeRequestThreadSchema>;
|
||||||
export type CreateMergeRequestNoteOptions = z.infer<typeof CreateMergeRequestNoteSchema>;
|
export type CreateMergeRequestNoteOptions = z.infer<typeof CreateMergeRequestNoteSchema>;
|
||||||
|
export type GitLabUser = z.infer<typeof GitLabUserSchema>;
|
||||||
|
export type GitLabUsersResponse = z.infer<typeof GitLabUsersResponseSchema>;
|
||||||
|
Reference in New Issue
Block a user