@zereight/mcp-gitlab 2.1.6 → 2.1.7

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 CHANGED
@@ -8,13 +8,13 @@
8
8
 
9
9
  ## @zereight/mcp-gitlab
10
10
 
11
- A comprehensive GitLab MCP server for AI clients. Manage projects, merge requests, issues, pipelines, wiki, releases, milestones, and more through stdio, SSE, and Streamable HTTP.
11
+ A comprehensive GitLab MCP server for AI clients. Manage projects, merge requests, issues, pipelines, wiki, releases, tags, milestones, and more through stdio, SSE, and Streamable HTTP.
12
12
 
13
13
  Supports PAT, OAuth, read-only mode, dynamic API URLs, and remote authorization for VS Code, Claude, Cursor, Copilot, and other MCP clients.
14
14
 
15
15
  ### Why use this GitLab MCP?
16
16
 
17
- - Broad GitLab coverage — projects, repository browsing, merge requests, issues, pipelines, wiki, releases, labels, milestones, and more
17
+ - Broad GitLab coverage — projects, repository browsing, merge requests, issues, pipelines, wiki, releases, tags, labels, milestones, and more
18
18
  - Flexible auth — Personal Access Token, local OAuth2 browser flow, MCP OAuth proxy, and per-request remote authorization
19
19
  - Multiple transports — stdio for local clients, SSE for legacy clients, and Streamable HTTP for modern remote deployments
20
20
  - Client-friendly setup — examples for Claude Code, Codex, Antigravity, OpenCode, Copilot, Cline, Roo Code, Cursor, Kilo Code, and Amp Code
@@ -564,30 +564,35 @@ Register the skill directory in your AI client to get optimal tool usage guidanc
564
564
  115. `delete_release` - Delete a release from a GitLab project (does not delete the associated tag)
565
565
  116. `create_release_evidence` - Create release evidence for an existing release (GitLab Premium/Ultimate only)
566
566
  117. `download_release_asset` - Download a release asset file by direct asset path
567
- 118. `get_users` - Get GitLab user details by usernames
568
- 119. `list_events` - List all events for the currently authenticated user
569
- 120. `get_project_events` - List all visible events for a specified project
570
- 121. `upload_markdown` - Upload a file to a GitLab project for use in markdown content
571
- 122. `download_attachment` - Download an uploaded file from a GitLab project by secret and filename
572
- 123. `get_work_item` - Get a single work item with full details including status, hierarchy (parent/children), type, labels, assignees, and all widgets
573
- 124. `list_work_items` - List work items in a project with filters (type, state, search, assignees, labels). Returns items with status and hierarchy info
574
- 125. `create_work_item` - Create a new work item (issue, task, incident, test_case, epic, key_result, objective, requirement, ticket). Supports setting title, description, labels, assignees, weight, parent, health status, start/due dates, milestone, and confidentiality
575
- 126. `update_work_item` - Update a work item. Can modify title, description, labels, assignees, weight, state, status, parent hierarchy, children, health status, start/due dates, milestone, confidentiality, linked items, and custom fields
576
- 127. `convert_work_item_type` - Convert a work item to a different type (e.g. issue to task, task to incident)
577
- 128. `list_work_item_statuses` - List available statuses for a work item type in a project. Requires GitLab Premium/Ultimate with configurable statuses
578
- 129. `list_custom_field_definitions` - List available custom field definitions for a work item type in a project. Returns field names, types, and IDs needed for setting custom fields via update_work_item
579
- 130. `move_work_item` - Move a work item (issue, task, etc.) to a different project. Uses GitLab GraphQL issueMove mutation
580
- 131. `list_work_item_notes` - List notes and discussions on a work item. Returns threaded discussions with author, body, timestamps, and system/internal flags
581
- 132. `create_work_item_note` - Add a note/comment to a work item. Supports Markdown, internal notes, and threaded replies
582
- 133. `get_timeline_events` - List timeline events for an incident. Returns chronological events with notes, timestamps, and tags
583
- 134. `create_timeline_event` - Create a timeline event on an incident. Supports tags: 'Start time', 'End time', 'Impact detected', 'Response initiated', 'Impact mitigated', 'Cause identified'
584
- 135. `list_webhooks` - List all configured webhooks for a GitLab project or group. Provide either project_id or group_id
585
- 136. `list_webhook_events` - List recent webhook events (past 7 days) for a project or group webhook. Use summary mode for overview, then get_webhook_event for full details
586
- 137. `get_webhook_event` - Get full details of a specific webhook event by ID, including request/response payloads
587
- 138. `search_code` - Search for code across all projects on the GitLab instance (requires advanced search or exact code search to be enabled)
588
- 139. `search_project_code` - Search for code within a specific GitLab project (requires advanced search or exact code search to be enabled)
589
- 140. `search_group_code` - Search for code within a specific GitLab group (requires advanced search or exact code search to be enabled)
590
- 141. `execute_graphql` - Execute a GitLab GraphQL query
567
+ 118. `list_tags` - List repository tags with filtering and pagination support
568
+ 119. `get_tag` - Get details of a specific repository tag
569
+ 120. `create_tag` - Create a new tag in the repository
570
+ 121. `delete_tag` - Delete a tag from the repository
571
+ 122. `get_tag_signature` - Get the signature of a signed tag
572
+ 123. `get_users` - Get GitLab user details by usernames
573
+ 124. `list_events` - List all events for the currently authenticated user
574
+ 125. `get_project_events` - List all visible events for a specified project
575
+ 126. `upload_markdown` - Upload a file to a GitLab project for use in markdown content
576
+ 127. `download_attachment` - Download an uploaded file from a GitLab project by secret and filename
577
+ 128. `get_work_item` - Get a single work item with full details including status, hierarchy (parent/children), type, labels, assignees, and all widgets
578
+ 129. `list_work_items` - List work items in a project with filters (type, state, search, assignees, labels). Returns items with status and hierarchy info
579
+ 130. `create_work_item` - Create a new work item (issue, task, incident, test_case, epic, key_result, objective, requirement, ticket). Supports setting title, description, labels, assignees, weight, parent, health status, start/due dates, milestone, and confidentiality
580
+ 131. `update_work_item` - Update a work item. Can modify title, description, labels, assignees, weight, state, status, parent hierarchy, children, health status, start/due dates, milestone, confidentiality, linked items, and custom fields
581
+ 132. `convert_work_item_type` - Convert a work item to a different type (e.g. issue to task, task to incident)
582
+ 133. `list_work_item_statuses` - List available statuses for a work item type in a project. Requires GitLab Premium/Ultimate with configurable statuses
583
+ 134. `list_custom_field_definitions` - List available custom field definitions for a work item type in a project. Returns field names, types, and IDs needed for setting custom fields via update_work_item
584
+ 135. `move_work_item` - Move a work item (issue, task, etc.) to a different project. Uses GitLab GraphQL issueMove mutation
585
+ 136. `list_work_item_notes` - List notes and discussions on a work item. Returns threaded discussions with author, body, timestamps, and system/internal flags
586
+ 137. `create_work_item_note` - Add a note/comment to a work item. Supports Markdown, internal notes, and threaded replies
587
+ 138. `get_timeline_events` - List timeline events for an incident. Returns chronological events with notes, timestamps, and tags
588
+ 139. `create_timeline_event` - Create a timeline event on an incident. Supports tags: 'Start time', 'End time', 'Impact detected', 'Response initiated', 'Impact mitigated', 'Cause identified'
589
+ 140. `list_webhooks` - List all configured webhooks for a GitLab project or group. Provide either project_id or group_id
590
+ 141. `list_webhook_events` - List recent webhook events (past 7 days) for a project or group webhook. Use summary mode for overview, then get_webhook_event for full details
591
+ 142. `get_webhook_event` - Get full details of a specific webhook event by ID, including request/response payloads
592
+ 143. `search_code` - Search for code across all projects on the GitLab instance (requires advanced search or exact code search to be enabled)
593
+ 144. `search_project_code` - Search for code within a specific GitLab project (requires advanced search or exact code search to be enabled)
594
+ 145. `search_group_code` - Search for code within a specific GitLab group (requires advanced search or exact code search to be enabled)
595
+ 146. `execute_graphql` - Execute a GitLab GraphQL query
591
596
  <!-- TOOLS-END -->
