@zereight/mcp-gitlab 2.1.15 → 2.1.17
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/README.md +10 -0
- package/build/index.js +241 -21
- package/build/schemas.js +151 -20
- package/build/test/nullish-tool-arguments-schema.test.js +99 -0
- package/build/test/schema-tests.js +116 -2
- package/build/test/test-ci-variables.js +306 -0
- package/build/test/test-geteffectiveprojectid.js +42 -0
- package/build/test/test-list-merge-requests.js +19 -0
- package/build/test/test-toolset-filtering.js +3 -0
- package/build/test/utils/merge-request-position.test.js +57 -0
- package/build/test/utils/tool-args.test.js +75 -0
- package/build/tools/registry.js +74 -2
- package/build/utils/merge-request-position.js +29 -0
- package/build/utils/tool-args.js +22 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -647,6 +647,16 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
|
|
|
647
647
|
154. `search_group_code` - Search for code within a specific GitLab group (requires advanced search or exact code search to be enabled)
|
|
648
648
|
155. `execute_graphql` - Execute a GitLab GraphQL query
|
|
649
649
|
156. `list_merge_request_pipelines` - List pipelines for a merge request with pagination support
|
|
650
|
+
157. `list_project_variables` - List CI/CD variables for a project with optional environment scope filter
|
|
651
|
+
158. `get_project_variable` - Get a single CI/CD variable from a project by key, with optional environment scope filter
|
|
652
|
+
159. `create_project_variable` - Create a new CI/CD variable in a project
|
|
653
|
+
160. `update_project_variable` - Update an existing CI/CD variable in a project, with optional filter to disambiguate by environment scope
|
|
654
|
+
161. `delete_project_variable` - Delete a CI/CD variable from a project, with optional filter to disambiguate by environment scope
|
|
655
|
+
162. `list_group_variables` - List CI/CD variables for a group with optional environment scope filter
|
|
656
|
+
163. `get_group_variable` - Get a single CI/CD variable from a group by key, with optional environment scope filter
|
|
657
|
+
164. `create_group_variable` - Create a new CI/CD variable in a group
|
|
658
|
+
165. `update_group_variable` - Update an existing CI/CD variable in a group, with optional filter to disambiguate by environment scope
|
|
659
|
+
166. `delete_group_variable` - Delete a CI/CD variable from a group, with optional filter to disambiguate by environment scope
|
|
650
660
|
|
|
651
661
|
<!-- TOOLS-END -->
|
|
652
662
|
|
package/build/index.js
CHANGED
|
@@ -128,6 +128,7 @@ import { createGitLabOAuthProvider } from "./oauth-proxy.js";
|
|
|
128
128
|
import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
129
129
|
import { normalizeGitLabApiUrl } from "./utils/url.js";
|
|
130
130
|
import { estimateMergeCommitCount, filterDiffsByPatterns, summarizeWebhookEvents } from "./utils/helpers.js";
|
|
131
|
+
import { sanitizeToolArguments } from "./utils/tool-args.js";
|
|
131
132
|
import { parseSearchReplaceBlocks, applySearchReplace, applyUnifiedDiff, } from "./utils/patch-helper.js";
|
|
132
133
|
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
133
134
|
import { GitLabClientPool } from "./gitlab-client-pool.js";
|
|
@@ -138,7 +139,7 @@ CreateMergeRequestNoteSchema, CreateMergeRequestDiscussionNoteSchema, CreateMerg
|
|
|
138
139
|
GitLabDiscussionNoteSchema, // Added
|
|
139
140
|
GitLabDiscussionSchema,
|
|
140
141
|
// Draft Notes Schemas
|
|
141
|
-
GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
|
|
142
|
+
GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, GitLabCiVariableSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
|
|
142
143
|
GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, MarkAllTodosDoneSchema, MarkTodoDoneSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, ListTagsSchema, GetTagSchema, CreateTagSchema, DeleteTagSchema, GetTagSignatureSchema, GitLabTagSchema, GitLabTagSignatureSchema, GetMergeRequestNotesSchema, GetMergeRequestNoteSchema, DeleteMergeRequestDiscussionNoteSchema, ResolveMergeRequestThreadSchema, GetWorkItemSchema, ListWorkItemsSchema, CreateWorkItemSchema, UpdateWorkItemSchema, ConvertWorkItemTypeSchema, ListWorkItemStatusesSchema, ListWorkItemNotesSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, MoveWorkItemSchema, ListCustomFieldDefinitionsSchema, GetTimelineEventsSchema, CreateTimelineEventSchema, ListWebhooksSchema, ListWebhookEventsSchema, GetWebhookEventSchema, HealthCheckSchema, } from "./schemas.js";
|
|
143
144
|
import { randomUUID, createCipheriv, createDecipheriv, randomBytes, createHash } from "node:crypto";
|
|
144
145
|
import { pino } from "pino";
|
|
@@ -1275,6 +1276,12 @@ async function listMergeRequests(projectId, options = {}) {
|
|
|
1275
1276
|
// Handle array of labels
|
|
1276
1277
|
url.searchParams.append(key, value.join(","));
|
|
1277
1278
|
}
|
|
1279
|
+
else if (key === "approved_by_usernames" && Array.isArray(value)) {
|
|
1280
|
+
// GitLab expects array-bracket form: approved_by_usernames[]=alice&approved_by_usernames[]=bob
|
|
1281
|
+
for (const v of value) {
|
|
1282
|
+
url.searchParams.append(`${key}[]`, String(v));
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1278
1285
|
else {
|
|
1279
1286
|
url.searchParams.append(key, String(value));
|
|
1280
1287
|
}
|
|
@@ -1382,16 +1389,9 @@ async function executeGraphQL(query, variables = {}) {
|
|
|
1382
1389
|
* Resolve a project path and issue IID to a work item GraphQL GID.
|
|
1383
1390
|
*/
|
|
1384
1391
|
async function resolveWorkItemGID(projectId, issueIid) {
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
const projectUrl = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}`);
|
|
1389
|
-
const projectResponse = await fetch(projectUrl.toString(), {
|
|
1390
|
-
...getFetchConfig(),
|
|
1391
|
-
});
|
|
1392
|
-
await handleGitLabError(projectResponse);
|
|
1393
|
-
const project = await projectResponse.json();
|
|
1394
|
-
const projectPath = project.path_with_namespace;
|
|
1392
|
+
// resolveProjectPath handles both project and group paths (including the
|
|
1393
|
+
// group fallback), so work item tools work for group-level namespaces too.
|
|
1394
|
+
const projectPath = await resolveProjectPath(projectId);
|
|
1395
1395
|
// Resolve work item GID via GraphQL
|
|
1396
1396
|
const data = await executeGraphQL(`query($path: ID!, $iid: String!) {
|
|
1397
1397
|
namespace(fullPath: $path) {
|
|
@@ -1514,15 +1514,7 @@ async function removeIssueParent(projectId, issueIid) {
|
|
|
1514
1514
|
* Requires Premium/Ultimate with configurable statuses enabled.
|
|
1515
1515
|
*/
|
|
1516
1516
|
async function listIssueStatuses(projectId, workItemType = "issue") {
|
|
1517
|
-
|
|
1518
|
-
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
1519
|
-
// Get project path
|
|
1520
|
-
const projectUrl = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}`);
|
|
1521
|
-
const projectResponse = await fetch(projectUrl.toString(), {
|
|
1522
|
-
...getFetchConfig(),
|
|
1523
|
-
});
|
|
1524
|
-
await handleGitLabError(projectResponse);
|
|
1525
|
-
const project = await projectResponse.json();
|
|
1517
|
+
const projectPath = await resolveProjectPath(projectId);
|
|
1526
1518
|
const typeName = WORK_ITEM_TYPE_NAMES[workItemType] || "Issue";
|
|
1527
1519
|
const data = await executeGraphQL(`query($path: ID!, $typeName: IssueType) {
|
|
1528
1520
|
namespace(fullPath: $path) {
|
|
@@ -1550,7 +1542,7 @@ async function listIssueStatuses(projectId, workItemType = "issue") {
|
|
|
1550
1542
|
}
|
|
1551
1543
|
}
|
|
1552
1544
|
}
|
|
1553
|
-
}`, { path:
|
|
1545
|
+
}`, { path: projectPath, typeName: typeName.replace(/ /g, "_").toUpperCase() });
|
|
1554
1546
|
const typeNodes = data.namespace?.workItemTypes?.nodes;
|
|
1555
1547
|
if (!typeNodes || typeNodes.length === 0) {
|
|
1556
1548
|
throw new Error(`Work item type '${typeName}' not found in project`);
|
|
@@ -1946,6 +1938,22 @@ async function resolveProjectPath(projectId) {
|
|
|
1946
1938
|
const projectResponse = await fetch(projectUrl.toString(), {
|
|
1947
1939
|
...getFetchConfig(),
|
|
1948
1940
|
});
|
|
1941
|
+
// On project 404, fall back to groups — but only for path-like identifiers.
|
|
1942
|
+
// Numeric IDs must not fall back: group and project IDs share no namespace,
|
|
1943
|
+
// so a numeric project 404 should fail immediately rather than silently
|
|
1944
|
+
// resolving to an unrelated group with the same integer ID.
|
|
1945
|
+
if (projectResponse.status === 404 && !/^\d+$/.test(effectiveProjectId)) {
|
|
1946
|
+
const groupUrl = new URL(`${getEffectiveApiUrl()}/groups/${encodeURIComponent(effectiveProjectId)}`);
|
|
1947
|
+
const groupResponse = await fetch(groupUrl.toString(), {
|
|
1948
|
+
...getFetchConfig(),
|
|
1949
|
+
});
|
|
1950
|
+
if (groupResponse.ok) {
|
|
1951
|
+
const group = await groupResponse.json();
|
|
1952
|
+
return group.full_path;
|
|
1953
|
+
}
|
|
1954
|
+
// Surface the group error
|
|
1955
|
+
await handleGitLabError(groupResponse);
|
|
1956
|
+
}
|
|
1949
1957
|
await handleGitLabError(projectResponse);
|
|
1950
1958
|
const project = await projectResponse.json();
|
|
1951
1959
|
return project.path_with_namespace;
|
|
@@ -5734,6 +5742,139 @@ async function listGroupIterations(groupId, options = {}) {
|
|
|
5734
5742
|
const data = await response.json();
|
|
5735
5743
|
return z.array(GroupIteration).parse(data);
|
|
5736
5744
|
}
|
|
5745
|
+
// --- CI/CD Variables ---
|
|
5746
|
+
async function listProjectVariables(projectId, options = {}) {
|
|
5747
|
+
projectId = decodeURIComponent(projectId);
|
|
5748
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/variables`);
|
|
5749
|
+
Object.entries(options).forEach(([key, value]) => {
|
|
5750
|
+
if (value === undefined)
|
|
5751
|
+
return;
|
|
5752
|
+
if (key === "filter" && typeof value === "object" && value !== null) {
|
|
5753
|
+
Object.entries(value).forEach(([fKey, fVal]) => {
|
|
5754
|
+
url.searchParams.append(`filter[${fKey}]`, fVal);
|
|
5755
|
+
});
|
|
5756
|
+
}
|
|
5757
|
+
else if (typeof value === "boolean") {
|
|
5758
|
+
url.searchParams.append(key, value ? "true" : "false");
|
|
5759
|
+
}
|
|
5760
|
+
else {
|
|
5761
|
+
url.searchParams.append(key, String(value));
|
|
5762
|
+
}
|
|
5763
|
+
});
|
|
5764
|
+
const response = await fetch(url.toString(), getFetchConfig());
|
|
5765
|
+
await handleGitLabError(response);
|
|
5766
|
+
const data = await response.json();
|
|
5767
|
+
return z.array(GitLabCiVariableSchema).parse(data);
|
|
5768
|
+
}
|
|
5769
|
+
async function getProjectVariable(projectId, key, filter) {
|
|
5770
|
+
projectId = decodeURIComponent(projectId);
|
|
5771
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/variables/${encodeURIComponent(key)}`);
|
|
5772
|
+
if (filter?.environment_scope) {
|
|
5773
|
+
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5774
|
+
}
|
|
5775
|
+
const response = await fetch(url.toString(), getFetchConfig());
|
|
5776
|
+
await handleGitLabError(response);
|
|
5777
|
+
const data = await response.json();
|
|
5778
|
+
return GitLabCiVariableSchema.parse(data);
|
|
5779
|
+
}
|
|
5780
|
+
async function createProjectVariable(projectId, options) {
|
|
5781
|
+
projectId = decodeURIComponent(projectId);
|
|
5782
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/variables`, { ...getFetchConfig(), method: "POST", body: JSON.stringify(options) });
|
|
5783
|
+
await handleGitLabError(response);
|
|
5784
|
+
const data = await response.json();
|
|
5785
|
+
return GitLabCiVariableSchema.parse(data);
|
|
5786
|
+
}
|
|
5787
|
+
async function updateProjectVariable(projectId, key, options) {
|
|
5788
|
+
projectId = decodeURIComponent(projectId);
|
|
5789
|
+
const { filter, ...body } = options;
|
|
5790
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/variables/${encodeURIComponent(key)}`);
|
|
5791
|
+
if (filter?.environment_scope) {
|
|
5792
|
+
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5793
|
+
}
|
|
5794
|
+
const response = await fetch(url.toString(), {
|
|
5795
|
+
...getFetchConfig(),
|
|
5796
|
+
method: "PUT",
|
|
5797
|
+
body: JSON.stringify(body),
|
|
5798
|
+
});
|
|
5799
|
+
await handleGitLabError(response);
|
|
5800
|
+
const data = await response.json();
|
|
5801
|
+
return GitLabCiVariableSchema.parse(data);
|
|
5802
|
+
}
|
|
5803
|
+
async function deleteProjectVariable(projectId, key, filter) {
|
|
5804
|
+
projectId = decodeURIComponent(projectId);
|
|
5805
|
+
const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/variables/${encodeURIComponent(key)}`);
|
|
5806
|
+
if (filter?.environment_scope) {
|
|
5807
|
+
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5808
|
+
}
|
|
5809
|
+
const response = await fetch(url.toString(), { ...getFetchConfig(), method: "DELETE" });
|
|
5810
|
+
await handleGitLabError(response);
|
|
5811
|
+
}
|
|
5812
|
+
async function listGroupVariables(groupId, options = {}) {
|
|
5813
|
+
const encoded = encodeURIComponent(decodeURIComponent(groupId));
|
|
5814
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables`);
|
|
5815
|
+
Object.entries(options).forEach(([key, value]) => {
|
|
5816
|
+
if (value === undefined)
|
|
5817
|
+
return;
|
|
5818
|
+
if (key === "filter" && typeof value === "object" && value !== null) {
|
|
5819
|
+
Object.entries(value).forEach(([fKey, fVal]) => {
|
|
5820
|
+
url.searchParams.append(`filter[${fKey}]`, fVal);
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5823
|
+
else if (typeof value === "boolean") {
|
|
5824
|
+
url.searchParams.append(key, value ? "true" : "false");
|
|
5825
|
+
}
|
|
5826
|
+
else {
|
|
5827
|
+
url.searchParams.append(key, String(value));
|
|
5828
|
+
}
|
|
5829
|
+
});
|
|
5830
|
+
const response = await fetch(url.toString(), getFetchConfig());
|
|
5831
|
+
await handleGitLabError(response);
|
|
5832
|
+
const data = await response.json();
|
|
5833
|
+
return z.array(GitLabCiVariableSchema).parse(data);
|
|
5834
|
+
}
|
|
5835
|
+
async function getGroupVariable(groupId, key, filter) {
|
|
5836
|
+
const encoded = encodeURIComponent(decodeURIComponent(groupId));
|
|
5837
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${encodeURIComponent(key)}`);
|
|
5838
|
+
if (filter?.environment_scope) {
|
|
5839
|
+
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5840
|
+
}
|
|
5841
|
+
const response = await fetch(url.toString(), getFetchConfig());
|
|
5842
|
+
await handleGitLabError(response);
|
|
5843
|
+
const data = await response.json();
|
|
5844
|
+
return GitLabCiVariableSchema.parse(data);
|
|
5845
|
+
}
|
|
5846
|
+
async function createGroupVariable(groupId, options) {
|
|
5847
|
+
const encoded = encodeURIComponent(decodeURIComponent(groupId));
|
|
5848
|
+
const response = await fetch(`${getEffectiveApiUrl()}/groups/${encoded}/variables`, {
|
|
5849
|
+
...getFetchConfig(),
|
|
5850
|
+
method: "POST",
|
|
5851
|
+
body: JSON.stringify(options),
|
|
5852
|
+
});
|
|
5853
|
+
await handleGitLabError(response);
|
|
5854
|
+
const data = await response.json();
|
|
5855
|
+
return GitLabCiVariableSchema.parse(data);
|
|
5856
|
+
}
|
|
5857
|
+
async function updateGroupVariable(groupId, key, options) {
|
|
5858
|
+
const encoded = encodeURIComponent(decodeURIComponent(groupId));
|
|
5859
|
+
const { filter, ...body } = options;
|
|
5860
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${encodeURIComponent(key)}`);
|
|
5861
|
+
if (filter?.environment_scope) {
|
|
5862
|
+
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5863
|
+
}
|
|
5864
|
+
const response = await fetch(url.toString(), { ...getFetchConfig(), method: "PUT", body: JSON.stringify(body) });
|
|
5865
|
+
await handleGitLabError(response);
|
|
5866
|
+
const data = await response.json();
|
|
5867
|
+
return GitLabCiVariableSchema.parse(data);
|
|
5868
|
+
}
|
|
5869
|
+
async function deleteGroupVariable(groupId, key, filter) {
|
|
5870
|
+
const encoded = encodeURIComponent(decodeURIComponent(groupId));
|
|
5871
|
+
const url = new URL(`${getEffectiveApiUrl()}/groups/${encoded}/variables/${encodeURIComponent(key)}`);
|
|
5872
|
+
if (filter?.environment_scope) {
|
|
5873
|
+
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5874
|
+
}
|
|
5875
|
+
const response = await fetch(url.toString(), { ...getFetchConfig(), method: "DELETE" });
|
|
5876
|
+
await handleGitLabError(response);
|
|
5877
|
+
}
|
|
5737
5878
|
/**
|
|
5738
5879
|
* Upload a file to a GitLab project for use in markdown content.
|
|
5739
5880
|
*
|
|
@@ -6126,6 +6267,9 @@ async function handleToolCall(params) {
|
|
|
6126
6267
|
args.iid = args.work_item_iid;
|
|
6127
6268
|
delete args.work_item_iid;
|
|
6128
6269
|
}
|
|
6270
|
+
if (!Array.isArray(args)) {
|
|
6271
|
+
params.arguments = sanitizeToolArguments(params.name, args);
|
|
6272
|
+
}
|
|
6129
6273
|
}
|
|
6130
6274
|
// Centralized read-only guard: reject write tools even if client bypasses list_tools filtering
|
|
6131
6275
|
if (GITLAB_READ_ONLY_MODE && !readOnlyTools.has(params.name)) {
|
|
@@ -7701,6 +7845,82 @@ async function handleToolCall(params) {
|
|
|
7701
7845
|
content: [{ type: "text", text: JSON.stringify(iterations, null, 2) }],
|
|
7702
7846
|
};
|
|
7703
7847
|
}
|
|
7848
|
+
// --- CI/CD Variables ---
|
|
7849
|
+
case "list_project_variables": {
|
|
7850
|
+
const args = ListProjectVariablesSchema.parse(params.arguments);
|
|
7851
|
+
const { project_id, ...options } = args;
|
|
7852
|
+
const variables = await listProjectVariables(project_id, options);
|
|
7853
|
+
return { content: [{ type: "text", text: JSON.stringify(variables, null, 2) }] };
|
|
7854
|
+
}
|
|
7855
|
+
case "get_project_variable": {
|
|
7856
|
+
const args = GetProjectVariableSchema.parse(params.arguments);
|
|
7857
|
+
const variable = await getProjectVariable(args.project_id, args.key, args.filter);
|
|
7858
|
+
return { content: [{ type: "text", text: JSON.stringify(variable, null, 2) }] };
|
|
7859
|
+
}
|
|
7860
|
+
case "create_project_variable": {
|
|
7861
|
+
const args = CreateProjectVariableSchema.parse(params.arguments);
|
|
7862
|
+
const { project_id, ...options } = args;
|
|
7863
|
+
const variable = await createProjectVariable(project_id, options);
|
|
7864
|
+
return { content: [{ type: "text", text: JSON.stringify(variable, null, 2) }] };
|
|
7865
|
+
}
|
|
7866
|
+
case "update_project_variable": {
|
|
7867
|
+
const args = UpdateProjectVariableSchema.parse(params.arguments);
|
|
7868
|
+
const { project_id, key, ...options } = args;
|
|
7869
|
+
const variable = await updateProjectVariable(project_id, key, options);
|
|
7870
|
+
return { content: [{ type: "text", text: JSON.stringify(variable, null, 2) }] };
|
|
7871
|
+
}
|
|
7872
|
+
case "delete_project_variable": {
|
|
7873
|
+
const args = DeleteProjectVariableSchema.parse(params.arguments);
|
|
7874
|
+
await deleteProjectVariable(args.project_id, args.key, args.filter);
|
|
7875
|
+
return {
|
|
7876
|
+
content: [
|
|
7877
|
+
{
|
|
7878
|
+
type: "text",
|
|
7879
|
+
text: JSON.stringify({ status: "success", message: `Variable '${args.key}' deleted from project` }, null, 2),
|
|
7880
|
+
},
|
|
7881
|
+
],
|
|
7882
|
+
};
|
|
7883
|
+
}
|
|
7884
|
+
case "list_group_variables": {
|
|
7885
|
+
rejectIfProjectScopedDeployment("list_group_variables");
|
|
7886
|
+
const args = ListGroupVariablesSchema.parse(params.arguments);
|
|
7887
|
+
const { group_id, ...options } = args;
|
|
7888
|
+
const variables = await listGroupVariables(group_id, options);
|
|
7889
|
+
return { content: [{ type: "text", text: JSON.stringify(variables, null, 2) }] };
|
|
7890
|
+
}
|
|
7891
|
+
case "get_group_variable": {
|
|
7892
|
+
rejectIfProjectScopedDeployment("get_group_variable");
|
|
7893
|
+
const args = GetGroupVariableSchema.parse(params.arguments);
|
|
7894
|
+
const variable = await getGroupVariable(args.group_id, args.key, args.filter);
|
|
7895
|
+
return { content: [{ type: "text", text: JSON.stringify(variable, null, 2) }] };
|
|
7896
|
+
}
|
|
7897
|
+
case "create_group_variable": {
|
|
7898
|
+
rejectIfProjectScopedDeployment("create_group_variable");
|
|
7899
|
+
const args = CreateGroupVariableSchema.parse(params.arguments);
|
|
7900
|
+
const { group_id, ...options } = args;
|
|
7901
|
+
const variable = await createGroupVariable(group_id, options);
|
|
7902
|
+
return { content: [{ type: "text", text: JSON.stringify(variable, null, 2) }] };
|
|
7903
|
+
}
|
|
7904
|
+
case "update_group_variable": {
|
|
7905
|
+
rejectIfProjectScopedDeployment("update_group_variable");
|
|
7906
|
+
const args = UpdateGroupVariableSchema.parse(params.arguments);
|
|
7907
|
+
const { group_id, key, ...options } = args;
|
|
7908
|
+
const variable = await updateGroupVariable(group_id, key, options);
|
|
7909
|
+
return { content: [{ type: "text", text: JSON.stringify(variable, null, 2) }] };
|
|
7910
|
+
}
|
|
7911
|
+
case "delete_group_variable": {
|
|
7912
|
+
rejectIfProjectScopedDeployment("delete_group_variable");
|
|
7913
|
+
const args = DeleteGroupVariableSchema.parse(params.arguments);
|
|
7914
|
+
await deleteGroupVariable(args.group_id, args.key, args.filter);
|
|
7915
|
+
return {
|
|
7916
|
+
content: [
|
|
7917
|
+
{
|
|
7918
|
+
type: "text",
|
|
7919
|
+
text: JSON.stringify({ status: "success", message: `Variable '${args.key}' deleted from group` }, null, 2),
|
|
7920
|
+
},
|
|
7921
|
+
],
|
|
7922
|
+
};
|
|
7923
|
+
}
|
|
7704
7924
|
case "upload_markdown": {
|
|
7705
7925
|
if (IS_REMOTE) {
|
|
7706
7926
|
const args = MarkdownUploadRemoteSchema.parse(params.arguments);
|
package/build/schemas.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { omitIncompleteMergeRequestPosition } from "./utils/merge-request-position.js";
|
|
2
3
|
// Helper: coerce a JSON-stringified array to an actual array.
|
|
3
4
|
// LLMs sometimes send '["a", "b"]' (string) instead of ["a", "b"] (array).
|
|
4
5
|
const coerceStringArray = z.preprocess((val) => {
|
|
@@ -1110,7 +1111,7 @@ export const GitLabMergeRequestSchema = z.object({
|
|
|
1110
1111
|
.describe("Whether rebase is currently in progress for this merge request"),
|
|
1111
1112
|
merge_when_pipeline_succeeds: z.coerce.boolean().optional(),
|
|
1112
1113
|
squash: z.coerce.boolean().optional(),
|
|
1113
|
-
labels: z.array(z.string()).optional(),
|
|
1114
|
+
labels: z.array(GitLabLabelSchema).or(z.array(z.string())).optional(), // Support both label objects and strings
|
|
1114
1115
|
});
|
|
1115
1116
|
export const LineRangeSchema = z
|
|
1116
1117
|
.object({
|
|
@@ -1823,6 +1824,9 @@ export const ListMergeRequestsSchema = z
|
|
|
1823
1824
|
.string()
|
|
1824
1825
|
.optional()
|
|
1825
1826
|
.describe("Returns merge requests which have the user as a reviewer by username. Mutually exclusive with reviewer_id."),
|
|
1827
|
+
approved_by_usernames: coerceStringArray
|
|
1828
|
+
.optional()
|
|
1829
|
+
.describe("Returns merge requests approved by the given usernames (array)."),
|
|
1826
1830
|
created_after: z
|
|
1827
1831
|
.string()
|
|
1828
1832
|
.optional()
|
|
@@ -2254,6 +2258,7 @@ export const MergeRequestThreadPositionSchema = z.object({
|
|
|
2254
2258
|
.optional()
|
|
2255
2259
|
.describe("IMAGE DIFFS ONLY: Y coordinate on the image (for position_type='image')."),
|
|
2256
2260
|
});
|
|
2261
|
+
const optionalMergeRequestThreadPosition = z.preprocess(omitIncompleteMergeRequestPosition, MergeRequestThreadPositionSchema.optional());
|
|
2257
2262
|
// Draft Notes API schemas
|
|
2258
2263
|
export const GitLabDraftNoteSchema = z
|
|
2259
2264
|
.object({
|
|
@@ -2295,7 +2300,7 @@ export const CreateDraftNoteSchema = ProjectParamsSchema.extend({
|
|
|
2295
2300
|
.string()
|
|
2296
2301
|
.optional()
|
|
2297
2302
|
.describe("The ID of a discussion the draft note replies to"),
|
|
2298
|
-
position:
|
|
2303
|
+
position: optionalMergeRequestThreadPosition.describe("Position when creating a diff note"),
|
|
2299
2304
|
resolve_discussion: z
|
|
2300
2305
|
.coerce.boolean()
|
|
2301
2306
|
.optional()
|
|
@@ -2306,7 +2311,7 @@ export const UpdateDraftNoteSchema = ProjectParamsSchema.extend({
|
|
|
2306
2311
|
merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
|
|
2307
2312
|
draft_note_id: z.coerce.string().describe("The ID of the draft note"),
|
|
2308
2313
|
body: z.string().optional().describe("The content of the draft note"),
|
|
2309
|
-
position:
|
|
2314
|
+
position: optionalMergeRequestThreadPosition.describe("Position when creating a diff note"),
|
|
2310
2315
|
resolve_discussion: z
|
|
2311
2316
|
.coerce.boolean()
|
|
2312
2317
|
.optional()
|
|
@@ -2330,7 +2335,7 @@ export const BulkPublishDraftNotesSchema = ProjectParamsSchema.extend({
|
|
|
2330
2335
|
export const CreateMergeRequestThreadSchema = ProjectParamsSchema.extend({
|
|
2331
2336
|
merge_request_iid: z.coerce.string().describe("The IID of a merge request"),
|
|
2332
2337
|
body: z.string().describe("The content of the thread"),
|
|
2333
|
-
position:
|
|
2338
|
+
position: optionalMergeRequestThreadPosition.describe("Position when creating a diff note"),
|
|
2334
2339
|
created_at: z.string().optional().describe("Date the thread was created at (ISO 8601 format)"),
|
|
2335
2340
|
});
|
|
2336
2341
|
export const ResolveMergeRequestThreadSchema = ProjectParamsSchema.extend({
|
|
@@ -3052,14 +3057,15 @@ export const GitLabTagSignatureSchema = z.object({
|
|
|
3052
3057
|
// --- Work item schemas (GraphQL-based) ---
|
|
3053
3058
|
// Case-insensitive work item type enum (accepts "ISSUE", "Issue", "issue")
|
|
3054
3059
|
const workItemTypeEnum = z.string().transform(v => v.toLowerCase()).pipe(z.enum(["issue", "task", "incident", "test_case", "epic", "key_result", "objective", "requirement", "ticket"]));
|
|
3060
|
+
const ProjectIdOrPathSchema = z.coerce.string().describe("Project ID, URL-encoded project path, or group path (e.g. 'group/subgroup' for group-level work items)");
|
|
3055
3061
|
// Common params for work item tools
|
|
3056
3062
|
const WorkItemParamsSchema = z.object({
|
|
3057
|
-
project_id:
|
|
3063
|
+
project_id: ProjectIdOrPathSchema,
|
|
3058
3064
|
iid: z.coerce.number().describe("The internal ID (IID) of the work item"),
|
|
3059
3065
|
});
|
|
3060
3066
|
export const GetWorkItemSchema = WorkItemParamsSchema;
|
|
3061
3067
|
export const ListWorkItemsSchema = z.object({
|
|
3062
|
-
project_id:
|
|
3068
|
+
project_id: ProjectIdOrPathSchema,
|
|
3063
3069
|
types: z
|
|
3064
3070
|
.array(workItemTypeEnum)
|
|
3065
3071
|
.optional()
|
|
@@ -3091,7 +3097,7 @@ export const ListWorkItemsSchema = z.object({
|
|
|
3091
3097
|
.describe("Cursor for pagination (from previous response's endCursor)"),
|
|
3092
3098
|
});
|
|
3093
3099
|
export const CreateWorkItemSchema = z.object({
|
|
3094
|
-
project_id:
|
|
3100
|
+
project_id: ProjectIdOrPathSchema,
|
|
3095
3101
|
title: z.string().describe("Title of the work item"),
|
|
3096
3102
|
type: workItemTypeEnum
|
|
3097
3103
|
.optional()
|
|
@@ -3161,38 +3167,38 @@ export const UpdateWorkItemSchema = WorkItemParamsSchema.extend({
|
|
|
3161
3167
|
.describe("Incident only: set escalation status"),
|
|
3162
3168
|
});
|
|
3163
3169
|
export const ConvertWorkItemTypeSchema = z.object({
|
|
3164
|
-
project_id:
|
|
3170
|
+
project_id: ProjectIdOrPathSchema,
|
|
3165
3171
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3166
3172
|
new_type: workItemTypeEnum.describe("The target work item type to convert to"),
|
|
3167
3173
|
});
|
|
3168
3174
|
export const ListWorkItemStatusesSchema = z.object({
|
|
3169
|
-
project_id:
|
|
3175
|
+
project_id: ProjectIdOrPathSchema,
|
|
3170
3176
|
work_item_type: workItemTypeEnum
|
|
3171
3177
|
.optional()
|
|
3172
3178
|
.default("issue")
|
|
3173
3179
|
.describe("The work item type to list available statuses for. Defaults to 'issue'."),
|
|
3174
3180
|
});
|
|
3175
3181
|
export const ListWorkItemNotesSchema = z.object({
|
|
3176
|
-
project_id:
|
|
3182
|
+
project_id: ProjectIdOrPathSchema,
|
|
3177
3183
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3178
3184
|
page_size: z.coerce.number().optional().default(20).describe("Number of discussions to return (default 20)"),
|
|
3179
3185
|
after: z.string().optional().describe("Cursor for pagination"),
|
|
3180
3186
|
sort: z.enum(["CREATED_ASC", "CREATED_DESC"]).optional().default("CREATED_ASC").describe("Sort order for discussions"),
|
|
3181
3187
|
});
|
|
3182
3188
|
export const CreateWorkItemNoteSchema = z.object({
|
|
3183
|
-
project_id:
|
|
3189
|
+
project_id: ProjectIdOrPathSchema,
|
|
3184
3190
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3185
3191
|
body: z.string().describe("Note body (Markdown supported)"),
|
|
3186
3192
|
internal: z.coerce.boolean().optional().default(false).describe("Create as internal/confidential note (only visible to project members)"),
|
|
3187
3193
|
discussion_id: z.string().optional().describe("Discussion ID to reply to (for threaded replies). If omitted, creates a new top-level note."),
|
|
3188
3194
|
});
|
|
3189
3195
|
export const MoveWorkItemSchema = z.object({
|
|
3190
|
-
project_id: z.coerce.string().describe("Project ID
|
|
3196
|
+
project_id: z.coerce.string().describe("Project ID, URL-encoded project path, or group path of the source namespace"),
|
|
3191
3197
|
iid: z.coerce.number().describe("The internal ID of the work item to move"),
|
|
3192
|
-
target_project_id: z.coerce.string().describe("Project ID
|
|
3198
|
+
target_project_id: z.coerce.string().describe("Project ID, URL-encoded project path, or group path of the target namespace"),
|
|
3193
3199
|
});
|
|
3194
3200
|
export const ListCustomFieldDefinitionsSchema = z.object({
|
|
3195
|
-
project_id:
|
|
3201
|
+
project_id: ProjectIdOrPathSchema,
|
|
3196
3202
|
work_item_type: workItemTypeEnum
|
|
3197
3203
|
.optional()
|
|
3198
3204
|
.default("issue")
|
|
@@ -3244,23 +3250,23 @@ export const DeleteIssueNoteEmojiReactionSchema = ProjectParamsSchema.extend({
|
|
|
3244
3250
|
});
|
|
3245
3251
|
// --- Emoji Reaction schemas (GraphQL: Work Items) ---
|
|
3246
3252
|
export const CreateWorkItemEmojiReactionSchema = z.object({
|
|
3247
|
-
project_id:
|
|
3253
|
+
project_id: ProjectIdOrPathSchema,
|
|
3248
3254
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3249
3255
|
name: emojiNameField,
|
|
3250
3256
|
});
|
|
3251
3257
|
export const DeleteWorkItemEmojiReactionSchema = z.object({
|
|
3252
|
-
project_id:
|
|
3258
|
+
project_id: ProjectIdOrPathSchema,
|
|
3253
3259
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3254
3260
|
name: emojiNameField,
|
|
3255
3261
|
});
|
|
3256
3262
|
export const CreateWorkItemNoteEmojiReactionSchema = z.object({
|
|
3257
|
-
project_id:
|
|
3263
|
+
project_id: ProjectIdOrPathSchema,
|
|
3258
3264
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3259
3265
|
note_id: z.string().describe("The GraphQL GID of the note (e.g. 'gid://gitlab/Note/123' from list_work_item_notes)"),
|
|
3260
3266
|
name: emojiNameField,
|
|
3261
3267
|
});
|
|
3262
3268
|
export const DeleteWorkItemNoteEmojiReactionSchema = z.object({
|
|
3263
|
-
project_id:
|
|
3269
|
+
project_id: ProjectIdOrPathSchema,
|
|
3264
3270
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3265
3271
|
note_id: z.string().describe("The GraphQL GID of the note (e.g. 'gid://gitlab/Note/123' from list_work_item_notes)"),
|
|
3266
3272
|
name: emojiNameField,
|
|
@@ -3282,11 +3288,11 @@ export const ListIssueNoteEmojiReactionsSchema = ProjectParamsSchema.extend({
|
|
|
3282
3288
|
discussion_id: noteEmojiDiscussionField,
|
|
3283
3289
|
});
|
|
3284
3290
|
export const ListWorkItemEmojiReactionsSchema = z.object({
|
|
3285
|
-
project_id:
|
|
3291
|
+
project_id: ProjectIdOrPathSchema,
|
|
3286
3292
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3287
3293
|
});
|
|
3288
3294
|
export const ListWorkItemNoteEmojiReactionsSchema = z.object({
|
|
3289
|
-
project_id:
|
|
3295
|
+
project_id: ProjectIdOrPathSchema,
|
|
3290
3296
|
iid: z.coerce.number().describe("The internal ID of the work item"),
|
|
3291
3297
|
note_id: z.string().describe("The GraphQL GID of the note (e.g. 'gid://gitlab/Note/123' from list_work_item_notes)"),
|
|
3292
3298
|
});
|
|
@@ -3372,3 +3378,128 @@ export const GetWebhookEventSchema = z
|
|
|
3372
3378
|
message: "Provide exactly one of project_id or group_id",
|
|
3373
3379
|
});
|
|
3374
3380
|
export const HealthCheckSchema = z.object({});
|
|
3381
|
+
// --- CI/CD Variable types ---
|
|
3382
|
+
export const GitLabCiVariableSchema = z.object({
|
|
3383
|
+
variable_type: z.enum(["env_var", "file"]).optional(),
|
|
3384
|
+
key: z.string(),
|
|
3385
|
+
value: z.string().nullable(),
|
|
3386
|
+
protected: z.boolean().optional(),
|
|
3387
|
+
masked: z.boolean().optional(),
|
|
3388
|
+
hidden: z.boolean().optional(),
|
|
3389
|
+
raw: z.boolean().optional(),
|
|
3390
|
+
environment_scope: z.string().optional(),
|
|
3391
|
+
description: z.string().nullable().optional(),
|
|
3392
|
+
});
|
|
3393
|
+
const ciVariableFields = {
|
|
3394
|
+
key: z.string().describe("The key of the variable (must match /[a-zA-Z0-9_]+/)"),
|
|
3395
|
+
value: z.string().describe("The value of the variable"),
|
|
3396
|
+
variable_type: z
|
|
3397
|
+
.enum(["env_var", "file"])
|
|
3398
|
+
.optional()
|
|
3399
|
+
.describe("The type of variable: 'env_var' (default) or 'file'"),
|
|
3400
|
+
protected: z
|
|
3401
|
+
.boolean()
|
|
3402
|
+
.optional()
|
|
3403
|
+
.describe("Whether the variable is only available on protected branches/tags"),
|
|
3404
|
+
masked: z.boolean().optional().describe("Whether the variable value is masked in job logs"),
|
|
3405
|
+
raw: z.boolean().optional().describe("Whether the variable is not expanded (treated as raw string)"),
|
|
3406
|
+
environment_scope: z
|
|
3407
|
+
.string()
|
|
3408
|
+
.optional()
|
|
3409
|
+
.describe("Environment scope (e.g. '*', 'production'). Default: '*'"),
|
|
3410
|
+
description: z.string().optional().describe("Description of the variable"),
|
|
3411
|
+
};
|
|
3412
|
+
export const ListProjectVariablesSchema = z
|
|
3413
|
+
.object({
|
|
3414
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
3415
|
+
filter: z
|
|
3416
|
+
.object({ environment_scope: z.string() })
|
|
3417
|
+
.optional()
|
|
3418
|
+
.describe("Filter by environment scope (e.g. '*', 'production')"),
|
|
3419
|
+
})
|
|
3420
|
+
.merge(PaginationOptionsSchema);
|
|
3421
|
+
export const GetProjectVariableSchema = z.object({
|
|
3422
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
3423
|
+
key: z.string().describe("The key of the variable"),
|
|
3424
|
+
filter: z
|
|
3425
|
+
.object({ environment_scope: z.string() })
|
|
3426
|
+
.optional()
|
|
3427
|
+
.describe("Filter by environment scope"),
|
|
3428
|
+
});
|
|
3429
|
+
export const CreateProjectVariableSchema = z.object({
|
|
3430
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
3431
|
+
...ciVariableFields,
|
|
3432
|
+
});
|
|
3433
|
+
export const UpdateProjectVariableSchema = z.object({
|
|
3434
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
3435
|
+
key: z.string().describe("The key of the variable to update"),
|
|
3436
|
+
value: z.string().describe("The new value of the variable"),
|
|
3437
|
+
variable_type: z.enum(["env_var", "file"]).optional().describe("The type of variable"),
|
|
3438
|
+
protected: z.boolean().optional().describe("Whether the variable is protected"),
|
|
3439
|
+
masked: z.boolean().optional().describe("Whether the variable value is masked in job logs"),
|
|
3440
|
+
raw: z.boolean().optional().describe("Whether the variable is not expanded"),
|
|
3441
|
+
environment_scope: z
|
|
3442
|
+
.string()
|
|
3443
|
+
.optional()
|
|
3444
|
+
.describe("New environment scope to assign to the variable (renames the scope, e.g. '*', 'production'). Use filter.environment_scope to identify which variable to update when multiple share the same key."),
|
|
3445
|
+
description: z.string().optional().describe("Description of the variable"),
|
|
3446
|
+
filter: z
|
|
3447
|
+
.object({ environment_scope: z.string() })
|
|
3448
|
+
.optional()
|
|
3449
|
+
.describe("Identifies which variable to update when multiple variables share the same key across different environment scopes"),
|
|
3450
|
+
});
|
|
3451
|
+
export const DeleteProjectVariableSchema = z.object({
|
|
3452
|
+
project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
|
|
3453
|
+
key: z.string().describe("The key of the variable to delete"),
|
|
3454
|
+
filter: z
|
|
3455
|
+
.object({ environment_scope: z.string() })
|
|
3456
|
+
.optional()
|
|
3457
|
+
.describe("Filter by environment scope to disambiguate when multiple variables share the same key"),
|
|
3458
|
+
});
|
|
3459
|
+
export const ListGroupVariablesSchema = z
|
|
3460
|
+
.object({
|
|
3461
|
+
group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
|
|
3462
|
+
filter: z
|
|
3463
|
+
.object({ environment_scope: z.string() })
|
|
3464
|
+
.optional()
|
|
3465
|
+
.describe("Filter by environment scope (e.g. '*', 'production')"),
|
|
3466
|
+
})
|
|
3467
|
+
.merge(PaginationOptionsSchema);
|
|
3468
|
+
export const GetGroupVariableSchema = z.object({
|
|
3469
|
+
group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
|
|
3470
|
+
key: z.string().describe("The key of the variable"),
|
|
3471
|
+
filter: z
|
|
3472
|
+
.object({ environment_scope: z.string() })
|
|
3473
|
+
.optional()
|
|
3474
|
+
.describe("Filter by environment scope"),
|
|
3475
|
+
});
|
|
3476
|
+
export const CreateGroupVariableSchema = z.object({
|
|
3477
|
+
group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
|
|
3478
|
+
...ciVariableFields,
|
|
3479
|
+
});
|
|
3480
|
+
export const UpdateGroupVariableSchema = z.object({
|
|
3481
|
+
group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
|
|
3482
|
+
key: z.string().describe("The key of the variable to update"),
|
|
3483
|
+
value: z.string().describe("The new value of the variable"),
|
|
3484
|
+
variable_type: z.enum(["env_var", "file"]).optional().describe("The type of variable"),
|
|
3485
|
+
protected: z.boolean().optional().describe("Whether the variable is protected"),
|
|
3486
|
+
masked: z.boolean().optional().describe("Whether the variable value is masked in job logs"),
|
|
3487
|
+
raw: z.boolean().optional().describe("Whether the variable is not expanded"),
|
|
3488
|
+
environment_scope: z
|
|
3489
|
+
.string()
|
|
3490
|
+
.optional()
|
|
3491
|
+
.describe("New environment scope to assign to the variable (renames the scope, e.g. '*', 'production'). Use filter.environment_scope to identify which variable to update when multiple share the same key."),
|
|
3492
|
+
description: z.string().optional().describe("Description of the variable"),
|
|
3493
|
+
filter: z
|
|
3494
|
+
.object({ environment_scope: z.string() })
|
|
3495
|
+
.optional()
|
|
3496
|
+
.describe("Identifies which variable to update when multiple variables share the same key across different environment scopes"),
|
|
3497
|
+
});
|
|
3498
|
+
export const DeleteGroupVariableSchema = z.object({
|
|
3499
|
+
group_id: z.coerce.string().describe("Group ID or URL-encoded path"),
|
|
3500
|
+
key: z.string().describe("The key of the variable to delete"),
|
|
3501
|
+
filter: z
|
|
3502
|
+
.object({ environment_scope: z.string() })
|
|
3503
|
+
.optional()
|
|
3504
|
+
.describe("Filter by environment scope to disambiguate when multiple variables share the same key"),
|
|
3505
|
+
});
|