Merge branch 'main' into pr/auth-naming-fixes

This commit is contained in:
Chad Loder
2025-03-18 15:39:37 -07:00
committed by GitHub
4 changed files with 428 additions and 34 deletions

247
index.ts
View File

@ -27,6 +27,7 @@ import {
GitLabNamespaceSchema,
GitLabNamespaceExistsResponseSchema,
GitLabProjectSchema,
GitLabLabelSchema,
CreateRepositoryOptionsSchema,
CreateIssueOptionsSchema,
CreateMergeRequestOptionsSchema,
@ -59,6 +60,11 @@ import {
VerifyNamespaceSchema,
GetProjectSchema,
ListProjectsSchema,
ListLabelsSchema,
GetLabelSchema,
CreateLabelSchema,
UpdateLabelSchema,
DeleteLabelSchema,
CreateNoteSchema,
type GitLabFork,
type GitLabReference,
@ -77,6 +83,7 @@ import {
type GitLabNamespace,
type GitLabNamespaceExistsResponse,
type GitLabProject,
type GitLabLabel,
} from "./schemas.js";
/**
@ -1204,22 +1211,187 @@ async function getProject(
* @returns {Promise<GitLabProject[]>} List of projects
*/
async function listProjects(options: z.infer<typeof ListProjectsSchema> = {}): Promise<GitLabProject[]> {
const url = new URL(`${GITLAB_API_URL}/projects`);
// Construct the query parameters
const params = new URLSearchParams();
for (const [key, value] of Object.entries(options)) {
if (value !== undefined && value !== null) {
if (typeof value === "boolean") {
params.append(key, value ? "true" : "false");
} else {
params.append(key, String(value));
}
}
}
// Add all the query parameters from options
// Make the API request
const response = await fetch(
`${GITLAB_API_URL}/projects?${params.toString()}`,
{
method: "GET",
headers: DEFAULT_HEADERS,
}
);
// Handle errors
await handleGitLabError(response);
// Parse and return the data
const data = await response.json();
return z.array(GitLabProjectSchema).parse(data);
}
/**
* List labels for a project
*
* @param projectId The ID or URL-encoded path of the project
* @param options Optional parameters for listing labels
* @returns Array of GitLab labels
*/
async function listLabels(
projectId: string,
options: Omit<z.infer<typeof ListLabelsSchema>, "project_id"> = {}
): Promise<GitLabLabel[]> {
// Construct the URL with project path
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`);
// Add query parameters
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) {
url.searchParams.append(key, value.toString());
if (typeof value === "boolean") {
url.searchParams.append(key, value ? "true" : "false");
} else {
url.searchParams.append(key, String(value));
}
}
});
// Make the API request
const response = await fetch(url.toString(), {
headers: DEFAULT_HEADERS,
});
// Handle errors
await handleGitLabError(response);
// Parse and return the data
const data = await response.json();
return z.array(GitLabRepositorySchema).parse(data);
return data as GitLabLabel[];
}
/**
* Get a single label from a project
*
* @param projectId The ID or URL-encoded path of the project
* @param labelId The ID or name of the label
* @param includeAncestorGroups Whether to include ancestor groups
* @returns GitLab label
*/
async function getLabel(
projectId: string,
labelId: number | string,
includeAncestorGroups?: boolean
): Promise<GitLabLabel> {
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`);
// Add query parameters
if (includeAncestorGroups !== undefined) {
url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
}
// Make the API request
const response = await fetch(url.toString(), {
headers: DEFAULT_HEADERS,
});
// Handle errors
await handleGitLabError(response);
// Parse and return the data
const data = await response.json();
return data as GitLabLabel;
}
/**
* Create a new label in a project
*
* @param projectId The ID or URL-encoded path of the project
* @param options Options for creating the label
* @returns Created GitLab label
*/
async function createLabel(
projectId: string,
options: Omit<z.infer<typeof CreateLabelSchema>, "project_id">
): Promise<GitLabLabel> {
// Make the API request
const response = await fetch(
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`,
{
method: "POST",
headers: DEFAULT_HEADERS,
body: JSON.stringify(options),
}
);
// Handle errors
await handleGitLabError(response);
// Parse and return the data
const data = await response.json();
return data as GitLabLabel;
}
/**
* Update an existing label in a project
*
* @param projectId The ID or URL-encoded path of the project
* @param labelId The ID or name of the label to update
* @param options Options for updating the label
* @returns Updated GitLab label
*/
async function updateLabel(
projectId: string,
labelId: number | string,
options: Omit<z.infer<typeof UpdateLabelSchema>, "project_id" | "label_id">
): Promise<GitLabLabel> {
// Make the API request
const response = await fetch(
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`,
{
method: "PUT",
headers: DEFAULT_HEADERS,
body: JSON.stringify(options),
}
);
// Handle errors
await handleGitLabError(response);
// Parse and return the data
const data = await response.json();
return data as GitLabLabel;
}
/**
* Delete a label from a project
*
* @param projectId The ID or URL-encoded path of the project
* @param labelId The ID or name of the label to delete
*/
async function deleteLabel(
projectId: string,
labelId: number | string
): Promise<void> {
// Make the API request
const response = await fetch(
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`,
{
method: "DELETE",
headers: DEFAULT_HEADERS,
}
);
// Handle errors
await handleGitLabError(response);
}
server.setRequestHandler(ListToolsRequestSchema, async () => {
@ -1358,6 +1530,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
description: "List projects accessible by the current user",
inputSchema: zodToJsonSchema(ListProjectsSchema),
},
{
name: "list_labels",
description: "List labels for a project",
inputSchema: zodToJsonSchema(ListLabelsSchema),
},
{
name: "get_label",
description: "Get a single label from a project",
inputSchema: zodToJsonSchema(GetLabelSchema),
},
{
name: "create_label",
description: "Create a new label in a project",
inputSchema: zodToJsonSchema(CreateLabelSchema),
},
{
name: "update_label",
description: "Update an existing label in a project",
inputSchema: zodToJsonSchema(UpdateLabelSchema),
},
{
name: "delete_label",
description: "Delete a label from a project",
inputSchema: zodToJsonSchema(DeleteLabelSchema),
},
],
};
});
@ -1613,6 +1810,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
case "list_projects": {
const args = ListProjectsSchema.parse(request.params.arguments);
const projects = await listProjects(args);
return {
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
};
@ -1699,6 +1897,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
};
}
case "list_labels": {
const args = ListLabelsSchema.parse(request.params.arguments);
const labels = await listLabels(args.project_id, args);
return {
content: [{ type: "text", text: JSON.stringify(labels, null, 2) }],
};
}
case "get_label": {
const args = GetLabelSchema.parse(request.params.arguments);
const label = await getLabel(args.project_id, args.label_id, args.include_ancestor_groups);
return {
content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
};
}
case "create_label": {
const args = CreateLabelSchema.parse(request.params.arguments);
const label = await createLabel(args.project_id, args);
return {
content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
};
}
case "update_label": {
const args = UpdateLabelSchema.parse(request.params.arguments);
const { project_id, label_id, ...options } = args;
const label = await updateLabel(project_id, label_id, options);
return {
content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
};
}
case "delete_label": {
const args = DeleteLabelSchema.parse(request.params.arguments);
await deleteLabel(args.project_id, args.label_id);
return {
content: [{ type: "text", text: JSON.stringify({ status: "success", message: "Label deleted successfully" }, null, 2) }],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}