opencode-gitlab-plugin 2.4.0 → 2.6.0
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/CHANGELOG.md +30 -0
- package/dist/index.js +180 -39
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +190 -49
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.6.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.5.0...v2.6.0) (2026-05-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ✨ Features
|
|
9
|
+
|
|
10
|
+
* **pagination:** expose pagination info in list MR and issue responses ([1c1722e](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/1c1722e62e0e41f00ab5409057bea1e76ec17a8d))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ♻️ Code Refactoring
|
|
14
|
+
|
|
15
|
+
* **client:** address review feedback on !23 ([2e94e46](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/2e94e46706807bc8797f10884f71fc56ecf967b9)), closes [23#note_3135645102](https://gitlab.com/vglafirov/23/issues/note_3135645102) [23#note_3135645423](https://gitlab.com/vglafirov/23/issues/note_3135645423) [23#note_3135644923](https://gitlab.com/vglafirov/23/issues/note_3135644923)
|
|
16
|
+
|
|
17
|
+
## [2.5.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.4.0...v2.5.0) (2026-05-20)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### ✨ Features
|
|
21
|
+
|
|
22
|
+
* **notes:** add gitlab_update_note tool for editing comments ([54a87e7](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/54a87e70d4d276c077ab25619e1d910d6d709575))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### 🐛 Bug Fixes
|
|
26
|
+
|
|
27
|
+
* handle ToolResult union type in mcp-adapter and tests ([a91d5dd](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/a91d5ddc5574d1cf9b7dab2d9624cdddeefd158f))
|
|
28
|
+
* **tests:** update orbit.test.ts for ToolResult type ([29fecec](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/29fecec6a8ba436b3d95b720cf67f162c8c62104))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### ♻️ Code Refactoring
|
|
32
|
+
|
|
33
|
+
* **notes:** address review feedback on !28 ([11e13ba](https://gitlab.com/vglafirov/opencode-gitlab-plugin/commit/11e13ba5a6dafeb77dfb256bde3e9b7b1f5ddd32))
|
|
34
|
+
|
|
5
35
|
## [2.4.0](https://gitlab.com/vglafirov/opencode-gitlab-plugin/compare/v2.3.0...v2.4.0) (2026-05-19)
|
|
6
36
|
|
|
7
37
|
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,13 @@ var GitLabApiClient = class {
|
|
|
26
26
|
}
|
|
27
27
|
return projectId;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Core HTTP request used by all REST helpers.
|
|
31
|
+
*
|
|
32
|
+
* Returns the raw `Response` after validating the status code so callers can
|
|
33
|
+
* inspect headers (e.g. for pagination) before parsing the body.
|
|
34
|
+
*/
|
|
35
|
+
async rawFetch(method, path2, body) {
|
|
30
36
|
const url = `${this.instanceUrl}/api/v4${path2}`;
|
|
31
37
|
const response = await fetch(url, {
|
|
32
38
|
method,
|
|
@@ -37,12 +43,53 @@ var GitLabApiClient = class {
|
|
|
37
43
|
const errorText = await response.text();
|
|
38
44
|
throw new Error(`GitLab API error ${response.status}: ${errorText}`);
|
|
39
45
|
}
|
|
46
|
+
return response;
|
|
47
|
+
}
|
|
48
|
+
async fetch(method, path2, body) {
|
|
49
|
+
const response = await this.rawFetch(method, path2, body);
|
|
40
50
|
const text = await response.text();
|
|
41
51
|
if (!text) {
|
|
42
52
|
return {};
|
|
43
53
|
}
|
|
44
54
|
return JSON.parse(text);
|
|
45
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse a numeric pagination header, guarding against empty strings.
|
|
58
|
+
*
|
|
59
|
+
* GitLab returns `""` (rather than omitting the header) for `X-Next-Page` and
|
|
60
|
+
* `X-Prev-Page` when there is no next/prev page, and bare `parseInt` without
|
|
61
|
+
* a radix on an empty string yields `NaN`. Treat empty/missing as `undefined`.
|
|
62
|
+
*/
|
|
63
|
+
parsePaginationHeader(response, name) {
|
|
64
|
+
const raw = response.headers.get(name);
|
|
65
|
+
if (raw == null || raw === "") return void 0;
|
|
66
|
+
const value = parseInt(raw, 10);
|
|
67
|
+
return Number.isNaN(value) ? void 0 : value;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Pagination info extracted from GitLab API response headers
|
|
71
|
+
*/
|
|
72
|
+
extractPaginationInfo(response) {
|
|
73
|
+
return {
|
|
74
|
+
total: this.parsePaginationHeader(response, "X-Total"),
|
|
75
|
+
totalPages: this.parsePaginationHeader(response, "X-Total-Pages"),
|
|
76
|
+
page: this.parsePaginationHeader(response, "X-Page"),
|
|
77
|
+
perPage: this.parsePaginationHeader(response, "X-Per-Page"),
|
|
78
|
+
nextPage: this.parsePaginationHeader(response, "X-Next-Page"),
|
|
79
|
+
prevPage: this.parsePaginationHeader(response, "X-Prev-Page")
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Fetch with pagination info from response headers.
|
|
84
|
+
* Use this for list endpoints where knowing if more data exists is important.
|
|
85
|
+
*/
|
|
86
|
+
async fetchWithPagination(method, path2, body) {
|
|
87
|
+
const response = await this.rawFetch(method, path2, body);
|
|
88
|
+
const text = await response.text();
|
|
89
|
+
const data = text ? JSON.parse(text) : [];
|
|
90
|
+
const pagination = this.extractPaginationInfo(response);
|
|
91
|
+
return { data, pagination };
|
|
92
|
+
}
|
|
46
93
|
async fetchText(method, path2) {
|
|
47
94
|
const url = `${this.instanceUrl}/api/v4${path2}`;
|
|
48
95
|
const response = await fetch(url, {
|
|
@@ -306,6 +353,7 @@ var MergeRequestsClient = class extends GitLabApiClient {
|
|
|
306
353
|
async listMergeRequests(options) {
|
|
307
354
|
const params = new URLSearchParams();
|
|
308
355
|
params.set("per_page", String(options.limit || 20));
|
|
356
|
+
if (options.page) params.set("page", String(options.page));
|
|
309
357
|
if (options.state) params.set("state", options.state);
|
|
310
358
|
if (options.scope) params.set("scope", options.scope);
|
|
311
359
|
if (options.search) params.set("search", options.search);
|
|
@@ -317,7 +365,7 @@ var MergeRequestsClient = class extends GitLabApiClient {
|
|
|
317
365
|
} else {
|
|
318
366
|
path2 = `/merge_requests?${params}`;
|
|
319
367
|
}
|
|
320
|
-
return this.
|
|
368
|
+
return this.fetchWithPagination("GET", path2);
|
|
321
369
|
}
|
|
322
370
|
async getMrChanges(projectId, mrIid) {
|
|
323
371
|
const encodedProject = this.encodeProjectId(projectId);
|
|
@@ -438,6 +486,14 @@ var MergeRequestsClient = class extends GitLabApiClient {
|
|
|
438
486
|
{ body }
|
|
439
487
|
);
|
|
440
488
|
}
|
|
489
|
+
async updateMrNote(projectId, mrIid, noteId, body) {
|
|
490
|
+
const encodedProject = this.encodeProjectId(projectId);
|
|
491
|
+
return this.fetch(
|
|
492
|
+
"PUT",
|
|
493
|
+
`/projects/${encodedProject}/merge_requests/${mrIid}/notes/${noteId}`,
|
|
494
|
+
{ body }
|
|
495
|
+
);
|
|
496
|
+
}
|
|
441
497
|
async createMergeRequest(projectId, options) {
|
|
442
498
|
const encodedProject = this.encodeProjectId(projectId);
|
|
443
499
|
return this.fetch(
|
|
@@ -721,6 +777,7 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
721
777
|
async listIssues(options) {
|
|
722
778
|
const params = new URLSearchParams();
|
|
723
779
|
params.set("per_page", String(options.limit || 20));
|
|
780
|
+
if (options.page) params.set("page", String(options.page));
|
|
724
781
|
if (options.state) params.set("state", options.state);
|
|
725
782
|
if (options.scope) params.set("scope", options.scope);
|
|
726
783
|
if (options.search) params.set("search", options.search);
|
|
@@ -733,7 +790,7 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
733
790
|
} else {
|
|
734
791
|
path2 = `/issues?${params}`;
|
|
735
792
|
}
|
|
736
|
-
return this.
|
|
793
|
+
return this.fetchWithPagination("GET", path2);
|
|
737
794
|
}
|
|
738
795
|
/**
|
|
739
796
|
* List notes on an issue using GraphQL API with pagination support
|
|
@@ -816,6 +873,14 @@ var IssuesClient = class extends GitLabApiClient {
|
|
|
816
873
|
`/projects/${encodedProject}/issues/${issueIid}/notes/${noteId}`
|
|
817
874
|
);
|
|
818
875
|
}
|
|
876
|
+
async updateIssueNote(projectId, issueIid, noteId, body) {
|
|
877
|
+
const encodedProject = this.encodeProjectId(projectId);
|
|
878
|
+
return this.fetch(
|
|
879
|
+
"PUT",
|
|
880
|
+
`/projects/${encodedProject}/issues/${issueIid}/notes/${noteId}`,
|
|
881
|
+
{ body }
|
|
882
|
+
);
|
|
883
|
+
}
|
|
819
884
|
};
|
|
820
885
|
|
|
821
886
|
// src/client/work-items.ts
|
|
@@ -2150,6 +2215,14 @@ var EpicsClient = class extends GitLabApiClient {
|
|
|
2150
2215
|
`/groups/${encodedGroup}/epics/${epicIid}/notes/${noteId}`
|
|
2151
2216
|
);
|
|
2152
2217
|
}
|
|
2218
|
+
async updateEpicNote(groupId, epicIid, noteId, body) {
|
|
2219
|
+
const encodedGroup = encodeURIComponent(groupId);
|
|
2220
|
+
return this.fetch(
|
|
2221
|
+
"PUT",
|
|
2222
|
+
`/groups/${encodedGroup}/epics/${epicIid}/notes/${noteId}`,
|
|
2223
|
+
{ body }
|
|
2224
|
+
);
|
|
2225
|
+
}
|
|
2153
2226
|
};
|
|
2154
2227
|
|
|
2155
2228
|
// src/client/snippets.ts
|
|
@@ -2278,6 +2351,14 @@ var SnippetsClient = class extends GitLabApiClient {
|
|
|
2278
2351
|
{ body }
|
|
2279
2352
|
);
|
|
2280
2353
|
}
|
|
2354
|
+
async updateSnippetNote(projectId, snippetId, noteId, body) {
|
|
2355
|
+
const encodedProject = this.encodeProjectId(projectId);
|
|
2356
|
+
return this.fetch(
|
|
2357
|
+
"PUT",
|
|
2358
|
+
`/projects/${encodedProject}/snippets/${snippetId}/notes/${noteId}`,
|
|
2359
|
+
{ body }
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2281
2362
|
};
|
|
2282
2363
|
|
|
2283
2364
|
// src/client/discussions.ts
|
|
@@ -2615,26 +2696,31 @@ Returns: title, description, state, author, assignees, reviewers, labels, diff s
|
|
|
2615
2696
|
}),
|
|
2616
2697
|
gitlab_list_merge_requests: tool({
|
|
2617
2698
|
description: `List merge requests for a project or search globally.
|
|
2618
|
-
Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels
|
|
2699
|
+
Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels.
|
|
2700
|
+
|
|
2701
|
+
IMPORTANT: Response includes pagination info. If nextPage exists, there are more results.
|
|
2702
|
+
Always paginate until you get an empty response or no nextPage to ensure you have all data.`,
|
|
2619
2703
|
args: {
|
|
2620
2704
|
project_id: z.string().optional().describe("The project ID or path. If not provided, searches globally."),
|
|
2621
2705
|
state: z.enum(["opened", "closed", "merged", "all"]).optional().describe("Filter by MR state (default: opened)"),
|
|
2622
2706
|
scope: z.enum(["assigned_to_me", "created_by_me", "all"]).optional().describe("Filter by scope"),
|
|
2623
2707
|
search: z.string().optional().describe("Search MRs by title or description"),
|
|
2624
2708
|
labels: z.string().optional().describe("Comma-separated list of labels to filter by"),
|
|
2625
|
-
limit: z.number().optional().describe("Maximum number of results (default: 20)")
|
|
2709
|
+
limit: z.number().optional().describe("Maximum number of results per page (default: 20)"),
|
|
2710
|
+
page: z.number().optional().describe("Page number for pagination (default: 1)")
|
|
2626
2711
|
},
|
|
2627
2712
|
execute: async (args, _ctx) => {
|
|
2628
2713
|
const client = getGitLabClient();
|
|
2629
|
-
const
|
|
2714
|
+
const result = await client.listMergeRequests({
|
|
2630
2715
|
projectId: args.project_id,
|
|
2631
2716
|
state: args.state,
|
|
2632
2717
|
scope: args.scope,
|
|
2633
2718
|
search: args.search,
|
|
2634
2719
|
labels: args.labels,
|
|
2635
|
-
limit: args.limit
|
|
2720
|
+
limit: args.limit,
|
|
2721
|
+
page: args.page
|
|
2636
2722
|
});
|
|
2637
|
-
return JSON.stringify(
|
|
2723
|
+
return JSON.stringify(result, null, 2);
|
|
2638
2724
|
}
|
|
2639
2725
|
}),
|
|
2640
2726
|
gitlab_get_mr_changes: tool({
|
|
@@ -2966,7 +3052,10 @@ Returns: title, description, state, author, assignees, labels, milestone, weight
|
|
|
2966
3052
|
}),
|
|
2967
3053
|
gitlab_list_issues: tool2({
|
|
2968
3054
|
description: `List issues for a project or search globally.
|
|
2969
|
-
Can filter by state, labels, assignee, milestone
|
|
3055
|
+
Can filter by state, labels, assignee, milestone.
|
|
3056
|
+
|
|
3057
|
+
IMPORTANT: Response includes pagination info. If nextPage exists, there are more results.
|
|
3058
|
+
Always paginate until you get an empty response or no nextPage to ensure you have all data.`,
|
|
2970
3059
|
args: {
|
|
2971
3060
|
project_id: z2.string().optional().describe("The project ID or path. If not provided, searches globally."),
|
|
2972
3061
|
state: z2.enum(["opened", "closed", "all"]).optional().describe("Filter by issue state (default: opened)"),
|
|
@@ -2974,20 +3063,22 @@ Can filter by state, labels, assignee, milestone.`,
|
|
|
2974
3063
|
search: z2.string().optional().describe("Search issues by title or description"),
|
|
2975
3064
|
labels: z2.string().optional().describe("Comma-separated list of labels to filter by"),
|
|
2976
3065
|
milestone: z2.string().optional().describe("Filter by milestone title"),
|
|
2977
|
-
limit: z2.number().optional().describe("Maximum number of results (default: 20)")
|
|
3066
|
+
limit: z2.number().optional().describe("Maximum number of results per page (default: 20)"),
|
|
3067
|
+
page: z2.number().optional().describe("Page number for pagination (default: 1)")
|
|
2978
3068
|
},
|
|
2979
3069
|
execute: async (args, _ctx) => {
|
|
2980
3070
|
const client = getGitLabClient();
|
|
2981
|
-
const
|
|
3071
|
+
const result = await client.listIssues({
|
|
2982
3072
|
projectId: args.project_id,
|
|
2983
3073
|
state: args.state,
|
|
2984
3074
|
scope: args.scope,
|
|
2985
3075
|
search: args.search,
|
|
2986
3076
|
labels: args.labels,
|
|
2987
3077
|
milestone: args.milestone,
|
|
2988
|
-
limit: args.limit
|
|
3078
|
+
limit: args.limit,
|
|
3079
|
+
page: args.page
|
|
2989
3080
|
});
|
|
2990
|
-
return JSON.stringify(
|
|
3081
|
+
return JSON.stringify(result, null, 2);
|
|
2991
3082
|
}
|
|
2992
3083
|
})
|
|
2993
3084
|
};
|
|
@@ -4489,19 +4580,22 @@ function filterNotesByResolved(result, resolved) {
|
|
|
4489
4580
|
}
|
|
4490
4581
|
};
|
|
4491
4582
|
}
|
|
4492
|
-
var
|
|
4493
|
-
var
|
|
4583
|
+
var NOTE_RESOURCE_TYPES = ["merge_request", "issue", "epic", "snippet"];
|
|
4584
|
+
var GET_NOTE_RESOURCE_TYPES = ["issue", "epic"];
|
|
4494
4585
|
function validationError3(param, resourceType) {
|
|
4495
4586
|
return new Error(
|
|
4496
4587
|
`Missing required parameter: '${param}' is required for resource_type '${resourceType}'`
|
|
4497
4588
|
);
|
|
4498
4589
|
}
|
|
4499
|
-
function
|
|
4500
|
-
if (!
|
|
4590
|
+
function validateNoteParams(resourceType, args, opts) {
|
|
4591
|
+
if (!opts.allowedTypes.includes(resourceType)) {
|
|
4501
4592
|
throw new Error(
|
|
4502
|
-
`Invalid resource_type '${resourceType}'. Must be one of: ${
|
|
4593
|
+
`Invalid resource_type '${resourceType}'. Must be one of: ${opts.allowedTypes.join(", ")}`
|
|
4503
4594
|
);
|
|
4504
4595
|
}
|
|
4596
|
+
if (opts.requireNoteId && args.note_id == null) {
|
|
4597
|
+
throw validationError3("note_id", resourceType);
|
|
4598
|
+
}
|
|
4505
4599
|
switch (resourceType) {
|
|
4506
4600
|
case "merge_request":
|
|
4507
4601
|
case "issue":
|
|
@@ -4518,24 +4612,6 @@ function validateListCreateParams(resourceType, args) {
|
|
|
4518
4612
|
break;
|
|
4519
4613
|
}
|
|
4520
4614
|
}
|
|
4521
|
-
function validateGetNoteParams(resourceType, args) {
|
|
4522
|
-
if (!VALID_GET_NOTE_TYPES.includes(resourceType)) {
|
|
4523
|
-
throw new Error(
|
|
4524
|
-
`Invalid resource_type '${resourceType}'. Must be one of: ${VALID_GET_NOTE_TYPES.join(", ")}`
|
|
4525
|
-
);
|
|
4526
|
-
}
|
|
4527
|
-
if (args.note_id == null) throw validationError3("note_id", resourceType);
|
|
4528
|
-
switch (resourceType) {
|
|
4529
|
-
case "issue":
|
|
4530
|
-
if (!args.project_id) throw validationError3("project_id", resourceType);
|
|
4531
|
-
if (args.iid == null) throw validationError3("iid", resourceType);
|
|
4532
|
-
break;
|
|
4533
|
-
case "epic":
|
|
4534
|
-
if (!args.group_id) throw validationError3("group_id", resourceType);
|
|
4535
|
-
if (args.iid == null) throw validationError3("iid", resourceType);
|
|
4536
|
-
break;
|
|
4537
|
-
}
|
|
4538
|
-
}
|
|
4539
4615
|
var notesUnifiedTools = {
|
|
4540
4616
|
/**
|
|
4541
4617
|
* List notes/comments for any GitLab resource type
|
|
@@ -4571,7 +4647,7 @@ Examples:
|
|
|
4571
4647
|
before: z14.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
|
|
4572
4648
|
},
|
|
4573
4649
|
execute: async (args, _ctx) => {
|
|
4574
|
-
|
|
4650
|
+
validateNoteParams(args.resource_type, args, { allowedTypes: NOTE_RESOURCE_TYPES });
|
|
4575
4651
|
const client = getGitLabClient();
|
|
4576
4652
|
const paginationOptions = {
|
|
4577
4653
|
first: args.first,
|
|
@@ -4625,7 +4701,10 @@ Examples:
|
|
|
4625
4701
|
iid: z14.number().describe("Internal ID of the issue or epic")
|
|
4626
4702
|
},
|
|
4627
4703
|
execute: async (args, _ctx) => {
|
|
4628
|
-
|
|
4704
|
+
validateNoteParams(args.resource_type, args, {
|
|
4705
|
+
allowedTypes: GET_NOTE_RESOURCE_TYPES,
|
|
4706
|
+
requireNoteId: true
|
|
4707
|
+
});
|
|
4629
4708
|
const client = getGitLabClient();
|
|
4630
4709
|
switch (args.resource_type) {
|
|
4631
4710
|
case "issue":
|
|
@@ -4669,7 +4748,7 @@ Examples:
|
|
|
4669
4748
|
snippet_id: z14.number().optional().describe("Snippet ID (required for snippet)")
|
|
4670
4749
|
},
|
|
4671
4750
|
execute: async (args, _ctx) => {
|
|
4672
|
-
|
|
4751
|
+
validateNoteParams(args.resource_type, args, { allowedTypes: NOTE_RESOURCE_TYPES });
|
|
4673
4752
|
const client = getGitLabClient();
|
|
4674
4753
|
switch (args.resource_type) {
|
|
4675
4754
|
case "merge_request":
|
|
@@ -4700,6 +4779,68 @@ Examples:
|
|
|
4700
4779
|
throw new Error(`Unsupported resource type: ${args.resource_type}`);
|
|
4701
4780
|
}
|
|
4702
4781
|
}
|
|
4782
|
+
}),
|
|
4783
|
+
/**
|
|
4784
|
+
* Update an existing note/comment on any GitLab resource
|
|
4785
|
+
*/
|
|
4786
|
+
gitlab_update_note: tool14({
|
|
4787
|
+
description: `Update an existing note/comment on any GitLab resource.
|
|
4788
|
+
Modifies the body content of an existing note. You can only update notes you authored.
|
|
4789
|
+
|
|
4790
|
+
Examples:
|
|
4791
|
+
- MR note: resource_type="merge_request", project_id="group/project", iid=123, note_id=456, body="Updated comment"
|
|
4792
|
+
- Issue note: resource_type="issue", project_id="group/project", iid=456, note_id=789, body="Updated comment"
|
|
4793
|
+
- Epic note: resource_type="epic", group_id="my-group", iid=1, note_id=456, body="Updated comment"
|
|
4794
|
+
- Snippet note: resource_type="snippet", project_id="group/project", snippet_id=789, note_id=123, body="Updated comment"`,
|
|
4795
|
+
args: {
|
|
4796
|
+
resource_type: z14.enum(["merge_request", "issue", "epic", "snippet"]).describe("Type of GitLab resource"),
|
|
4797
|
+
note_id: z14.number().describe("The ID of the note to update"),
|
|
4798
|
+
body: z14.string().describe("The new content for the note (supports Markdown)"),
|
|
4799
|
+
project_id: z14.string().optional().describe("Project ID or path. Required for merge_request, issue, snippet"),
|
|
4800
|
+
group_id: z14.string().optional().describe("Group ID or path. Required for epic"),
|
|
4801
|
+
iid: z14.number().optional().describe("Internal ID of the resource (for merge_request, issue, epic)"),
|
|
4802
|
+
snippet_id: z14.number().optional().describe("Snippet ID (required for snippet)")
|
|
4803
|
+
},
|
|
4804
|
+
execute: async (args, _ctx) => {
|
|
4805
|
+
validateNoteParams(args.resource_type, args, {
|
|
4806
|
+
allowedTypes: NOTE_RESOURCE_TYPES,
|
|
4807
|
+
requireNoteId: true
|
|
4808
|
+
});
|
|
4809
|
+
const client = getGitLabClient();
|
|
4810
|
+
switch (args.resource_type) {
|
|
4811
|
+
case "merge_request":
|
|
4812
|
+
return JSON.stringify(
|
|
4813
|
+
await client.updateMrNote(args.project_id, args.iid, args.note_id, args.body),
|
|
4814
|
+
null,
|
|
4815
|
+
2
|
|
4816
|
+
);
|
|
4817
|
+
case "issue":
|
|
4818
|
+
return JSON.stringify(
|
|
4819
|
+
await client.updateIssueNote(args.project_id, args.iid, args.note_id, args.body),
|
|
4820
|
+
null,
|
|
4821
|
+
2
|
|
4822
|
+
);
|
|
4823
|
+
case "epic":
|
|
4824
|
+
return JSON.stringify(
|
|
4825
|
+
await client.updateEpicNote(args.group_id, args.iid, args.note_id, args.body),
|
|
4826
|
+
null,
|
|
4827
|
+
2
|
|
4828
|
+
);
|
|
4829
|
+
case "snippet":
|
|
4830
|
+
return JSON.stringify(
|
|
4831
|
+
await client.updateSnippetNote(
|
|
4832
|
+
args.project_id,
|
|
4833
|
+
args.snippet_id,
|
|
4834
|
+
args.note_id,
|
|
4835
|
+
args.body
|
|
4836
|
+
),
|
|
4837
|
+
null,
|
|
4838
|
+
2
|
|
4839
|
+
);
|
|
4840
|
+
default:
|
|
4841
|
+
throw new Error(`Unsupported resource type: ${args.resource_type}`);
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4703
4844
|
})
|
|
4704
4845
|
};
|
|
4705
4846
|
|