Claude Code Plugins

Community-maintained marketplace

Feedback

GitLab REST API via curl. Use this skill to manage projects, issues, merge requests, and pipelines in GitLab.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name gitlab
description GitLab REST API via curl. Use this skill to manage projects, issues, merge requests, and pipelines in GitLab.
vm0_secrets GITLAB_TOKEN
vm0_vars GITLAB_HOST

GitLab API

Use the GitLab REST API via direct curl calls to manage projects, issues, merge requests, and pipelines.

Official docs: https://docs.gitlab.com/ee/api/


When to Use

Use this skill when you need to:

  • Manage projects - list, create, get project details
  • Handle issues - create, update, close, list issues
  • Work with merge requests - create, list, merge MRs
  • Check pipelines - list jobs, view pipeline status
  • Manage users - search users, get user info

Prerequisites

  1. Go to GitLab → User Settings → Access Tokens
  2. Create a personal access token with api scope
  3. Copy the generated token
export GITLAB_HOST="gitlab.com" # Or your self-hosted GitLab domain
export GITLAB_TOKEN="glpat-xxxxxxxxxxxx" # Personal access token with api scope

Rate Limits

GitLab.com has rate limits of ~2000 requests per minute for authenticated users. Self-hosted instances may vary.


Important: When using $VAR in a command that pipes to another command, wrap the command containing $VAR in bash -c '...'. Due to a Claude Code bug, environment variables are silently cleared when pipes are used directly.

bash -c 'curl -s "https://api.example.com" -H "Authorization: Bearer $API_KEY"' | jq .

How to Use

All examples below assume GITLAB_HOST and GITLAB_TOKEN are set.

Base URL: https://${GITLAB_HOST}/api/v4

Note: Project IDs can be numeric (e.g., 123) or URL-encoded paths (e.g., mygroup%2Fmyproject).


1. Get Current User

Verify your authentication:

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/user" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '{id, username, name, email, state}

2. List Projects

Get projects accessible to you:

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects?membership=true&per_page=20" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '.[] | {id, path_with_namespace, visibility, default_branch}

Filter options:

  • membership=true - Only projects you're a member of
  • owned=true - Only projects you own
  • search=keyword - Search by name
  • visibility=public|internal|private - Filter by visibility

3. Get Project Details

Get details for a specific project:

PROJECT_ID="123" # or URL-encoded path like "mygroup%2Fmyproject"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '{id, name, path_with_namespace, default_branch, visibility, web_url}

4. List Project Issues

Get issues for a project:

PROJECT_ID="123"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues?state=opened&per_page=20" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '.[] | {iid, title, state, author: .author.username, labels, web_url}

Filter options:

  • state=opened|closed|all - Filter by state
  • labels=bug,urgent - Filter by labels
  • assignee_id=123 - Filter by assignee
  • search=keyword - Search in title/description

5. Get Issue Details

Get a specific issue:

