@therealchristhomas/gitlab-mcp-server
Version:
MCP Server for GitLab API operations
94 lines (93 loc) • 3.5 kB
JavaScript
import { z } from "zod";
import { gitlabGet, gitlabGetWithHeaders, gitlabPost, gitlabPut, encodeProjectId, buildSearchParams } from "../utils/gitlab-client.js";
import { GitLabIssueSchema, GitLabCommentSchema } from "../types/index.js";
export async function listIssues(projectId, options = {}) {
if (!projectId?.trim()) {
throw new Error("Project ID is required");
}
const endpoint = `/projects/${encodeProjectId(projectId)}/issues`;
// If user explicitly provides page parameter, use single page request
if (options.page !== undefined) {
const params = buildSearchParams(options);
const rawIssues = await gitlabGet(endpoint, params);
return z.array(GitLabIssueSchema).parse(rawIssues);
}
// Otherwise, fetch all pages automatically
const allIssues = [];
let currentPage = 1;
const perPage = options.per_page || 100; // Use max page size for efficiency
while (true) {
const params = buildSearchParams({
...options,
page: currentPage,
per_page: perPage
});
const response = await gitlabGetWithHeaders(endpoint, params);
const pageIssues = response.data;
if (pageIssues.length === 0) {
break; // No more issues
}
allIssues.push(...pageIssues);
// Check if there's a next page
const nextPage = response.headers["x-next-page"];
if (!nextPage) {
break; // No more pages
}
currentPage = parseInt(nextPage, 10);
}
return z.array(GitLabIssueSchema).parse(allIssues);
}
export async function createIssue(projectId, options) {
if (!projectId?.trim()) {
throw new Error("Project ID is required");
}
if (!options?.title?.trim()) {
throw new Error("Issue title is required");
}
const endpoint = `/projects/${encodeProjectId(projectId)}/issues`;
const issue = await gitlabPost(endpoint, {
title: options.title,
description: options.description,
assignee_ids: options.assignee_ids,
milestone_id: options.milestone_id,
labels: options.labels?.join(",")
});
return GitLabIssueSchema.parse(issue);
}
export async function updateIssue(projectId, issueIid, options) {
if (!projectId?.trim()) {
throw new Error("Project ID is required");
}
if (!issueIid || issueIid < 1) {
throw new Error("Valid issue IID is required");
}
const endpoint = `/projects/${encodeProjectId(projectId)}/issues/${issueIid}`;
const issue = await gitlabPut(endpoint, {
...options,
labels: options.labels?.join(",")
});
return GitLabIssueSchema.parse(issue);
}
export async function searchIssues(projectId, searchTerm, options = {}) {
if (!searchTerm?.trim()) {
throw new Error("Search term is required");
}
return listIssues(projectId, {
search: searchTerm,
...options
});
}
export async function addIssueComment(projectId, issueIid, body) {
if (!projectId?.trim()) {
throw new Error("Project ID is required");
}
if (!issueIid || issueIid < 1) {
throw new Error("Valid issue IID is required");
}
if (!body?.trim()) {
throw new Error("Comment body is required");
}
const endpoint = `/projects/${encodeProjectId(projectId)}/issues/${issueIid}/notes`;
const comment = await gitlabPost(endpoint, { body });
return GitLabCommentSchema.parse(comment);
}