From 3f2b35535ee93b14a6649074608842d1ff8de208 Mon Sep 17 00:00:00 2001 From: Nicholas Crum Date: Tue, 13 May 2025 16:20:21 -0600 Subject: [PATCH] feat: Implement add_merge_request_thread_note function for adding notes to existing MR threads --- README.md | 51 +++++++++++++++++++++---------------------- index.ts | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ schemas.ts | 9 ++++++++ 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6d72db2..68fb3f5 100644 --- a/README.md +++ b/README.md @@ -58,29 +58,30 @@ When using with the Claude App, you need to set up your API key and URLs directl 14. `create_merge_request_thread` - Create a new thread on a merge request 15. `mr_discussions` - List discussion items for a merge request 16. `update_merge_request_note` - Modify an existing merge request thread note -17. `list_issues` - List issues in a GitLab project with filtering options -18. `get_issue` - Get details of a specific issue in a GitLab project -19. `update_issue` - Update an issue in a GitLab project -20. `delete_issue` - Delete an issue from a GitLab project -21. `list_issue_links` - List all issue links for a specific issue -22. `get_issue_link` - Get a specific issue link -23. `create_issue_link` - Create an issue link between two issues -24. `delete_issue_link` - Delete an issue link -25. `list_namespaces` - List all namespaces available to the current user -26. `get_namespace` - Get details of a namespace by ID or path -27. `verify_namespace` - Verify if a namespace path exists -28. `get_project` - Get details of a specific project -29. `list_projects` - List projects accessible by the current user -30. `list_labels` - List labels for a project -31. `get_label` - Get a single label from a project -32. `create_label` - Create a new label in a project -33. `update_label` - Update an existing label in a project -34. `delete_label` - Delete a label from a project -35. `list_group_projects` - List projects in a GitLab group with filtering options -36. `list_wiki_pages` - List wiki pages in a GitLab project -37. `get_wiki_page` - Get details of a specific wiki page -38. `create_wiki_page` - Create a new wiki page in a GitLab project -39. `update_wiki_page` - Update an existing wiki page in a GitLab project -40. `delete_wiki_page` - Delete a wiki page from a GitLab project -41. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories) +17. `add_merge_request_thread_note` - Add a new note to an existing merge request thread +18. `list_issues` - List issues in a GitLab project with filtering options +19. `get_issue` - Get details of a specific issue in a GitLab project +20. `update_issue` - Update an issue in a GitLab project +21. `delete_issue` - Delete an issue from a GitLab project +22. `list_issue_links` - List all issue links for a specific issue +23. `get_issue_link` - Get a specific issue link +24. `create_issue_link` - Create an issue link between two issues +25. `delete_issue_link` - Delete an issue link +26. `list_namespaces` - List all namespaces available to the current user +27. `get_namespace` - Get details of a namespace by ID or path +28. `verify_namespace` - Verify if a namespace path exists +29. `get_project` - Get details of a specific project +30. `list_projects` - List projects accessible by the current user +31. `list_labels` - List labels for a project +32. `get_label` - Get a single label from a project +33. `create_label` - Create a new label in a project +34. `update_label` - Update an existing label in a project +35. `delete_label` - Delete a label from a project +36. `list_group_projects` - List projects in a GitLab group with filtering options +37. `list_wiki_pages` - List wiki pages in a GitLab project +38. `get_wiki_page` - Get details of a specific wiki page +39. `create_wiki_page` - Create a new wiki page in a GitLab project +40. `update_wiki_page` - Update an existing wiki page in a GitLab project +41. `delete_wiki_page` - Delete a wiki page from a GitLab project +42. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories) diff --git a/index.ts b/index.ts index c21aab6..f7d8b74 100644 --- a/index.ts +++ b/index.ts @@ -87,6 +87,7 @@ import { GitLabDiscussionNoteSchema, // Added GitLabDiscussionSchema, UpdateMergeRequestNoteSchema, // Added + AddMergeRequestThreadNoteSchema, // Added ListMergeRequestDiscussionsSchema, type GitLabFork, type GitLabReference, @@ -280,6 +281,11 @@ const allTools = [ description: "Modify an existing merge request thread note", inputSchema: zodToJsonSchema(UpdateMergeRequestNoteSchema), }, + { + name: "add_merge_request_thread_note", + description: "Add a new note to an existing merge request thread", + inputSchema: zodToJsonSchema(AddMergeRequestThreadNoteSchema), + }, { name: "list_issues", description: "List issues in a GitLab project with filtering options", @@ -1060,6 +1066,47 @@ async function updateMergeRequestNote( return GitLabDiscussionNoteSchema.parse(data); } +/** + * Add a new note to an existing merge request thread + * 기존 병합 요청 스레드에 새 노트 추가 + * + * @param {string} projectId - The ID or URL-encoded path of the project + * @param {number} mergeRequestIid - The IID of a merge request + * @param {string} discussionId - The ID of a thread + * @param {string} body - The content of the new note + * @param {string} [createdAt] - The creation date of the note (ISO 8601 format) + * @returns {Promise} The created note + */ +async function addMergeRequestThreadNote( + projectId: string, + mergeRequestIid: number, + discussionId: string, + body: string, + createdAt?: string +): Promise { + projectId = decodeURIComponent(projectId); // Decode project ID + const url = new URL( + `${GITLAB_API_URL}/projects/${encodeURIComponent( + projectId + )}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes` + ); + + const payload: { body: string; created_at?: string } = { body }; + if (createdAt) { + payload.created_at = createdAt; + } + + const response = await fetch(url.toString(), { + ...DEFAULT_FETCH_CONFIG, + method: "POST", + body: JSON.stringify(payload), + }); + + await handleGitLabError(response); + const data = await response.json(); + return GitLabDiscussionNoteSchema.parse(data); +} + /** * Create or update a file in a GitLab project * 파일 생성 또는 업데이트 @@ -2337,6 +2384,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { content: [{ type: "text", text: JSON.stringify(note, null, 2) }], }; } + + case "add_merge_request_thread_note": { + const args = AddMergeRequestThreadNoteSchema.parse( + request.params.arguments + ); + const note = await addMergeRequestThreadNote( + args.project_id, + args.merge_request_iid, + args.discussion_id, + args.body, + args.created_at + ); + return { + content: [{ type: "text", text: JSON.stringify(note, null, 2) }], + }; + } case "get_merge_request": { const args = GetMergeRequestSchema.parse(request.params.arguments); diff --git a/schemas.ts b/schemas.ts index 2e27c00..d779592 100644 --- a/schemas.ts +++ b/schemas.ts @@ -479,6 +479,14 @@ export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({ resolved: z.boolean().optional().describe("Resolve or unresolve the note"), // Optional based on API docs }); +// Input schema for adding a note to an existing merge request discussion +export const AddMergeRequestThreadNoteSchema = ProjectParamsSchema.extend({ + merge_request_iid: z.number().describe("The IID of a merge request"), + discussion_id: z.string().describe("The ID of a thread"), + body: z.string().describe("The content of the note or reply"), + created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"), +}); + // API Operation Parameter Schemas export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({ @@ -1079,3 +1087,4 @@ export type GitLabTreeItem = z.infer; export type GetRepositoryTreeOptions = z.infer; export type MergeRequestThreadPosition = z.infer; export type CreateMergeRequestThreadOptions = z.infer; +export type AddMergeRequestThreadNoteOptions = z.infer;