592
597
 
593
598
  </details>
package/build/index.js CHANGED
@@ -33,7 +33,7 @@ GitLabDiscussionNoteSchema, // Added
33
33
  GitLabDiscussionSchema,
34
34
  // Draft Notes Schemas
35
35
  GitLabDraftNoteSchema, GitLabForkSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
36
- GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateLabelSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, 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, } from "./schemas.js";
36
+ GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, 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, } from "./schemas.js";
37
37
  import { randomUUID } from "node:crypto";
38
38
  import { pino } from "pino";
39
39
  const logger = pino({
@@ -5700,6 +5700,92 @@ async function downloadReleaseAsset(projectId, tagName, directAssetPath) {
5700
5700
  await handleGitLabError(response);
5701
5701
  return await response.text();
5702
5702
  }
5703
+ /**
5704
+ * List repository tags
5705
+ *
5706
+ * @param projectId The ID or URL-encoded path of the project
5707
+ * @param options Optional parameters for filtering and pagination
5708
+ * @returns Array of GitLab tags
5709
+ */
5710
+ async function listTags(projectId, options = {}) {
5711
+ const effectiveProjectId = getEffectiveProjectId(projectId);
5712
+ const url = new URL(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/tags`);
5713
+ Object.entries(options).forEach(([key, value]) => {
5714
+ if (value !== undefined) {
5715
+ url.searchParams.append(key, value.toString());
5716
+ }
5717
+ });
5718
+ const response = await fetch(url.toString(), {
5719
+ ...getFetchConfig(),
5720
+ });
5721
+ await handleGitLabError(response);
5722
+ const data = await response.json();
5723
+ return GitLabTagSchema.array().parse(data);
5724
+ }
5725
+ /**
5726
+ * Get a repository tag by name
5727
+ *
5728
+ * @param projectId The ID or URL-encoded path of the project
5729
+ * @param tagName The name of the tag
5730
+ * @returns GitLab tag
5731
+ */
5732
+ async function getTag(projectId, tagName) {
5733
+ const effectiveProjectId = getEffectiveProjectId(projectId);
5734
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/tags/${encodeURIComponent(tagName)}`, {
5735
+ ...getFetchConfig(),
5736
+ });
5737
+ await handleGitLabError(response);
5738
+ const data = await response.json();
5739
+ return GitLabTagSchema.parse(data);
5740
+ }
5741
+ /**
5742
+ * Create a new repository tag
5743
+ *
5744
+ * @param projectId The ID or URL-encoded path of the project
5745
+ * @param options Options for creating the tag
5746
+ * @returns Created GitLab tag
5747
+ */
5748
+ async function createTag(projectId, options) {
5749
+ const effectiveProjectId = getEffectiveProjectId(projectId);
5750
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/tags`, {
5751
+ ...getFetchConfig(),
5752
+ method: "POST",
5753
+ body: JSON.stringify(options),
5754
+ });
5755
+ await handleGitLabError(response);
5756
+ const data = await response.json();
5757
+ return GitLabTagSchema.parse(data);
5758
+ }
5759
+ /**
5760
+ * Delete a repository tag
5761
+ *
5762
+ * @param projectId The ID or URL-encoded path of the project
5763
+ * @param tagName The name of the tag
5764
+ */
5765
+ async function deleteTag(projectId, tagName) {
5766
+ const effectiveProjectId = getEffectiveProjectId(projectId);
5767
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/tags/${encodeURIComponent(tagName)}`, {
5768
+ ...getFetchConfig(),
5769
+ method: "DELETE",
5770
+ });
5771
+ await handleGitLabError(response);
5772
+ }
5773
+ /**
5774
+ * Get the signature of a repository tag
5775
+ *
5776
+ * @param projectId The ID or URL-encoded path of the project
5777
+ * @param tagName The name of the tag
5778
+ * @returns Tag signature
5779
+ */
5780
+ async function getTagSignature(projectId, tagName) {
5781
+ const effectiveProjectId = getEffectiveProjectId(projectId);
5782
+ const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}/repository/tags/${encodeURIComponent(tagName)}/signature`, {
5783
+ ...getFetchConfig(),
5784
+ });
5785
+ await handleGitLabError(response);
5786
+ const data = await response.json();
5787
+ return GitLabTagSignatureSchema.parse(data);
5788
+ }
5703
5789
  // Request handlers are now registered inside createServer() factory function
5704
5790
  // to ensure each transport connection gets its own Server instance (GHSA-345p-7cg4-v4c7).
5705
5791
  async function handleToolCall(params) {
@@ -7191,6 +7277,48 @@ async function handleToolCall(params) {
7191
7277
  content: [{ type: "text", text: assetContent }],
7192
7278
  };
7193
7279
  }
7280
+ case "list_tags": {
7281
+ const args = ListTagsSchema.parse(params.arguments);
7282
+ const { project_id, ...options } = args;
7283
+ const tags = await listTags(project_id, options);
7284
+ return {
7285
+ content: [{ type: "text", text: JSON.stringify(tags, null, 2) }],
7286
+ };
7287
+ }
7288
+ case "get_tag": {
7289
+ const args = GetTagSchema.parse(params.arguments);
7290
+ const tag = await getTag(args.project_id, args.tag_name);
7291
+ return {
7292
+ content: [{ type: "text", text: JSON.stringify(tag, null, 2) }],
7293
+ };
7294
+ }
7295
+ case "create_tag": {
7296
+ const args = CreateTagSchema.parse(params.arguments);
7297
+ const { project_id, ...options } = args;
7298
+ const tag = await createTag(project_id, options);
7299
+ return {
7300
+ content: [{ type: "text", text: JSON.stringify(tag, null, 2) }],
7301
+ };
7302
+ }
7303
+ case "delete_tag": {
7304
+ const args = DeleteTagSchema.parse(params.arguments);
7305
+ await deleteTag(args.project_id, args.tag_name);
7306
+ return {
7307
+ content: [
7308
+ {
7309
+ type: "text",
7310
+ text: JSON.stringify({ status: "success", message: `Tag '${args.tag_name}' deleted successfully` }, null, 2),
7311
+ },
7312
+ ],
7313
+ };
7314
+ }
7315
+ case "get_tag_signature": {
7316
+ const args = GetTagSignatureSchema.parse(params.arguments);
7317
+ const signature = await getTagSignature(args.project_id, args.tag_name);
7318
+ return {
7319
+ content: [{ type: "text", text: JSON.stringify(signature, null, 2) }],
7320
+ };
7321
+ }
7194
7322
  case "list_webhooks": {
7195
7323
  const args = ListWebhooksSchema.parse(params.arguments);
7196
7324
  const webhooks = await listWebhooks(args);
package/build/schemas.js CHANGED
@@ -2570,6 +2570,84 @@ export const DownloadReleaseAssetSchema = z.object({
2570
2570
  .string()
2571
2571
  .describe("Path to the release asset file as specified when creating or updating its link"),
2572
2572
  });
2573
+ // Tag schemas
2574
+ export const ListTagsSchema = z
2575
+ .object({
2576
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
2577
+ order_by: z
2578
+ .enum(["name", "updated", "version"])
2579
+ .optional()
2580
+ .describe("Return tags ordered by name, updated, or version. Default is updated."),
2581
+ sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
2582
+ search: z
2583
+ .string()
2584
+ .optional()
2585
+ .describe("Restrict on tag name. You can use ^term and term$ to find tags that begin and end with term. No other regular expressions are supported."),
2586
+ })
2587
+ .merge(PaginationOptionsSchema);
2588
+ export const GetTagSchema = z.object({
2589
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
2590
+ tag_name: z.string().describe("The name of the tag"),
2591
+ });
2592
+ export const CreateTagSchema = z.object({
2593
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
2594
+ tag_name: z.string().describe("The name of the tag"),
2595
+ ref: z.string().describe("Create tag using commit SHA, another tag name, or branch name"),
2596
+ message: z.string().optional().describe("Create annotated tag with message"),
2597
+ });
2598
+ export const DeleteTagSchema = z.object({
2599
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
2600
+ tag_name: z.string().describe("The name of the tag"),
2601
+ });
2602
+ export const GetTagSignatureSchema = z.object({
2603
+ project_id: z.coerce.string().describe("Project ID or URL-encoded path"),
2604
+ tag_name: z.string().describe("The name of the tag"),
2605
+ });
2606
+ export const GitLabTagSchema = z.object({
2607
+ name: z.string(),
2608
+ message: z.string().nullable(),
2609
+ target: z.string(),
2610
+ commit: z.object({
2611
+ id: z.string(),
2612
+ short_id: z.string(),
2613
+ title: z.string(),
2614
+ created_at: z.string(),
2615
+ parent_ids: z.array(z.string()),
2616
+ message: z.string(),
2617
+ author_name: z.string(),
2618
+ author_email: z.string(),
2619
+ authored_date: z.string(),
2620
+ committer_name: z.string(),
2621
+ committer_email: z.string(),
2622
+ committed_date: z.string(),
2623
+ }),
2624
+ release: z
2625
+ .object({
2626
+ tag_name: z.string(),
2627
+ description: z.string(),
2628
+ })
2629
+ .nullable(),
2630
+ protected: z.boolean(),
2631
+ created_at: z.string().nullable(),
2632
+ });
2633
+ export const GitLabTagSignatureSchema = z.object({
2634
+ signature_type: z.literal("X509"),
2635
+ verification_status: z.string(),
2636
+ x509_certificate: z.object({
2637
+ id: z.number(),
2638
+ subject: z.string(),
2639
+ subject_key_identifier: z.string(),
2640
+ email: z.string().nullable().optional(),
2641
+ serial_number: z.number(),
2642
+ certificate_status: z.string(),
2643
+ x509_issuer: z.object({
2644
+ id: z.number(),
2645
+ subject: z.string(),
2646
+ subject_key_identifier: z.string(),
2647
+ crl_url: z.string().nullable().optional(),
2648
+ }),
2649
+ }),
2650
+ });
2573
2651
  // --- Work item schemas (GraphQL-based) ---
2574
2652
  // Case-insensitive work item type enum (accepts "ISSUE", "Issue", "issue")
2575
2653
  const workItemTypeEnum = z.string().transform(v => v.toLowerCase()).pipe(z.enum(["issue", "task", "incident", "test_case", "epic", "key_result", "objective", "requirement", "ticket"]));
@@ -0,0 +1,206 @@
1
+ import { after, before, describe, test } from "node:test";
2
+ import assert from "node:assert";
3
+ import { spawn } from "child_process";
4
+ import { MockGitLabServer, findMockServerPort } from "./utils/mock-gitlab-server.js";
5
+ const MOCK_TOKEN = "glpat-mock-token-tags";
6
+ const TEST_PROJECT_ID = "123";
7
+ const TEST_TAG_NAME = "v1.0.0";
8
+ function buildTag(overrides = {}) {
9
+ return {
10
+ name: TEST_TAG_NAME,
11
+ message: "Annotated release tag",
12
+ target: "abc123def4567890",
13
+ commit: {
14
+ id: "abc123def4567890",
15
+ short_id: "abc123de",
16
+ title: "Release v1.0.0",
17
+ created_at: "2026-03-13T10:00:00.000Z",
18
+ parent_ids: ["1111111111111111"],
19
+ message: "Release v1.0.0",
20
+ author_name: "Test User",
21
+ author_email: "test@example.com",
22
+ authored_date: "2026-03-13T09:55:00.000Z",
23
+ committer_name: "Test User",
24
+ committer_email: "test@example.com",
25
+ committed_date: "2026-03-13T10:00:00.000Z",
26
+ },
27
+ release: {
28
+ tag_name: TEST_TAG_NAME,
29
+ description: "Release notes",
30
+ },
31
+ protected: false,
32
+ created_at: "2026-03-13T10:00:00.000Z",
33
+ ...overrides,
34
+ };
35
+ }
36
+ async function callTool(toolName, args, env) {
37
+ return new Promise((resolve, reject) => {
38
+ const proc = spawn("node", ["build/index.js"], {
39
+ stdio: ["pipe", "pipe", "pipe"],
40
+ env: {
41
+ ...process.env,
42
+ ...env,
43
+ },
44
+ });
45
+ let output = "";
46
+ let errorOutput = "";
47
+ proc.stdout?.on("data", (d) => (output += d));
48
+ proc.stderr?.on("data", (d) => (errorOutput += d));
49
+ proc.on("close", code => {
50
+ if (code !== 0) {
51
+ return reject(new Error(`Process exited with code ${code}: ${errorOutput}`));
52
+ }
53
+ const line = output.split("\n").find(l => l.startsWith("{"));
54
+ if (!line) {
55
+ return reject(new Error("No JSON output found"));
56
+ }
57
+ try {
58
+ const response = JSON.parse(line);
59
+ if (response.error) {
60
+ reject(response.error);
61
+ return;
62
+ }
63
+ const content = response.result?.content?.[0]?.text;
64
+ if (!content) {
65
+ resolve(response.result);
66
+ return;
67
+ }
68
+ try {
69
+ resolve(JSON.parse(content));
70
+ }
71
+ catch {
72
+ resolve(content);
73
+ }
74
+ }
75
+ catch (error) {
76
+ reject(error);
77
+ }
78
+ });
79
+ proc.stdin?.end(JSON.stringify({
80
+ jsonrpc: "2.0",
81
+ id: 1,
82
+ method: "tools/call",
83
+ params: { name: toolName, arguments: args },
84
+ }) + "\n");
85
+ });
86
+ }
87
+ describe("tag tools", () => {
88
+ let mockGitLab;
89
+ let mockGitLabUrl;
90
+ before(async () => {
91
+ const mockPort = await findMockServerPort(20000, 50);
92
+ mockGitLab = new MockGitLabServer({
93
+ port: mockPort,
94
+ validTokens: [MOCK_TOKEN],
95
+ });
96
+ mockGitLab.addMockHandler("get", `/projects/${TEST_PROJECT_ID}/repository/tags`, (req, res) => {
97
+ assert.strictEqual(req.query.search, "^v");
98
+ assert.strictEqual(req.query.order_by, "version");
99
+ assert.strictEqual(req.query.sort, "desc");
100
+ assert.strictEqual(req.query.page, "2");
101
+ assert.strictEqual(req.query.per_page, "10");
102
+ res.json([buildTag()]);
103
+ });
104
+ mockGitLab.addMockHandler("get", `/projects/${TEST_PROJECT_ID}/repository/tags/${encodeURIComponent(TEST_TAG_NAME)}`, (_req, res) => {
105
+ res.json(buildTag());
106
+ });
107
+ mockGitLab.addMockHandler("post", `/projects/${TEST_PROJECT_ID}/repository/tags`, (req, res) => {
108
+ assert.deepStrictEqual(req.body, {
109
+ tag_name: TEST_TAG_NAME,
110
+ ref: "main",
111
+ message: "Release tag",
112
+ });
113
+ res.json(buildTag({ created_at: null }));
114
+ });
115
+ mockGitLab.addMockHandler("delete", `/projects/${TEST_PROJECT_ID}/repository/tags/${encodeURIComponent(TEST_TAG_NAME)}`, (_req, res) => {
116
+ res.status(204).send();
117
+ });
118
+ mockGitLab.addMockHandler("get", `/projects/${TEST_PROJECT_ID}/repository/tags/${encodeURIComponent(TEST_TAG_NAME)}/signature`, (_req, res) => {
119
+ res.json({
120
+ signature_type: "X509",
121
+ verification_status: "unverified",
122
+ x509_certificate: {
123
+ id: 1,
124
+ subject: "CN=Test User,O=Example",
125
+ subject_key_identifier: "A1725E379E7B25B2",
126
+ email: null,
127
+ serial_number: 123456789,
128
+ certificate_status: "good",
129
+ x509_issuer: {
130
+ id: 1,
131
+ subject: "CN=Example CA,O=Example",
132
+ subject_key_identifier: "C6411E6F5B9E2CFD",
133
+ crl_url: null,
134
+ },
135
+ },
136
+ });
137
+ });
138
+ await mockGitLab.start();
139
+ mockGitLabUrl = mockGitLab.getUrl();
140
+ });
141
+ after(async () => {
142
+ await mockGitLab.stop();
143
+ });
144
+ const env = () => ({
145
+ GITLAB_API_URL: `${mockGitLabUrl}/api/v4`,
146
+ GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN,
147
+ GITLAB_TOOLSETS: "tags",
148
+ });
149
+ test("list_tags returns parsed tags with query filters", async () => {
150
+ const result = await callTool("list_tags", {
151
+ project_id: TEST_PROJECT_ID,
152
+ search: "^v",
153
+ order_by: "version",
154
+ sort: "desc",
155
+ page: 2,
156
+ per_page: 10,
157
+ }, env());
158
+ assert.ok(Array.isArray(result));
159
+ assert.strictEqual(result.length, 1);
160
+ assert.strictEqual(result[0].name, TEST_TAG_NAME);
161
+ });
162
+ test("get_tag returns a single tag", async () => {
163
+ const result = await callTool("get_tag", { project_id: TEST_PROJECT_ID, tag_name: TEST_TAG_NAME }, env());
164
+ assert.strictEqual(result.name, TEST_TAG_NAME);
165
+ assert.strictEqual(result.commit.short_id, "abc123de");
166
+ });
167
+ test("create_tag posts the expected payload", async () => {
168
+ const result = await callTool("create_tag", {
169
+ project_id: TEST_PROJECT_ID,
170
+ tag_name: TEST_TAG_NAME,
171
+ ref: "main",
172
+ message: "Release tag",
173
+ }, env());
174
+ assert.strictEqual(result.name, TEST_TAG_NAME);
175
+ assert.strictEqual(result.release.description, "Release notes");
176
+ assert.strictEqual(result.created_at, null);
177
+ });
178
+ test("delete_tag returns a success payload", async () => {
179
+ const result = await callTool("delete_tag", { project_id: TEST_PROJECT_ID, tag_name: TEST_TAG_NAME }, env());
180
+ assert.deepStrictEqual(result, {
181
+ status: "success",
182
+ message: `Tag '${TEST_TAG_NAME}' deleted successfully`,
183
+ });
184
+ });
185
+ test("get_tag_signature returns signature data", async () => {
186
+ const result = await callTool("get_tag_signature", { project_id: TEST_PROJECT_ID, tag_name: TEST_TAG_NAME }, env());
187
+ assert.deepStrictEqual(result, {
188
+ signature_type: "X509",
189
+ verification_status: "unverified",
190
+ x509_certificate: {
191
+ id: 1,
192
+ subject: "CN=Test User,O=Example",
193
+ subject_key_identifier: "A1725E379E7B25B2",
194
+ email: null,
195
+ serial_number: 123456789,
196
+ certificate_status: "good",
197
+ x509_issuer: {
198
+ id: 1,
199
+ subject: "CN=Example CA,O=Example",
200
+ subject_key_identifier: "C6411E6F5B9E2CFD",
201
+ crl_url: null,
202
+ },
203
+ },
204
+ });
205
+ });
206
+ });
@@ -26,6 +26,7 @@ const TOOLSET_TOOL_COUNTS = {
26
26
  milestones: 9,
27
27
  wiki: 10,
28
28
  releases: 7,
29
+ tags: 5,
29
30
  users: 5,
30
31
  search: 3,
31
32
  workitems: 18,
@@ -40,7 +41,16 @@ const DEFAULT_TOOLSETS = [
40
41
  "labels",
41
42
  "users",
42
43
  ];
43
- const NON_DEFAULT_TOOLSETS = ["pipelines", "milestones", "wiki", "releases", "workitems", "webhooks", "search"];
44
+ const NON_DEFAULT_TOOLSETS = [
45
+ "pipelines",
46
+ "milestones",
47
+ "wiki",
48
+ "releases",
49
+ "tags",
50
+ "workitems",
51
+ "webhooks",
52
+ "search",
53
+ ];
44
54
  // discover_tools meta-tool is always force-injected (Step 5.5)
45
55
  const DISCOVER_TOOLS_COUNT = 1;
46
56
  const DEFAULT_TOOL_COUNT = DEFAULT_TOOLSETS.reduce((sum, id) => sum + TOOLSET_TOOL_COUNTS[id], 0) + DISCOVER_TOOLS_COUNT;
@@ -57,6 +67,7 @@ const TOOLSET_SAMPLE_TOOLS = {
57
67
  milestones: ["list_milestones", "create_milestone", "get_milestone_burndown_events"],
58
68
  wiki: ["list_wiki_pages", "create_wiki_page", "list_group_wiki_pages", "create_group_wiki_page"],
59
69
  releases: ["list_releases", "create_release", "download_release_asset"],
70
+ tags: ["list_tags", "create_tag", "get_tag_signature"],
60
71
  users: ["get_users", "upload_markdown", "download_attachment"],
61
72
  search: ["search_code", "search_project_code", "search_group_code"],
62
73
  webhooks: ["list_webhooks", "list_webhook_events", "get_webhook_event"],
@@ -1,7 +1,7 @@
1
1
  import { zodToJsonSchema } from "zod-to-json-schema";
2
2
  import { toJSONSchema } from "../utils/schema.js";
3
3
  import { USE_GITLAB_WIKI, USE_MILESTONE, USE_PIPELINE, } from "../config.js";
4
- import { ApproveMergeRequestSchema, BulkPublishDraftNotesSchema, CancelPipelineJobSchema, CancelPipelineSchema, ConvertWorkItemTypeSchema, CreateBranchSchema, CreateDraftNoteSchema, CreateGroupWikiPageSchema, CreateIssueLinkSchema, CreateIssueNoteSchema, CreateIssueSchema, CreateIssueEmojiReactionSchema, CreateIssueNoteEmojiReactionSchema, ListIssueEmojiReactionsSchema, ListIssueNoteEmojiReactionsSchema, CreateLabelSchema, CreateMergeRequestDiscussionNoteSchema, CreateMergeRequestEmojiReactionSchema, ListMergeRequestEmojiReactionsSchema, ListMergeRequestNoteEmojiReactionsSchema, CreateMergeRequestNoteSchema, CreateMergeRequestNoteEmojiReactionSchema, CreateMergeRequestSchema, CreateMergeRequestThreadSchema, CreateNoteSchema, CreateOrUpdateFileSchema, CreatePipelineSchema, CreateProjectMilestoneSchema, CreateReleaseEvidenceSchema, CreateReleaseSchema, CreateRepositorySchema, CreateTimelineEventSchema, CreateWikiPageSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, CreateWorkItemSchema, DeleteDraftNoteSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, ExecuteGraphQLSchema, ForkRepositorySchema, GetBranchDiffsSchema, GetCommitDiffSchema, GetCommitSchema, GetDeploymentSchema, GetDraftNoteSchema, GetEnvironmentSchema, GetFileContentsSchema, GetGroupWikiPageSchema, GetIssueLinkSchema, GetIssueSchema, GetJobArtifactFileSchema, GetLabelSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GetMergeRequestDiffsSchema, GetMergeRequestFileDiffSchema, GetMergeRequestNoteSchema, GetMergeRequestNotesSchema, GetMergeRequestSchema, GetMergeRequestVersionSchema, GetMilestoneBurndownEventsSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, GetNamespaceSchema, GetPipelineJobOutputSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTimelineEventsSchema, GetUsersSchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListCommitsSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ListPipelinesSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListReleasesSchema, ListWebhookEventsSchema, ListWebhooksSchema, ListWikiPagesSchema, ListWorkItemNotesSchema, ListWorkItemStatusesSchema, ListWorkItemsSchema, MarkdownUploadSchema, MergeMergeRequestSchema, MoveWorkItemSchema, MyIssuesSchema, PlayPipelineJobSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PushFilesSchema, ResolveMergeRequestThreadSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UnapproveMergeRequestSchema, UpdateDraftNoteSchema, UpdateGroupWikiPageSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateLabelSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, } from "../schemas.js";
4
+ import { ApproveMergeRequestSchema, BulkPublishDraftNotesSchema, CancelPipelineJobSchema, CancelPipelineSchema, ConvertWorkItemTypeSchema, CreateBranchSchema, CreateDraftNoteSchema, CreateGroupWikiPageSchema, CreateIssueLinkSchema, CreateIssueNoteSchema, CreateIssueSchema, CreateIssueEmojiReactionSchema, CreateIssueNoteEmojiReactionSchema, ListIssueEmojiReactionsSchema, ListIssueNoteEmojiReactionsSchema, CreateLabelSchema, CreateMergeRequestDiscussionNoteSchema, CreateMergeRequestEmojiReactionSchema, ListMergeRequestEmojiReactionsSchema, ListMergeRequestNoteEmojiReactionsSchema, CreateMergeRequestNoteSchema, CreateMergeRequestNoteEmojiReactionSchema, CreateMergeRequestSchema, CreateMergeRequestThreadSchema, CreateNoteSchema, CreateOrUpdateFileSchema, CreatePipelineSchema, CreateProjectMilestoneSchema, CreateReleaseEvidenceSchema, CreateReleaseSchema, CreateRepositorySchema, CreateTagSchema, CreateTimelineEventSchema, CreateWikiPageSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, CreateWorkItemSchema, DeleteDraftNoteSchema, DeleteGroupWikiPageSchema, DeleteIssueLinkSchema, DeleteIssueSchema, DeleteIssueEmojiReactionSchema, DeleteIssueNoteEmojiReactionSchema, DeleteLabelSchema, DeleteMergeRequestDiscussionNoteSchema, DeleteMergeRequestNoteSchema, DeleteMergeRequestEmojiReactionSchema, DeleteMergeRequestNoteEmojiReactionSchema, DeleteProjectMilestoneSchema, DeleteReleaseSchema, DeleteTagSchema, DeleteWikiPageSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, DownloadReleaseAssetSchema, EditProjectMilestoneSchema, ExecuteGraphQLSchema, ForkRepositorySchema, GetBranchDiffsSchema, GetCommitDiffSchema, GetCommitSchema, GetDeploymentSchema, GetDraftNoteSchema, GetEnvironmentSchema, GetFileContentsSchema, GetGroupWikiPageSchema, GetIssueLinkSchema, GetIssueSchema, GetJobArtifactFileSchema, GetLabelSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GetMergeRequestDiffsSchema, GetMergeRequestFileDiffSchema, GetMergeRequestNoteSchema, GetMergeRequestNotesSchema, GetMergeRequestSchema, GetMergeRequestVersionSchema, GetMilestoneBurndownEventsSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, GetNamespaceSchema, GetPipelineJobOutputSchema, GetPipelineSchema, GetProjectEventsSchema, GetProjectMilestoneSchema, GetProjectSchema, GetReleaseSchema, GetRepositoryTreeSchema, GetTagSchema, GetTagSignatureSchema, GetTimelineEventsSchema, GetUsersSchema, GetWebhookEventSchema, GetWikiPageSchema, GetWorkItemSchema, ListCommitsSchema, ListCustomFieldDefinitionsSchema, ListDeploymentsSchema, ListDraftNotesSchema, ListEnvironmentsSchema, ListEventsSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, ListGroupWikiPagesSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListJobArtifactsSchema, ListLabelsSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiffsSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestVersionsSchema, ListMergeRequestsSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelineTriggerJobsSchema, ListPipelinesSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListReleasesSchema, ListTagsSchema, ListWebhookEventsSchema, ListWebhooksSchema, ListWikiPagesSchema, ListWorkItemNotesSchema, ListWorkItemStatusesSchema, ListWorkItemsSchema, MarkdownUploadSchema, MergeMergeRequestSchema, MoveWorkItemSchema, MyIssuesSchema, PlayPipelineJobSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PushFilesSchema, ResolveMergeRequestThreadSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UnapproveMergeRequestSchema, UpdateDraftNoteSchema, UpdateGroupWikiPageSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateLabelSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestSchema, UpdateReleaseSchema, UpdateWikiPageSchema, UpdateWorkItemSchema, VerifyNamespaceSchema, } from "../schemas.js";
5
5
  // Define all available tools
6
6
  export const allTools = [
7
7
  {
@@ -681,6 +681,31 @@ export const allTools = [
681
681
  description: "Download a release asset file by direct asset path",
682
682
  inputSchema: toJSONSchema(DownloadReleaseAssetSchema),
683
683
  },
684
+ {
685
+ name: "list_tags",
686
+ description: "List repository tags for a project",
687
+ inputSchema: toJSONSchema(ListTagsSchema),
688
+ },
689
+ {
690
+ name: "get_tag",
691
+ description: "Get a repository tag by name",
692
+ inputSchema: toJSONSchema(GetTagSchema),
693
+ },
694
+ {
695
+ name: "create_tag",
696
+ description: "Create a new repository tag",
697
+ inputSchema: toJSONSchema(CreateTagSchema),
698
+ },
699
+ {
700
+ name: "delete_tag",
701
+ description: "Delete a repository tag",
702
+ inputSchema: toJSONSchema(DeleteTagSchema),
703
+ },
704
+ {
705
+ name: "get_tag_signature",
706
+ description: "Get the X.509 signature of a signed tag (404 if unsigned)",
707
+ inputSchema: toJSONSchema(GetTagSignatureSchema),
708
+ },
684
709
  // --- Work item tools (GraphQL-based) ---
685
710
  {
686
711
  name: "get_work_item",
@@ -892,6 +917,9 @@ export const readOnlyTools = new Set([
892
917
  "list_releases",
893
918
  "get_release",
894
919
  "download_release_asset",
920
+ "list_tags",
921
+ "get_tag",
922
+ "get_tag_signature",
895
923
  "get_merge_request_approval_state",
896
924
  "get_work_item",
897
925
  "list_work_items",
@@ -919,6 +947,7 @@ export const destructiveTools = new Set([
919
947
  "delete_group_wiki_page",
920
948
  "delete_milestone",
921
949
  "delete_release",
950
+ "delete_tag",
922
951
  "delete_merge_request_note",
923
952
  "delete_merge_request_discussion_note",
924
953
  "delete_draft_note",
@@ -1169,6 +1198,17 @@ export const TOOLSET_DEFINITIONS = [
1169
1198
  "download_release_asset",
1170
1199
  ]),
1171
1200
  },
1201
+ {
1202
+ id: "tags",
1203
+ isDefault: false,
1204
+ tools: new Set([
1205
+ "list_tags",
1206
+ "get_tag",
1207
+ "create_tag",
1208
+ "delete_tag",
1209
+ "get_tag_signature",
1210
+ ]),
1211
+ },
1172
1212
  {
1173
1213
  id: "users",
1174
1214
  isDefault: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.1.6",
3
+ "version": "2.1.7",
4
4
  "mcpName": "io.github.zereight/gitlab-mcp",
5
5
  "description": "GitLab MCP server for projects, merge requests, issues, pipelines, wiki, releases, and more",
6
6
  "keywords": [
@@ -51,7 +51,7 @@
51
51
  "changelog": "auto-changelog -p",
52
52
  "test": "npm run test:all",
53
53
  "test:all": "npm run build && npm run test:mock && npm run test:live",
54
- "test:mock": "node --import tsx/esm --test test/remote-auth-simple-test.ts && node --import tsx/esm --test test/mcp-oauth-tests.ts && tsx test/oauth-tests.ts && tsx test/test-list-merge-requests.ts && tsx test/test-list-project-members.ts && tsx test/test-download-attachment.ts && node --import tsx/esm --test test/test-job-artifacts.ts && node --import tsx/esm --test test/test-deployment-tools.ts && node --import tsx/esm --test test/test-merge-request-approval-state-tools.ts && node --import tsx/esm --test test/test-search-code.ts && node --import tsx/esm --test test/test-toolset-filtering.ts && node --import tsx/esm --test test/test-auth-retry.ts && node --import tsx/esm --test test/stateless/codec.test.ts test/stateless/client-id.test.ts test/stateless/callback-proxy.test.ts test/stateless/session-id.test.ts test/stateless/session-id-integration.test.ts test/stateless/config-ttl.test.ts",
54
+ "test:mock": "node --import tsx/esm --test test/remote-auth-simple-test.ts && node --import tsx/esm --test test/mcp-oauth-tests.ts && tsx test/oauth-tests.ts && tsx test/test-list-merge-requests.ts && tsx test/test-list-project-members.ts && tsx test/test-download-attachment.ts && node --import tsx/esm --test test/test-job-artifacts.ts && node --import tsx/esm --test test/test-deployment-tools.ts && node --import tsx/esm --test test/test-merge-request-approval-state-tools.ts && node --import tsx/esm --test test/test-search-code.ts && node --import tsx/esm --test test/test-tags.ts && node --import tsx/esm --test test/test-toolset-filtering.ts && node --import tsx/esm --test test/test-auth-retry.ts && node --import tsx/esm --test test/stateless/codec.test.ts test/stateless/client-id.test.ts test/stateless/callback-proxy.test.ts test/stateless/session-id.test.ts test/stateless/session-id-integration.test.ts test/stateless/config-ttl.test.ts",
55
55
  "test:stateless": "npm run build && node --import tsx/esm --test test/stateless/codec.test.ts test/stateless/client-id.test.ts test/stateless/callback-proxy.test.ts test/stateless/session-id.test.ts test/stateless/session-id-integration.test.ts test/stateless/config-ttl.test.ts",
56
56
  "test:mcp-oauth": "npm run build && node --import tsx/esm --test test/mcp-oauth-tests.ts",
57
57
  "test:live": "node test/validate-api.js",