From 6cb8e559e04550322c1ba7cec8b0883660579599 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 23:29:36 -0700 Subject: [PATCH] Fix create_or_update_file parameter handling and schema validation - Added support for both commit_id and last_commit_id parameters in createOrUpdateFile function - Made commit_id optional in GitLabCreateUpdateFileResponseSchema to match GitLab API - Implemented smart handling to extract commit IDs from existing files when updating - Resolves zereight/gitlab-mcp#12 --- index.ts | 38 +++++++++++++++++++++++++++++++++----- schemas.ts | 12 +++++++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/index.ts b/index.ts index b1c70fa..77ba10b 100644 --- a/index.ts +++ b/index.ts @@ -330,7 +330,9 @@ async function createOrUpdateFile( content: string, commitMessage: string, branch: string, - previousPath?: string + previousPath?: string, + last_commit_id?: string, + commit_id?: string ): Promise { const encodedPath = encodeURIComponent(filePath); const url = new URL( @@ -339,7 +341,7 @@ async function createOrUpdateFile( )}/repository/files/${encodedPath}` ); - const body = { + const body: Record = { branch, content, commit_message: commitMessage, @@ -350,13 +352,37 @@ async function createOrUpdateFile( // Check if file exists let method = "POST"; try { - await getFileContents(projectId, filePath, branch); + // Get file contents to check existence and retrieve commit IDs + const fileData = await getFileContents(projectId, filePath, branch); method = "PUT"; + + // If fileData is not an array, it's a file content object with commit IDs + if (!Array.isArray(fileData)) { + // Use commit IDs from the file data if not provided in parameters + if (!commit_id && fileData.commit_id) { + body.commit_id = fileData.commit_id; + } else if (commit_id) { + body.commit_id = commit_id; + } + + if (!last_commit_id && fileData.last_commit_id) { + body.last_commit_id = fileData.last_commit_id; + } else if (last_commit_id) { + body.last_commit_id = last_commit_id; + } + } } catch (error) { if (!(error instanceof Error && error.message.includes("File not found"))) { throw error; } - // File doesn't exist, use POST + // File doesn't exist, use POST - no need for commit IDs for new files + // But still use any provided as parameters if they exist + if (commit_id) { + body.commit_id = commit_id; + } + if (last_commit_id) { + body.last_commit_id = last_commit_id; + } } const response = await fetch(url.toString(), { @@ -795,7 +821,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { args.content, args.commit_message, args.branch, - args.previous_path + args.previous_path, + args.last_commit_id, + args.commit_id ); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], diff --git a/schemas.ts b/schemas.ts index fa1ae3b..f8bc29b 100644 --- a/schemas.ts +++ b/schemas.ts @@ -43,7 +43,9 @@ export const GitLabFileContentSchema = z.object({ content_sha256: z.string(), // Changed from sha to match GitLab API ref: z.string(), // Added as GitLab requires branch reference blob_id: z.string(), // Added to match GitLab API + commit_id: z.string(), // ID of the current file version last_commit_id: z.string(), // Added to match GitLab API + execute_filemode: z.boolean().optional(), // Added to match GitLab API }); export const GitLabDirectoryContentSchema = z.object({ @@ -138,7 +140,7 @@ export const CreateBranchOptionsSchema = z.object({ export const GitLabCreateUpdateFileResponseSchema = z.object({ file_path: z.string(), branch: z.string(), - commit_id: z.string(), // Changed from sha to match GitLab API + commit_id: z.string().optional(), // Optional since it's not always returned by the API content: GitLabFileContentSchema.optional(), }); @@ -263,6 +265,14 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({ .string() .optional() .describe("Path of the file to move/rename"), + last_commit_id: z + .string() + .optional() + .describe("Last known file commit ID"), + commit_id: z + .string() + .optional() + .describe("Current file commit ID (for update operations)"), }); export const SearchRepositoriesSchema = z.object({