Skip to content

Commit 3d06892

Browse files
committed
feat: add issue discussions support
Added `list_issue_discussions` tool to support GitLab issue discussions similar to merge request discussions.
1 parent d8016ca commit 3d06892

File tree

3 files changed

+102
-24
lines changed

3 files changed

+102
-24
lines changed

README.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,25 @@ When using with the Claude App, you need to set up your API key and URLs directl
9898
20. `update_issue` - Update an issue in a GitLab project
9999
21. `delete_issue` - Delete an issue from a GitLab project
100100
22. `list_issue_links` - List all issue links for a specific issue
101-
23. `get_issue_link` - Get a specific issue link
102-
24. `create_issue_link` - Create an issue link between two issues
103-
25. `delete_issue_link` - Delete an issue link
104-
26. `list_namespaces` - List all namespaces available to the current user
105-
27. `get_namespace` - Get details of a namespace by ID or path
106-
28. `verify_namespace` - Verify if a namespace path exists
107-
29. `get_project` - Get details of a specific project
108-
30. `list_projects` - List projects accessible by the current user
109-
31. `list_labels` - List labels for a project
110-
32. `get_label` - Get a single label from a project
111-
33. `create_label` - Create a new label in a project
112-
34. `update_label` - Update an existing label in a project
113-
35. `delete_label` - Delete a label from a project
114-
36. `list_group_projects` - List projects in a GitLab group with filtering options
115-
37. `list_wiki_pages` - List wiki pages in a GitLab project
116-
38. `get_wiki_page` - Get details of a specific wiki page
117-
39. `create_wiki_page` - Create a new wiki page in a GitLab project
118-
40. `update_wiki_page` - Update an existing wiki page in a GitLab project
119-
41. `delete_wiki_page` - Delete a wiki page from a GitLab project
120-
42. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
101+
23. `list_issue_discussions` - List discussions for an issue in a GitLab project
102+
24. `get_issue_link` - Get a specific issue link
103+
25. `create_issue_link` - Create an issue link between two issues
104+
26. `delete_issue_link` - Delete an issue link
105+
27. `list_namespaces` - List all namespaces available to the current user
106+
28. `get_namespace` - Get details of a namespace by ID or path
107+
29. `verify_namespace` - Verify if a namespace path exists
108+
30. `get_project` - Get details of a specific project
109+
31. `list_projects` - List projects accessible by the current user
110+
32. `list_labels` - List labels for a project
111+
33. `get_label` - Get a single label from a project
112+
34. `create_label` - Create a new label in a project
113+
35. `update_label` - Update an existing label in a project
114+
36. `delete_label` - Delete a label from a project
115+
37. `list_group_projects` - List projects in a GitLab group with filtering options
116+
38. `list_wiki_pages` - List wiki pages in a GitLab project
117+
39. `get_wiki_page` - Get details of a specific wiki page
118+
40. `create_wiki_page` - Create a new wiki page in a GitLab project
119+
41. `update_wiki_page` - Update an existing wiki page in a GitLab project
120+
42. `delete_wiki_page` - Delete a wiki page from a GitLab project
121+
43. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
121122
<!-- TOOLS-END -->