PROJECT_ID="123"
ISSUE_IID="1" # Issue internal ID (not global ID)

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues/${ISSUE_IID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '{iid, title, description, state, author: .author.username, assignees: [.assignees[].username], labels, created_at, web_url}

6. Create Issue

Create a new issue in a project:

PROJECT_ID="123"

Write to /tmp/gitlab_request.json:

{
  "title": "Bug: Login page not loading",
  "description": "The login page shows a blank screen on mobile devices.",
  "labels": "bug,frontend"
}

Then run:

bash -c 'curl -s -X POST "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{iid, title, web_url}'

7. Create Issue with Assignee and Milestone

Create issue with additional fields:

PROJECT_ID="123"
ASSIGNEE_ID="456"
MILESTONE_ID="1"

Write to /tmp/gitlab_request.json:

{
  "title": "Implement user profile page",
  "description": "Create a user profile page with avatar and bio.",
  "assignee_ids": [${ASSIGNEE_ID}],
  "milestone_id": ${MILESTONE_ID},
  "labels": "feature,frontend"
}

Then run:

bash -c 'curl -s -X POST "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{iid, title, web_url}'

8. Update Issue

Update an existing issue:

PROJECT_ID="123"
ISSUE_IID="1"

Write to /tmp/gitlab_request.json:

{
  "title": "Updated: Bug fix for login page",
  "labels": "bug,frontend,in-progress"
}

Then run:

bash -c 'curl -s -X PUT "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues/${ISSUE_IID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{iid, title, labels, updated_at}'

9. Close Issue

Close an issue:

PROJECT_ID="123"
ISSUE_IID="1"

Write to /tmp/gitlab_request.json:

{
  "state_event": "close"
}

Then run:

bash -c 'curl -s -X PUT "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues/${ISSUE_IID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{iid, title, state}'

Use "state_event": "reopen" to reopen a closed issue.


10. Add Comment to Issue

Add a note/comment to an issue:

PROJECT_ID="123"
ISSUE_IID="1"

Write to /tmp/gitlab_request.json:

{
  "body": "Investigating this issue. Will update soon."
}

Then run:

bash -c 'curl -s -X POST "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues/${ISSUE_IID}/notes" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{id, body, author: .author.username, created_at}'

11. List Merge Requests

Get merge requests for a project:

PROJECT_ID="123"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/merge_requests?state=opened&per_page=20" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '.[] | {iid, title, state, source_branch, target_branch, author: .author.username, web_url}

Filter options:

  • state=opened|closed|merged|all - Filter by state
  • scope=created_by_me|assigned_to_me|all - Filter by involvement
  • labels=review-needed - Filter by labels

12. Get Merge Request Details

Get a specific merge request:

PROJECT_ID="123"
MR_IID="1"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/merge_requests/${MR_IID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '{iid, title, state, source_branch, target_branch, author: .author.username, merge_status, has_conflicts, web_url}

13. Create Merge Request

Create a new merge request:

PROJECT_ID="123"

Write to /tmp/gitlab_request.json:

{
  "source_branch": "feature/user-profile",
  "target_branch": "main",
  "title": "Add user profile page",
  "description": "This MR adds a new user profile page with avatar support."
}

Then run:

bash -c 'curl -s -X POST "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/merge_requests" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{iid, title, web_url}'

14. Merge a Merge Request

Merge an MR (if it's ready):

PROJECT_ID="123"
MR_IID="1"

Write to /tmp/gitlab_request.json:

{
  "merge_when_pipeline_succeeds": true
}

Then run:

bash -c 'curl -s -X PUT "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/merge_requests/${MR_IID}/merge" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{iid, title, state, merged_by: .merged_by.username}'

Options:

  • merge_when_pipeline_succeeds=true - Auto-merge when pipeline passes
  • squash=true - Squash commits before merging
  • should_remove_source_branch=true - Delete source branch after merge

15. List Pipelines

Get pipelines for a project:

PROJECT_ID="123"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/pipelines?per_page=10" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '.[] | {id, status, ref, sha: .sha[0:8], created_at, web_url}

16. Get Pipeline Details

Get details of a specific pipeline:

PROJECT_ID="123"
PIPELINE_ID="456"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/pipelines/${PIPELINE_ID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '{id, status, ref, duration, finished_at, web_url}

17. List Pipeline Jobs

Get jobs in a pipeline:

PROJECT_ID="123"
PIPELINE_ID="456"

bash -c 'curl -s "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/pipelines/${PIPELINE_ID}/jobs" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}"' | jq '.[] | {id, name, stage, status, duration}

18. Search Users

Search for users:

bash -c 'curl -s -G "https://${GITLAB_HOST}/api/v4/users" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --data-urlencode "search=john"' | jq '.[] | {id, username, name, state}

19. Create Project

Create a new project:

Write to /tmp/gitlab_request.json:

{
  "name": "my-new-project",
  "visibility": "private",
  "initialize_with_readme": true
}

Then run:

bash -c 'curl -s -X POST "https://${GITLAB_HOST}/api/v4/projects" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --header "Content-Type: application/json" -d @/tmp/gitlab_request.json' | jq '{id, path_with_namespace, web_url}'

20. Delete Issue

Delete an issue (requires admin or owner permissions):

PROJECT_ID="123"
ISSUE_IID="1"

curl -s -X DELETE "https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/issues/${ISSUE_IID}" --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" -w "\nHTTP Status: %{http_code}"

Returns 204 No Content on success.


Project ID Encoding

When using project paths instead of numeric IDs, URL-encode the path:

  • mygroup/myprojectmygroup%2Fmyproject
  • mygroup/subgroup/myprojectmygroup%2Fsubgroup%2Fmyproject
# Using numeric ID
PROJECT_ID="123"

# Using encoded path
PROJECT_ID="mygroup%2Fmyproject"

Guidelines

  1. Use IID for issues/MRs: Within a project, use iid (internal ID like #1, #2) not the global id
  2. URL-encode project paths: If using paths instead of numeric IDs, encode slashes as %2F
  3. Handle pagination: Use per_page and page params for large result sets
  4. Check merge status: Before merging, verify merge_status is can_be_merged
  5. Rate limiting: Implement backoff if you receive 429 responses
  6. Self-hosted GitLab: Set GITLAB_HOST to your instance domain (without https://)