@zereight/mcp-gitlab 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -264,7 +264,7 @@ async function createMergeRequest(projectId, options) {
264
264
  * @param {string} [previousPath] - The previous path of the file in case of rename
265
265
  * @returns {Promise<GitLabCreateUpdateFileResponse>} The file update response
266
266
  */
267
- async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath) {
267
+ async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id) {
268
268
  const encodedPath = encodeURIComponent(filePath);
269
269
  const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/files/${encodedPath}`);
270
270
  const body = {
@@ -277,14 +277,38 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
277
277
  // Check if file exists
278
278
  let method = "POST";
279
279
  try {
280
- await getFileContents(projectId, filePath, branch);
280
+ // Get file contents to check existence and retrieve commit IDs
281
+ const fileData = await getFileContents(projectId, filePath, branch);
281
282
  method = "PUT";
283
+ // If fileData is not an array, it's a file content object with commit IDs
284
+ if (!Array.isArray(fileData)) {
285
+ // Use commit IDs from the file data if not provided in parameters
286
+ if (!commit_id && fileData.commit_id) {
287
+ body.commit_id = fileData.commit_id;
288
+ }
289
+ else if (commit_id) {
290
+ body.commit_id = commit_id;
291
+ }
292
+ if (!last_commit_id && fileData.last_commit_id) {
293
+ body.last_commit_id = fileData.last_commit_id;
294
+ }
295
+ else if (last_commit_id) {
296
+ body.last_commit_id = last_commit_id;
297
+ }
298
+ }
282
299
  }
283
300
  catch (error) {
284
301
  if (!(error instanceof Error && error.message.includes("File not found"))) {
285
302
  throw error;
286
303
  }
287
- // File doesn't exist, use POST
304
+ // File doesn't exist, use POST - no need for commit IDs for new files
305
+ // But still use any provided as parameters if they exist
306
+ if (commit_id) {
307
+ body.commit_id = commit_id;
308
+ }
309
+ if (last_commit_id) {
310
+ body.last_commit_id = last_commit_id;
311
+ }
288
312
  }
289
313
  const response = await fetch(url.toString(), {
290
314
  method,
@@ -614,11 +638,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
614
638
  }
615
639
  switch (request.params.name) {
616
640
  case "fork_repository": {
617
- const args = ForkRepositorySchema.parse(request.params.arguments);
618
- const fork = await forkProject(args.project_id, args.namespace);
619
- return {
620
- content: [{ type: "text", text: JSON.stringify(fork, null, 2) }],
621
- };
641
+ const forkArgs = ForkRepositorySchema.parse(request.params.arguments);
642
+ try {
643
+ const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
644
+ return {
645
+ content: [{ type: "text", text: JSON.stringify(forkedProject, null, 2) }],
646
+ };
647
+ }
648
+ catch (forkError) {
649
+ console.error("Error forking repository:", forkError);
650
+ let forkErrorMessage = "Failed to fork repository";
651
+ if (forkError instanceof Error) {
652
+ forkErrorMessage = `${forkErrorMessage}: ${forkError.message}`;
653
+ }
654
+ return {
655
+ content: [{ type: "text", text: JSON.stringify({ error: forkErrorMessage }, null, 2) }],
656
+ };
657
+ }
622
658
  }
623
659
  case "create_branch": {
624
660
  const args = CreateBranchSchema.parse(request.params.arguments);
@@ -659,7 +695,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
659
695
  }
660
696
  case "create_or_update_file": {
661
697
  const args = CreateOrUpdateFileSchema.parse(request.params.arguments);
662
- const result = await createOrUpdateFile(args.project_id, args.file_path, args.content, args.commit_message, args.branch, args.previous_path);
698
+ const result = await createOrUpdateFile(args.project_id, args.file_path, args.content, args.commit_message, args.branch, args.previous_path, args.last_commit_id, args.commit_id);
663
699
  return {
664
700
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
665
701
  };
package/build/schemas.js CHANGED
@@ -39,7 +39,9 @@ export const GitLabFileContentSchema = z.object({
39
39
  content_sha256: z.string(), // Changed from sha to match GitLab API
40
40
  ref: z.string(), // Added as GitLab requires branch reference
41
41
  blob_id: z.string(), // Added to match GitLab API
42
+ commit_id: z.string(), // ID of the current file version
42
43
  last_commit_id: z.string(), // Added to match GitLab API
44
+ execute_filemode: z.boolean().optional(), // Added to match GitLab API
43
45
  });
44
46
  export const GitLabDirectoryContentSchema = z.object({
45
47
  name: z.string(),
@@ -122,7 +124,7 @@ export const CreateBranchOptionsSchema = z.object({
122
124
  export const GitLabCreateUpdateFileResponseSchema = z.object({
123
125
  file_path: z.string(),
124
126
  branch: z.string(),
125
- commit_id: z.string(), // Changed from sha to match GitLab API
127
+ commit_id: z.string().optional(), // Optional since it's not always returned by the API
126
128
  content: GitLabFileContentSchema.optional(),
127
129
  });
128
130
  export const GitLabSearchResponseSchema = z.object({
@@ -139,11 +141,11 @@ export const GitLabForkParentSchema = z.object({
139
141
  username: z.string(), // Changed from login to match GitLab API
140
142
  id: z.number(),
141
143
  avatar_url: z.string(),
142
- }),
144
+ }).optional(), // Made optional to handle cases where GitLab API doesn't include it
143
145
  web_url: z.string(), // Changed from html_url to match GitLab API
144
146
  });
145
147
  export const GitLabForkSchema = GitLabRepositorySchema.extend({
146
- forked_from_project: GitLabForkParentSchema, // Changed from parent to match GitLab API
148
+ forked_from_project: GitLabForkParentSchema.optional(), // Made optional to handle cases where GitLab API doesn't include it
147
149
  });
148
150
  // Issue related schemas
149
151
  export const GitLabLabelSchema = z.object({
@@ -236,6 +238,14 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({
236
238
  .string()
237
239
  .optional()
238
240
  .describe("Path of the file to move/rename"),
241
+ last_commit_id: z
242
+ .string()
243
+ .optional()
244
+ .describe("Last known file commit ID"),
245
+ commit_id: z
246
+ .string()
247
+ .optional()
248
+ .describe("Current file commit ID (for update operations)"),
239
249
  });
240
250
  export const SearchRepositoriesSchema = z.object({
241
251
  search: z.string().describe("Search query"), // Changed from query to match GitLab API
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",