Compare commits

...

11 Commits

Author SHA1 Message Date
44d8d11b4a FIX: list issues assginee username (#87) 2025-06-07 08:20:20 +09:00
622c112f7f FEAT: add support for remove_source_branch and squash options for merge requests (#86) 2025-06-07 08:20:07 +09:00
0930ce3636 [version-update] feat: bump version to 1.0.59 🎉
🚀 Breaking Changes:
- Updated package version from 1.0.58 to 1.0.59
2025-06-04 21:37:59 +09:00
061e19d861 Fix for null error (#85)
Co-authored-by: Jean Paul Gatt <jeanpaul.gatt@ballys.com>
2025-06-04 21:37:28 +09:00
511d2d9c06 FIX: bug get issues (#83) 2025-06-04 21:37:14 +09:00
8cb7703aa1 [feat] update: bump version to 1.0.58
🚀 Breaking Changes:
- Updated package version from 1.0.57 to 1.0.58
2025-06-03 19:34:00 +09:00
5e254836e8 Add support for retrieving wiki page content in list_wiki_pages (#82)
Co-authored-by: Vince Liao <vince.liao@nextbank.com.tw>
2025-06-03 19:33:36 +09:00
93710f2846 Merge pull request #81 from zereight/doc/readme
DOC: readme docker image
2025-06-03 14:58:18 +09:00
c07356bd46 [feat] update: bump version to 1.0.57
🚀 Breaking Changes:
- Updated package version from 1.0.56 to 1.0.57
2025-06-03 14:33:54 +09:00
c82be8c94f Add pagination to merge request discussions, similar to issue discussions (#80) 2025-06-03 14:33:17 +09:00
cd8f0e5525 fix: merge_requests_template can be null (#79) 2025-06-03 14:33:05 +09:00
3 changed files with 155 additions and 123 deletions

151
index.ts
View File

@ -100,6 +100,7 @@ import {
// Discussion Schemas // Discussion Schemas
GitLabDiscussionNoteSchema, // Added GitLabDiscussionNoteSchema, // Added
GitLabDiscussionSchema, GitLabDiscussionSchema,
PaginatedDiscussionsResponseSchema,
UpdateMergeRequestNoteSchema, // Added UpdateMergeRequestNoteSchema, // Added
CreateMergeRequestNoteSchema, // Added CreateMergeRequestNoteSchema, // Added
ListMergeRequestDiscussionsSchema, ListMergeRequestDiscussionsSchema,
@ -142,8 +143,10 @@ import {
type PromoteProjectMilestoneOptions, type PromoteProjectMilestoneOptions,
type GetMilestoneBurndownEventsOptions, type GetMilestoneBurndownEventsOptions,
// Discussion Types // Discussion Types
type GitLabDiscussionNote, // Added type GitLabDiscussionNote,
type GitLabDiscussion, type GitLabDiscussion,
type PaginatedDiscussionsResponse,
type PaginationOptions,
type MergeRequestThreadPosition, type MergeRequestThreadPosition,
type GetWikiPageOptions, type GetWikiPageOptions,
type CreateWikiPageOptions, type CreateWikiPageOptions,
@ -168,6 +171,7 @@ import {
GitLabCompareResult, GitLabCompareResult,
GitLabCompareResultSchema, GitLabCompareResultSchema,
GetBranchDiffsSchema, GetBranchDiffsSchema,
ListWikiPagesOptions,
} from "./schemas.js"; } from "./schemas.js";
/** /**
@ -903,13 +907,18 @@ async function listIssues(
// Add all query parameters // Add all query parameters
Object.entries(options).forEach(([key, value]) => { Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) { if (value !== undefined) {
if (key === "labels" && Array.isArray(value)) { const keys = ["labels", "assignee_username"];
// Handle array of labels if ( keys.includes(key)) {
value.forEach(label => { if (Array.isArray(value)) {
url.searchParams.append("labels[]", label.toString()); // Handle array of labels
}); value.forEach(label => {
url.searchParams.append(`${key}[]`, label.toString());
});
} else {
url.searchParams.append(`${key}[]`, value.toString());
}
} else { } else {
url.searchParams.append("labels[]", value.toString()); url.searchParams.append(key, value.toString());
} }
} }
}); });
@ -1191,6 +1200,8 @@ async function createMergeRequest(
labels: options.labels?.join(","), labels: options.labels?.join(","),
allow_collaboration: options.allow_collaboration, allow_collaboration: options.allow_collaboration,
draft: options.draft, draft: options.draft,
remove_source_branch: options.remove_source_branch,
squash: options.squash,
}), }),
}); });
@ -1209,55 +1220,26 @@ async function createMergeRequest(
} }
/** /**
* List merge request discussion items * Shared helper function for listing discussions
* 병합 요청 토론 목록 조회 * 토론 목록 조회를 위한 공유 헬퍼 함수
* *
* @param {string} projectId - The ID or URL-encoded path of the project * @param {string} projectId - The ID or URL-encoded path of the project
* @param {number} mergeRequestIid - The IID of a merge request * @param {"issues" | "merge_requests"} resourceType - The type of resource (issues or merge_requests)
* @returns {Promise<GitLabDiscussion[]>} List of discussions * @param {number} resourceIid - The IID of the issue or merge request
* @param {PaginationOptions} options - Pagination and sorting options
* @returns {Promise<PaginatedDiscussionsResponse>} Paginated list of discussions
*/ */
async function listMergeRequestDiscussions( async function listDiscussions(
projectId: string, projectId: string,
mergeRequestIid: number resourceType: "issues" | "merge_requests",
): Promise<GitLabDiscussion[]> { resourceIid: number,
options: PaginationOptions = {}
): Promise<PaginatedDiscussionsResponse> {
projectId = decodeURIComponent(projectId); // Decode project ID projectId = decodeURIComponent(projectId); // Decode project ID
const url = new URL( const url = new URL(
`${GITLAB_API_URL}/projects/${encodeURIComponent( `${GITLAB_API_URL}/projects/${encodeURIComponent(
projectId projectId
)}/merge_requests/${mergeRequestIid}/discussions` )}/${resourceType}/${resourceIid}/discussions`
);
const response = await fetch(url.toString(), {
...DEFAULT_FETCH_CONFIG,
});
await handleGitLabError(response);
const data = await response.json();
// Ensure the response is parsed as an array of discussions
return z.array(GitLabDiscussionSchema).parse(data);
}
/**
* List discussions for an issue
*
* @param {string} projectId - The ID or URL-encoded path of the project
* @param {number} issueIid - The internal ID of the project issue
* @param {Object} options - Pagination and sorting options
* @returns {Promise<GitLabDiscussion[]>} List of issue discussions
*/
async function listIssueDiscussions(
projectId: string,
issueIid: number,
options: {
page?: number;
per_page?: number;
sort?: "asc" | "desc";
order_by?: "created_at" | "updated_at";
} = {}
): Promise<GitLabDiscussion[]> {
projectId = decodeURIComponent(projectId); // Decode project ID
const url = new URL(
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions`
); );
// Add query parameters for pagination and sorting // Add query parameters for pagination and sorting
@ -1267,22 +1249,61 @@ async function listIssueDiscussions(
if (options.per_page) { if (options.per_page) {
url.searchParams.append("per_page", options.per_page.toString()); url.searchParams.append("per_page", options.per_page.toString());
} }
if (options.sort) {
url.searchParams.append("sort", options.sort);
}
if (options.order_by) {
url.searchParams.append("order_by", options.order_by);
}
const response = await fetch(url.toString(), { const response = await fetch(url.toString(), {
...DEFAULT_FETCH_CONFIG, ...DEFAULT_FETCH_CONFIG,
}); });
await handleGitLabError(response); await handleGitLabError(response);
const data = await response.json(); const discussions = await response.json();
// Parse the response as an array of discussions // Extract pagination headers
return z.array(GitLabDiscussionSchema).parse(data); const pagination = {
x_next_page: response.headers.get("x-next-page") ? parseInt(response.headers.get("x-next-page")!) : null,
x_page: response.headers.get("x-page") ? parseInt(response.headers.get("x-page")!) : undefined,
x_per_page: response.headers.get("x-per-page") ? parseInt(response.headers.get("x-per-page")!) : undefined,
x_prev_page: response.headers.get("x-prev-page") ? parseInt(response.headers.get("x-prev-page")!) : null,
x_total: response.headers.get("x-total") ? parseInt(response.headers.get("x-total")!) : null,
x_total_pages: response.headers.get("x-total-pages") ? parseInt(response.headers.get("x-total-pages")!) : null,
};
return PaginatedDiscussionsResponseSchema.parse({
items: discussions,
pagination: pagination,
});
}
/**
* List merge request discussion items
* 병합 요청 토론 목록 조회
*
* @param {string} projectId - The ID or URL-encoded path of the project
* @param {number} mergeRequestIid - The IID of a merge request
* @param {DiscussionPaginationOptions} options - Pagination and sorting options
* @returns {Promise<GitLabDiscussion[]>} List of discussions
*/
async function listMergeRequestDiscussions(
projectId: string,
mergeRequestIid: number,
options: PaginationOptions = {}
): Promise<PaginatedDiscussionsResponse> {
return listDiscussions(projectId, "merge_requests", mergeRequestIid, options);
}
/**
* List discussions for an issue
*
* @param {string} projectId - The ID or URL-encoded path of the project
* @param {number} issueIid - The internal ID of the project issue
* @param {DiscussionPaginationOptions} options - Pagination and sorting options
* @returns {Promise<GitLabDiscussion[]>} List of issue discussions
*/
async function listIssueDiscussions(
projectId: string,
issueIid: number,
options: PaginationOptions = {}
): Promise<PaginatedDiscussionsResponse> {
return listDiscussions(projectId, "issues", issueIid, options);
} }
/** /**
@ -2363,12 +2384,14 @@ async function listGroupProjects(
*/ */
async function listWikiPages( async function listWikiPages(
projectId: string, projectId: string,
options: Omit<z.infer<typeof ListWikiPagesSchema>, "project_id"> = {} options: Omit<ListWikiPagesOptions, "project_id"> = {}
): Promise<GitLabWikiPage[]> { ): Promise<GitLabWikiPage[]> {
projectId = decodeURIComponent(projectId); // Decode project ID projectId = decodeURIComponent(projectId); // Decode project ID
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`); const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`);
if (options.page) url.searchParams.append("page", options.page.toString()); if (options.page) url.searchParams.append("page", options.page.toString());
if (options.per_page) url.searchParams.append("per_page", options.per_page.toString()); if (options.per_page) url.searchParams.append("per_page", options.per_page.toString());
if (options.with_content)
url.searchParams.append("with_content", options.with_content.toString());
const response = await fetch(url.toString(), { const response = await fetch(url.toString(), {
...DEFAULT_FETCH_CONFIG, ...DEFAULT_FETCH_CONFIG,
}); });
@ -3284,9 +3307,11 @@ server.setRequestHandler(CallToolRequestSchema, async request => {
case "mr_discussions": { case "mr_discussions": {
const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments); const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
const { project_id, merge_request_iid, ...options } = args;
const discussions = await listMergeRequestDiscussions( const discussions = await listMergeRequestDiscussions(
args.project_id, project_id,
args.merge_request_iid merge_request_iid,
options
); );
return { return {
content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }], content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
@ -3582,8 +3607,10 @@ server.setRequestHandler(CallToolRequestSchema, async request => {
} }
case "list_wiki_pages": { case "list_wiki_pages": {
const { project_id, page, per_page } = ListWikiPagesSchema.parse(request.params.arguments); const { project_id, page, per_page, with_content } = ListWikiPagesSchema.parse(
const wikiPages = await listWikiPages(project_id, { page, per_page }); request.params.arguments
);
const wikiPages = await listWikiPages(project_id, { page, per_page, with_content });
return { return {
content: [{ type: "text", text: JSON.stringify(wikiPages, null, 2) }], content: [{ type: "text", text: JSON.stringify(wikiPages, null, 2) }],
}; };

View File

@ -1,6 +1,6 @@
{ {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.56", "version": "1.0.59",
"description": "MCP server for using the GitLab API", "description": "MCP server for using the GitLab API",
"license": "MIT", "license": "MIT",
"author": "zereight", "author": "zereight",

View File

@ -94,6 +94,13 @@ export const GitLabPipelineJobSchema = z.object({
web_url: z.string().optional(), web_url: z.string().optional(),
}); });
// Shared base schema for various pagination options
// See https://docs.gitlab.com/api/rest/#pagination
export const PaginationOptionsSchema = z.object({
page: z.number().optional().describe("Page number for pagination (default: 1)"),
per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
});
// Schema for listing pipelines // Schema for listing pipelines
export const ListPipelinesSchema = z.object({ export const ListPipelinesSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
@ -134,9 +141,7 @@ export const ListPipelinesSchema = z.object({
.optional() .optional()
.describe("Order pipelines by"), .describe("Order pipelines by"),
sort: z.enum(["asc", "desc"]).optional().describe("Sort pipelines"), sort: z.enum(["asc", "desc"]).optional().describe("Sort pipelines"),
page: z.number().optional().describe("Page number for pagination"), }).merge(PaginationOptionsSchema);
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Schema for getting a specific pipeline // Schema for getting a specific pipeline
export const GetPipelineSchema = z.object({ export const GetPipelineSchema = z.object({
@ -153,9 +158,7 @@ export const ListPipelineJobsSchema = z.object({
.optional() .optional()
.describe("The scope of jobs to show"), .describe("The scope of jobs to show"),
include_retried: z.boolean().optional().describe("Whether to include retried jobs"), include_retried: z.boolean().optional().describe("Whether to include retried jobs"),
page: z.number().optional().describe("Page number for pagination"), }).merge(PaginationOptionsSchema);
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Schema for creating a new pipeline // Schema for creating a new pipeline
export const CreatePipelineSchema = z.object({ export const CreatePipelineSchema = z.object({
@ -209,7 +212,7 @@ export const GitLabUsersResponseSchema = z.record(
id: z.number(), id: z.number(),
username: z.string(), username: z.string(),
name: z.string(), name: z.string(),
avatar_url: z.string(), avatar_url: z.string().nullable(),
web_url: z.string(), web_url: z.string(),
}).nullable() }).nullable()
); );
@ -250,7 +253,7 @@ export const GitLabNamespaceExistsResponseSchema = z.object({
export const GitLabOwnerSchema = z.object({ export const GitLabOwnerSchema = z.object({
username: z.string(), // Changed from login to match GitLab API username: z.string(), // Changed from login to match GitLab API
id: z.number(), id: z.number(),
avatar_url: z.string(), avatar_url: z.string().nullable(),
web_url: z.string(), // Changed from html_url to match GitLab API web_url: z.string(), // Changed from html_url to match GitLab API
name: z.string(), // Added as GitLab includes full name name: z.string(), // Added as GitLab includes full name
state: z.string(), // Added as GitLab includes user state state: z.string(), // Added as GitLab includes user state
@ -310,7 +313,7 @@ export const GitLabRepositorySchema = z.object({
container_registry_access_level: z.string().optional(), container_registry_access_level: z.string().optional(),
issues_enabled: z.boolean().optional(), issues_enabled: z.boolean().optional(),
merge_requests_enabled: z.boolean().optional(), merge_requests_enabled: z.boolean().optional(),
merge_requests_template: z.string().optional(), merge_requests_template: z.string().nullable().optional(),
wiki_enabled: z.boolean().optional(), wiki_enabled: z.boolean().optional(),
jobs_enabled: z.boolean().optional(), jobs_enabled: z.boolean().optional(),
snippets_enabled: z.boolean().optional(), snippets_enabled: z.boolean().optional(),
@ -464,6 +467,8 @@ export const CreateMergeRequestOptionsSchema = z.object({
labels: z.array(z.string()).optional(), labels: z.array(z.string()).optional(),
allow_collaboration: z.boolean().optional(), // Changed from maintainer_can_modify to match GitLab API allow_collaboration: z.boolean().optional(), // Changed from maintainer_can_modify to match GitLab API
draft: z.boolean().optional(), draft: z.boolean().optional(),
remove_source_branch: z.boolean().optional().describe("Flag indicating if a merge request should remove the source branch when merging."),
squash: z.boolean().optional().describe("If true, squash all commits into a single commit on merge.")
}); });
export const GitLabDiffSchema = z.object({ export const GitLabDiffSchema = z.object({
@ -590,7 +595,7 @@ export const GitLabForkParentSchema = z.object({
.object({ .object({
username: z.string(), // Changed from login to match GitLab API username: z.string(), // Changed from login to match GitLab API
id: z.number(), id: z.number(),
avatar_url: z.string(), avatar_url: z.string().nullable(),
}) })
.optional(), // Made optional to handle cases where GitLab API doesn't include it .optional(), // Made optional to handle cases where GitLab API doesn't include it
web_url: z.string(), // Changed from html_url to match GitLab API web_url: z.string(), // Changed from html_url to match GitLab API
@ -698,6 +703,24 @@ export const GitLabDiscussionNoteSchema = z.object({
}); });
export type GitLabDiscussionNote = z.infer<typeof GitLabDiscussionNoteSchema>; export type GitLabDiscussionNote = z.infer<typeof GitLabDiscussionNoteSchema>;
// Reusable pagination schema for GitLab API responses.
// See https://docs.gitlab.com/api/rest/#pagination
export const GitLabPaginationSchema = z.object({
x_next_page: z.number().nullable().optional(),
x_page: z.number().optional(),
x_per_page: z.number().optional(),
x_prev_page: z.number().nullable().optional(),
x_total: z.number().nullable().optional(),
x_total_pages: z.number().nullable().optional(),
});
export type GitLabPagination = z.infer<typeof GitLabPaginationSchema>;
// Base paginated response schema that can be extended.
// See https://docs.gitlab.com/api/rest/#pagination
export const PaginatedResponseSchema = z.object({
pagination: GitLabPaginationSchema.optional(),
});
export const GitLabDiscussionSchema = z.object({ export const GitLabDiscussionSchema = z.object({
id: z.string(), id: z.string(),
individual_note: z.boolean(), individual_note: z.boolean(),
@ -705,10 +728,24 @@ export const GitLabDiscussionSchema = z.object({
}); });
export type GitLabDiscussion = z.infer<typeof GitLabDiscussionSchema>; export type GitLabDiscussion = z.infer<typeof GitLabDiscussionSchema>;
// Create a schema for paginated discussions response
export const PaginatedDiscussionsResponseSchema = z.object({
items: z.array(GitLabDiscussionSchema),
pagination: GitLabPaginationSchema,
});
// Export the paginated response type for discussions
export type PaginatedDiscussionsResponse = z.infer<typeof PaginatedDiscussionsResponseSchema>;
export const ListIssueDiscussionsSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
issue_iid: z.number().describe("The internal ID of the project issue"),
}).merge(PaginationOptionsSchema);
// Input schema for listing merge request discussions // Input schema for listing merge request discussions
export const ListMergeRequestDiscussionsSchema = ProjectParamsSchema.extend({ export const ListMergeRequestDiscussionsSchema = ProjectParamsSchema.extend({
merge_request_iid: z.number().describe("The IID of a merge request"), merge_request_iid: z.number().describe("The IID of a merge request"),
}); }).merge(PaginationOptionsSchema);
// Input schema for updating a merge request discussion note // Input schema for updating a merge request discussion note
export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({ export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({
@ -763,9 +800,7 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({
export const SearchRepositoriesSchema = z.object({ export const SearchRepositoriesSchema = z.object({
search: z.string().describe("Search query"), // Changed from query to match GitLab API search: z.string().describe("Search query"), // Changed from query to match GitLab API
page: z.number().optional().describe("Page number for pagination (default: 1)"), }).merge(PaginationOptionsSchema);
per_page: z.number().optional().describe("Number of results per page (default: 20)"),
});
export const CreateRepositorySchema = z.object({ export const CreateRepositorySchema = z.object({
name: z.string().describe("Repository name"), name: z.string().describe("Repository name"),
@ -883,7 +918,7 @@ export const CreateNoteSchema = z.object({
export const ListIssuesSchema = z.object({ export const ListIssuesSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
assignee_id: z.number().optional().describe("Return issues assigned to the given user ID"), assignee_id: z.number().optional().describe("Return issues assigned to the given user ID"),
assignee_username: z.string().optional().describe("Return issues assigned to the given username"), assignee_username: z.array(z.string()).optional().describe("Return issues assigned to the given username"),
author_id: z.number().optional().describe("Return issues created by the given user ID"), author_id: z.number().optional().describe("Return issues created by the given user ID"),
author_username: z.string().optional().describe("Return issues created by the given username"), author_username: z.string().optional().describe("Return issues created by the given username"),
confidential: z.boolean().optional().describe("Filter confidential or public issues"), confidential: z.boolean().optional().describe("Filter confidential or public issues"),
@ -904,9 +939,7 @@ export const ListIssuesSchema = z.object({
updated_after: z.string().optional().describe("Return issues updated after the given time"), updated_after: z.string().optional().describe("Return issues updated after the given time"),
updated_before: z.string().optional().describe("Return issues updated before the given time"), updated_before: z.string().optional().describe("Return issues updated before the given time"),
with_labels_details: z.boolean().optional().describe("Return more details for each label"), with_labels_details: z.boolean().optional().describe("Return more details for each label"),
page: z.number().optional().describe("Page number for pagination"), }).merge(PaginationOptionsSchema);
per_page: z.number().optional().describe("Number of items per page"),
});
// Merge Requests API operation schemas // Merge Requests API operation schemas
export const ListMergeRequestsSchema = z.object({ export const ListMergeRequestsSchema = z.object({
@ -977,9 +1010,7 @@ export const ListMergeRequestsSchema = z.object({
.describe("Return merge requests from a specific source branch"), .describe("Return merge requests from a specific source branch"),
wip: z.enum(["yes", "no"]).optional().describe("Filter merge requests against their wip status"), wip: z.enum(["yes", "no"]).optional().describe("Filter merge requests against their wip status"),
with_labels_details: z.boolean().optional().describe("Return more details for each label"), with_labels_details: z.boolean().optional().describe("Return more details for each label"),
page: z.number().optional().describe("Page number for pagination"), }).merge(PaginationOptionsSchema);
per_page: z.number().optional().describe("Number of items per page"),
});
export const GetIssueSchema = z.object({ export const GetIssueSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
@ -1018,21 +1049,6 @@ export const ListIssueLinksSchema = z.object({
issue_iid: z.number().describe("The internal ID of a project's issue"), issue_iid: z.number().describe("The internal ID of a project's issue"),
}); });
export const ListIssueDiscussionsSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
issue_iid: z.number().describe("The internal ID of the project issue"),
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"),
sort: z
.enum(["asc", "desc"])
.optional()
.describe("Return issue discussions sorted in ascending or descending order"),
order_by: z
.enum(["created_at", "updated_at"])
.optional()
.describe("Return issue discussions ordered by created_at or updated_at fields"),
});
export const GetIssueLinkSchema = z.object({ export const GetIssueLinkSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
issue_iid: z.number().describe("The internal ID of a project's issue"), issue_iid: z.number().describe("The internal ID of a project's issue"),
@ -1059,10 +1075,8 @@ export const DeleteIssueLinkSchema = z.object({
// Namespace API operation schemas // Namespace API operation schemas
export const ListNamespacesSchema = z.object({ export const ListNamespacesSchema = z.object({
search: z.string().optional().describe("Search term for namespaces"), search: z.string().optional().describe("Search term for namespaces"),
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"),
owned: z.boolean().optional().describe("Filter for namespaces owned by current user"), owned: z.boolean().optional().describe("Filter for namespaces owned by current user"),
}); }).merge(PaginationOptionsSchema);
export const GetNamespaceSchema = z.object({ export const GetNamespaceSchema = z.object({
namespace_id: z.string().describe("Namespace ID or full path"), namespace_id: z.string().describe("Namespace ID or full path"),
@ -1079,8 +1093,6 @@ export const GetProjectSchema = z.object({
export const ListProjectsSchema = z.object({ export const ListProjectsSchema = z.object({
search: z.string().optional().describe("Search term for projects"), search: z.string().optional().describe("Search term for projects"),
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"),
search_namespaces: z.boolean().optional().describe("Needs to be true if search is full path"), search_namespaces: z.boolean().optional().describe("Needs to be true if search is full path"),
owned: z.boolean().optional().describe("Filter for projects owned by current user"), owned: z.boolean().optional().describe("Filter for projects owned by current user"),
membership: z.boolean().optional().describe("Filter for projects where current user is a member"), membership: z.boolean().optional().describe("Filter for projects where current user is a member"),
@ -1107,7 +1119,7 @@ export const ListProjectsSchema = z.object({
.optional() .optional()
.describe("Filter projects with merge requests feature enabled"), .describe("Filter projects with merge requests feature enabled"),
min_access_level: z.number().optional().describe("Filter by minimum access level"), min_access_level: z.number().optional().describe("Filter by minimum access level"),
}); }).merge(PaginationOptionsSchema);
// Label operation schemas // Label operation schemas
export const ListLabelsSchema = z.object({ export const ListLabelsSchema = z.object({
@ -1163,8 +1175,6 @@ export const ListGroupProjectsSchema = z.object({
.optional() .optional()
.describe("Field to sort by"), .describe("Field to sort by"),
sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"), sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
page: z.number().optional().describe("Page number"),
per_page: z.number().optional().describe("Number of results per page"),
archived: z.boolean().optional().describe("Filter for archived projects"), archived: z.boolean().optional().describe("Filter for archived projects"),
visibility: z visibility: z
.enum(["public", "internal", "private"]) .enum(["public", "internal", "private"])
@ -1184,14 +1194,14 @@ export const ListGroupProjectsSchema = z.object({
statistics: z.boolean().optional().describe("Include project statistics"), statistics: z.boolean().optional().describe("Include project statistics"),
with_custom_attributes: z.boolean().optional().describe("Include custom attributes"), with_custom_attributes: z.boolean().optional().describe("Include custom attributes"),
with_security_reports: z.boolean().optional().describe("Include security reports"), with_security_reports: z.boolean().optional().describe("Include security reports"),
}); }).merge(PaginationOptionsSchema);
// Add wiki operation schemas // Add wiki operation schemas
export const ListWikiPagesSchema = z.object({ export const ListWikiPagesSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
page: z.number().optional().describe("Page number for pagination"), with_content: z.boolean().optional().describe("Include content of the wiki pages"),
per_page: z.number().optional().describe("Number of items per page"), }).merge(PaginationOptionsSchema);
});
export const GetWikiPageSchema = z.object({ export const GetWikiPageSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
slug: z.string().describe("URL-encoded slug of the wiki page"), slug: z.string().describe("URL-encoded slug of the wiki page"),
@ -1209,6 +1219,7 @@ export const UpdateWikiPageSchema = z.object({
content: z.string().optional().describe("New content of the wiki page"), content: z.string().optional().describe("New content of the wiki page"),
format: z.string().optional().describe("Content format, e.g., markdown, rdoc"), format: z.string().optional().describe("Content format, e.g., markdown, rdoc"),
}); });
export const DeleteWikiPageSchema = z.object({ export const DeleteWikiPageSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
slug: z.string().describe("URL-encoded slug of the wiki page"), slug: z.string().describe("URL-encoded slug of the wiki page"),
@ -1219,7 +1230,7 @@ export const GitLabWikiPageSchema = z.object({
title: z.string(), title: z.string(),
slug: z.string(), slug: z.string(),
format: z.string(), format: z.string(),
content: z.string(), content: z.string().optional(),
created_at: z.string().optional(), created_at: z.string().optional(),
updated_at: z.string().optional(), updated_at: z.string().optional(),
}); });
@ -1275,9 +1286,7 @@ export const ListProjectMilestonesSchema = ProjectParamsSchema.extend({
.string() .string()
.optional() .optional()
.describe("Return milestones updated after the specified date (ISO 8601 format)"), .describe("Return milestones updated after the specified date (ISO 8601 format)"),
page: z.number().optional().describe("Page number for pagination"), }).merge(PaginationOptionsSchema);
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Schema for getting a single milestone // Schema for getting a single milestone
export const GetProjectMilestoneSchema = ProjectParamsSchema.extend({ export const GetProjectMilestoneSchema = ProjectParamsSchema.extend({
@ -1311,19 +1320,13 @@ export const DeleteProjectMilestoneSchema = GetProjectMilestoneSchema;
export const GetMilestoneIssuesSchema = GetProjectMilestoneSchema; export const GetMilestoneIssuesSchema = GetProjectMilestoneSchema;
// Schema for getting merge requests assigned to a milestone // Schema for getting merge requests assigned to a milestone
export const GetMilestoneMergeRequestsSchema = GetProjectMilestoneSchema.extend({ export const GetMilestoneMergeRequestsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Schema for promoting a project milestone to a group milestone // Schema for promoting a project milestone to a group milestone
export const PromoteProjectMilestoneSchema = GetProjectMilestoneSchema; export const PromoteProjectMilestoneSchema = GetProjectMilestoneSchema;
// Schema for getting burndown chart events for a milestone // Schema for getting burndown chart events for a milestone
export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.extend({ export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Export types // Export types
export type GitLabAuthor = z.infer<typeof GitLabAuthorSchema>; export type GitLabAuthor = z.infer<typeof GitLabAuthorSchema>;
@ -1352,6 +1355,7 @@ export type GitLabMergeRequestDiff = z.infer<
export type CreateNoteOptions = z.infer<typeof CreateNoteSchema>; export type CreateNoteOptions = z.infer<typeof CreateNoteSchema>;
export type GitLabIssueLink = z.infer<typeof GitLabIssueLinkSchema>; export type GitLabIssueLink = z.infer<typeof GitLabIssueLinkSchema>;
export type ListIssueDiscussionsOptions = z.infer<typeof ListIssueDiscussionsSchema>; export type ListIssueDiscussionsOptions = z.infer<typeof ListIssueDiscussionsSchema>;
export type ListMergeRequestDiscussionsOptions = z.infer<typeof ListMergeRequestDiscussionsSchema>;
export type UpdateIssueNoteOptions = z.infer<typeof UpdateIssueNoteSchema>; export type UpdateIssueNoteOptions = z.infer<typeof UpdateIssueNoteSchema>;
export type CreateIssueNoteOptions = z.infer<typeof CreateIssueNoteSchema>; export type CreateIssueNoteOptions = z.infer<typeof CreateIssueNoteSchema>;
export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>; export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>;
@ -1389,3 +1393,4 @@ export type PromoteProjectMilestoneOptions = z.infer<typeof PromoteProjectMilest
export type GetMilestoneBurndownEventsOptions = z.infer<typeof GetMilestoneBurndownEventsSchema>; export type GetMilestoneBurndownEventsOptions = z.infer<typeof GetMilestoneBurndownEventsSchema>;
export type GitLabUser = z.infer<typeof GitLabUserSchema>; export type GitLabUser = z.infer<typeof GitLabUserSchema>;
export type GitLabUsersResponse = z.infer<typeof GitLabUsersResponseSchema>; export type GitLabUsersResponse = z.infer<typeof GitLabUsersResponseSchema>;
export type PaginationOptions = z.infer<typeof PaginationOptionsSchema>;