index.ts

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
GitLabIssueLinkSchema,
6262
GitLabIssueWithLinkDetailsSchema,
6363
ListIssueLinksSchema,
64+
ListIssueDiscussionsSchema,
6465
GetIssueLinkSchema,
6566
CreateIssueLinkSchema,
6667
DeleteIssueLinkSchema,
@@ -311,6 +312,11 @@ const allTools = [
311312
description: "List all issue links for a specific issue",
312313
inputSchema: zodToJsonSchema(ListIssueLinksSchema),
313314
},
315+
{
316+
name: "list_issue_discussions",
317+
description: "List discussions for an issue in a GitLab project",
318+
inputSchema: zodToJsonSchema(ListIssueDiscussionsSchema),
319+
},
314320
{
315321
name: "get_issue_link",
316322
description: "Get a specific issue link",
@@ -424,6 +430,7 @@ const readOnlyTools = [
424430
"list_issues",
425431
"get_issue",
426432
"list_issue_links",
433+
"list_issue_discussions",
427434
"get_issue_link",
428435
"list_namespaces",
429436
"get_namespace",
@@ -1023,6 +1030,56 @@ async function listMergeRequestDiscussions(
10231030
return z.array(GitLabDiscussionSchema).parse(data);
10241031
}
10251032

1033+
/**
1034+
* List discussions for an issue
1035+
*
1036+
* @param {string} projectId - The ID or URL-encoded path of the project
1037+
* @param {number} issueIid - The internal ID of the project issue
1038+
* @param {Object} options - Pagination and sorting options
1039+
* @returns {Promise<GitLabDiscussion[]>} List of issue discussions
1040+
*/
1041+
async function listIssueDiscussions(
1042+
projectId: string,
1043+
issueIid: number,
1044+
options: {
1045+
page?: number,
1046+
per_page?: number,
1047+
sort?: "asc" | "desc",
1048+
order_by?: "created_at" | "updated_at"
1049+
} = {}
1050+
): Promise<GitLabDiscussion[]> {
1051+
projectId = decodeURIComponent(projectId); // Decode project ID
1052+
const url = new URL(
1053+
`${GITLAB_API_URL}/projects/${encodeURIComponent(
1054+
projectId
1055+
)}/issues/${issueIid}/discussions`
1056+
);
1057+
1058+
// Add query parameters for pagination and sorting
1059+
if (options.page) {
1060+
url.searchParams.append("page", options.page.toString());
1061+
}
1062+
if (options.per_page) {
1063+
url.searchParams.append("per_page", options.per_page.toString());
1064+
}
1065+
if (options.sort) {
1066+
url.searchParams.append("sort", options.sort);
1067+
}
1068+
if (options.order_by) {
1069+
url.searchParams.append("order_by", options.order_by);
1070+
}
1071+
1072+
const response = await fetch(url.toString(), {
1073+
...DEFAULT_FETCH_CONFIG,
1074+
});
1075+
1076+
await handleGitLabError(response);
1077+
const data = await response.json();
1078+
1079+
// Parse the response as an array of discussions
1080+
return z.array(GitLabDiscussionSchema).parse(data);
1081+
}
1082+
10261083
/**
10271084
* Modify an existing merge request thread note
10281085
* 병합 요청 토론 노트 수정
@@ -1581,7 +1638,7 @@ async function createNote(
15811638
* Create a new thread on a merge request
15821639
* 📦 새로운 함수: createMergeRequestThread - 병합 요청에 새로운 스레드(토론)를 생성하는 함수
15831640
* (New function: createMergeRequestThread - Function to create a new thread (discussion) on a merge request)
1584-
*
1641+
*
15851642
* This function provides more capabilities than createNote, including the ability to:
15861643
* - Create diff notes (comments on specific lines of code)
15871644
* - Specify exact positions for comments
@@ -1609,12 +1666,12 @@ async function createMergeRequestThread(
16091666
);
16101667

16111668
const payload: Record<string, any> = { body };
1612-
1669+
16131670
// Add optional parameters if provided
16141671
if (position) {
16151672
payload.position = position;
16161673
}
1617-
1674+
16181675
if (createdAt) {
16191676
payload.created_at = createdAt;
16201677
}
@@ -2387,7 +2444,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
23872444
content: [{ type: "text", text: JSON.stringify(note, null, 2) }],
23882445
};
23892446
}
2390-
2447+
23912448
case "create_merge_request_note": {
23922449
const args = CreateMergeRequestNoteSchema.parse(
23932450
request.params.arguments
@@ -2647,6 +2704,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
26472704
};
26482705
}
26492706

2707+
case "list_issue_discussions": {
2708+
const args = ListIssueDiscussionsSchema.parse(request.params.arguments);
2709+
const { project_id, issue_iid, ...options } = args;
2710+
2711+
const discussions = await listIssueDiscussions(project_id, issue_iid, options);
2712+
return {
2713+
content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
2714+
};
2715+
}
2716+
26502717
case "get_issue_link": {
26512718
const args = GetIssueLinkSchema.parse(request.params.arguments);
26522719
const link = await getIssueLink(

schemas.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,15 @@ export const ListIssueLinksSchema = z.object({
757757
issue_iid: z.number().describe("The internal ID of a project's issue"),
758758
});
759759

760+
export const ListIssueDiscussionsSchema = z.object({
761+
project_id: z.string().describe("Project ID or URL-encoded path"),
762+
issue_iid: z.number().describe("The internal ID of the project issue"),
763+
page: z.number().optional().describe("Page number for pagination"),
764+
per_page: z.number().optional().describe("Number of items per page"),
765+
sort: z.enum(["asc", "desc"]).optional().describe("Return issue discussions sorted in ascending or descending order"),
766+
order_by: z.enum(["created_at", "updated_at"]).optional().describe("Return issue discussions ordered by created_at or updated_at fields"),
767+
});
768+
760769
export const GetIssueLinkSchema = z.object({
761770
project_id: z.string().describe("Project ID or URL-encoded path"),
762771
issue_iid: z.number().describe("The internal ID of a project's issue"),
@@ -1075,6 +1084,7 @@ export type GitLabMergeRequestDiff = z.infer<
10751084
>;
10761085
export type CreateNoteOptions = z.infer<typeof CreateNoteSchema>;
10771086
export type GitLabIssueLink = z.infer<typeof GitLabIssueLinkSchema>;
1087+
export type ListIssueDiscussionsOptions = z.infer<typeof ListIssueDiscussionsSchema>;
10781088
export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>;
10791089
export type GitLabNamespaceExistsResponse = z.infer<
10801090
typeof GitLabNamespaceExistsResponseSchema

0 commit comments

Comments
 (0)