Connectors
/
GitLab
Live · 110 tools

GitLab Integration for AI Agents

The GitLab API is well-documented. Getting your agent to act on real users’ projects — across merge requests, pipelines, and groups, without token chaos — is the part that takes longer than it should.
GitLab
Live

Developer Tools

Code Collaboration

Status
Live
Tools
110 pre-built
Auth
OAuth 2.0
Credential storage
Sandbox support

OAuth 2.0

Auto token refresh

Revocation detection

BYOC supported

The real problem

Why this is harder than it looks

GitLab’s REST API is well-structured and the documentation is thorough. A proof-of-concept that calls a project endpoint works in an afternoon. The problems start when you try to run this for real users across a multi-tenant product — and GitLab has a few quirks that don’t surface until you hit them in production.

The first thing to understand is that GitLab supports both GitLab.com (SaaS) and self-managed instances. If any of your customers run self-managed GitLab, the base URL for every API call changes per user. Your token storage, request routing, and redirect URIs all need to account for this. It’s not a flag you flip — it changes how you register the OAuth app and how you construct every downstream request.

Then there’s the scope model. GitLab’s OAuth scopes are coarse: the api scope grants full read/write access to everything. If your agent only needs to read repositories, you should use read_api or read_repository instead — but the scope set is fixed at authorization time. Getting this wrong means users over-authorize your app, which enterprise security reviews will flag. Changing scopes later requires re-authorization of every connected user.

Beyond scopes: triggering CI/CD pipelines via the API on GitLab.com requires the authenticated user to have completed identity verification at gitlab.com/-/profile/verify — a requirement that is easy to miss in development but surfaces as a hard 403 in production. The merge request approval tools (gitlab_merge_request_approve, gitlab_merge_request_approvals_get) are restricted to GitLab Premium and above; on Free plans they return 403 Forbidden with no explanation in the error body. And IDs in GitLab are split into a global id and a per-project iid — passing the wrong one to any issue or MR tool returns a silent 404.

On top of all that: per-user token isolation in a multi-tenant system, proactive refresh before expiry, and revocation detection when users disconnect your OAuth app. Each is manageable alone. Together, they add up to weeks of infrastructure work that has nothing to do with what your agent is supposed to do. Scalekit handles all of it.

Capabilities

What your agent can do with GitLab

Once connected, your agent has 110 pre-built tools covering the full GitLab developer workflow:

  • Manage repositories end-to-end: read and write files, create and delete branches, inspect commit history and diffs, compare refs, and list repository trees
  • Create and triage issues: open issues with labels, assignees, milestones, and due dates; update state; add and manage comments with Markdown and quick actions
  • Automate merge requests: create, update, merge, and comment on MRs; retrieve diffs and commit lists; approve and check approval state (Premium)
  • Monitor and control CI/CD: list, trigger, cancel, retry, and delete pipelines; inspect individual jobs, retrieve logs, and download artifacts; manage project-level CI/CD variables
  • Administer groups and projects: create and update groups, manage group and project membership with numeric access levels, list namespaces, and fork or star projects
  • Handle releases and webhooks: create and update tagged releases; manage project webhooks, deploy keys, milestones, labels, and snippets
Setup context

What we’re building

This guide connects a developer assistant agent to GitLab — helping engineers manage issues, review open merge requests, trigger pipelines, and commit file changes across their projects.

🤖
Example agent
Developer assistant triaging issues, reviewing MRs, and managing pipelines on behalf of each engineer
🔐
Auth model
B2B SaaS — each engineer connects their own GitLab account. identifier = your user ID
⚙️
Scalekit account
app.scalekit.com — Client ID, Secret, Env URL
🐍
Runtime
Python 3.8+ · Node.js also available
Setup

1 Setup: One SDK, One credential

Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no GitLab secrets, no user tokens, nothing belonging to your users.

pip install scalekit-sdk-python
npm install @scalekit-sdk/node
import scalekit.client import os from dotenv import load_dotenv load_dotenv() scalekit = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENV_URL"), ) actions = scalekit.actions
import { ScalekitClient } from '@scalekit-sdk/node'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET ); const actions = scalekit.actions;
Already have credentials?
Connected Accounts

2 Per-User Auth: Creating connected accounts

Each engineer gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.

response = actions.get_or_create_connected_account( connection_name="gitlab", identifier="user_gl_456" # your internal user ID ) connected_account = response.connected_account print(f"Status: {connected_account.status}") # Status: PENDING — user hasn't authorized yet
const response = await actions.getOrCreateConnectedAccount({ connectionName: "gitlab", identifier: "user_gl_456" // your internal user ID }); const connectedAccount = response.connectedAccount; console.log(`Status: ${connectedAccount.status}`); // Status: PENDING — user hasn't authorized yet

This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.

Authorization Flow

3 The authorization flow

The engineer authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.

if connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name="gitlab", identifier="user_gl_456" ) # Redirect user → GitLab OAuth consent screen # Scalekit captures the token on callback return redirect(link.link)
if (connectedAccount.status !== "ACTIVE") { const { link } = await actions.getAuthorizationLink({ connectionName: "gitlab", identifier: "user_gl_456" }); // Redirect user → GitLab OAuth consent screen // Scalekit captures the token on callback return redirect(link); }
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access tokens refresh before expiry. If an engineer revokes access from their GitLab settings, the account moves to REVOKED — no silent failures. Check account.status before critical write operations.
Bring Your Own Credentials for production
By default, Scalekit uses its managed GitLab OAuth app for testing. For production deployments, register your own OAuth application in GitLab User Settings → Applications, paste the Scalekit redirect URI as the callback URL, configure your scopes, then enter your Client ID and Secret in the Scalekit dashboard. Token management stays fully handled.
Calling GitLab

4 Calling GitLab: What your agent writes

With the connected account active, your agent calls GitLab actions using execute_tool. Name the tool, pass parameters. Scalekit handles token retrieval, request construction, and response parsing.

List open issues with filters

Fetch open issues filtered by label, assignee, or date. The updated_before parameter accepts ISO 8601 — useful for stale issue triage.

result = actions.execute_tool( identifier="user_gl_456", tool_name="gitlab_issues_list", tool_input={ "project_id": "my-group/my-repo", "state": "opened", "labels": "bug,p1", "order_by": "updated_at", "sort": "asc", "per_page": 50 } ) # Returns list of issue objects with iid, title, labels, assignees, state
const result = await actions.executeTool({ identifier: "user_gl_456", toolName: "gitlab_issues_list", toolInput: { "project_id": "my-group/my-repo", "state": "opened", "labels": "bug,p1", "order_by": "updated_at", "sort": "asc", "per_page": 50 } }); // Returns list of issue objects with iid, title, labels, assignees, state

Create a merge request

Open a merge request from a feature branch. Use remove_source_branch and squash to control post-merge behavior. The description field supports GitLab quick actions like /assign and /label.

result = actions.execute_tool( identifier="user_gl_456", tool_name="gitlab_merge_request_create", tool_input={ "project_id": "my-group/my-repo", "source_branch": "feature/add-oauth", "target_branch": "main", "title": "feat: add OAuth 2.0 authentication", "description": "Adds GitLab OAuth integration.\n\n/assign @reviewer", "remove_source_branch": True, "squash": False } ) # Returns: { "iid": 42, "web_url": "https://gitlab.com/...", ... }
const result = await actions.executeTool({ identifier: "user_gl_456", toolName: "gitlab_merge_request_create", toolInput: { "project_id": "my-group/my-repo", "source_branch": "feature/add-oauth", "target_branch": "main", "title": "feat: add OAuth 2.0 authentication", "description": "Adds GitLab OAuth integration.\n\n/assign @reviewer", "remove_source_branch": true, "squash": false } }); // Returns: { "iid": 42, "web_url": "https://gitlab.com/...", ... }

Read and update a file in a branch

Get a file’s content, modify it, and write it back. Unlike GitHub, GitLab accepts plain string content — no Base64 encoding required. Passing last_commit_id enables conflict detection.

# 1. Read the current file current = actions.execute_tool( identifier="user_gl_456", tool_name="gitlab_file_get", tool_input={ "project_id": "my-group/my-repo", "file_path": "docs/CHANGELOG.md", "ref": "main" } ) # 2. Write an updated version back result = actions.execute_tool( identifier="user_gl_456", tool_name="gitlab_file_update", tool_input={ "project_id": "my-group/my-repo", "file_path": "docs/CHANGELOG.md", "branch": "main", "content": "## v2.5.0\n- Added OAuth\n\n" + current["content"], "commit_message": "docs: add v2.5.0 changelog entry", "last_commit_id": current["last_commit_id"] # conflict detection } )
// 1. Read the current file const current = await actions.executeTool({ identifier: "user_gl_456", toolName: "gitlab_file_get", toolInput: { "project_id": "my-group/my-repo", "file_path": "docs/CHANGELOG.md", "ref": "main" } }); // 2. Write an updated version back const result = await actions.executeTool({ identifier: "user_gl_456", toolName: "gitlab_file_update", toolInput: { "project_id": "my-group/my-repo", "file_path": "docs/CHANGELOG.md", "branch": "main", "content": "## v2.5.0\n- Added OAuth\n\n" + current.content, "commit_message": "docs: add v2.5.0 changelog entry", "last_commit_id": current.last_commit_id } });

Trigger a pipeline and inspect jobs

Trigger a CI/CD pipeline on a branch and then list its jobs to check per-stage status. Note that triggering pipelines on GitLab.com requires the user to have completed identity verification.

pipeline = actions.execute_tool( identifier="user_gl_456", tool_name="gitlab_pipeline_create", tool_input={ "project_id": "my-group/my-repo", "ref": "main" } ) jobs = actions.execute_tool( identifier="user_gl_456", tool_name="gitlab_pipeline_jobs_list", tool_input={ "project_id": "my-group/my-repo", "pipeline_id": pipeline["data"]["id"] } ) for job in jobs["data"]: print(f"[{job['status']}] {job['name']} — stage: {job['stage']}")
const pipeline = await actions.executeTool({ identifier: "user_gl_456", toolName: "gitlab_pipeline_create", toolInput: { "project_id": "my-group/my-repo", "ref": "main" } }); const jobs = await actions.executeTool({ identifier: "user_gl_456", toolName: "gitlab_pipeline_jobs_list", toolInput: { "project_id": "my-group/my-repo", "pipeline_id": pipeline.data.id } }); for (const job of jobs.data) { console.log(`[${job.status}] ${job.name} — stage: ${job.stage}`); }
Framework wiring

5 Wiring into your agent framework

Scalekit integrates directly with LangChain. The agent decides what to call; Scalekit handles auth on every invocation. No token plumbing in your agent logic.

from langchain_anthropic import ChatAnthropic from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from scalekit.langchain import get_tools gl_tools = get_tools( connection_name="gitlab", identifier="user_gl_456" ) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a developer assistant. Use the available tools to help manage GitLab projects, issues, merge requests, and pipelines."), MessagesPlaceholder("chat_history", optional=True), ("human", "{input}"), MessagesPlaceholder("agent_scratchpad"), ]) agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), gl_tools, prompt) result = AgentExecutor(agent=agent, tools=gl_tools).invoke({ "input": "Show me all open P1 bugs in the backend-api project and create a summary issue linking them" })
import { ChatAnthropic } from "@langchain/anthropic"; import { AgentExecutor, createToolCallingAgent } from "langchain/agents"; import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; import { getTools } from "@scalekit-sdk/langchain"; const glTools = getTools({ connectionName: "gitlab", identifier: "user_gl_456" }); const prompt = ChatPromptTemplate.fromMessages([ ["system", "You are a developer assistant. Use the available tools to help manage GitLab projects, issues, merge requests, and pipelines."], new MessagesPlaceholder("chat_history", true), ["human", "{input}"], new MessagesPlaceholder("agent_scratchpad"), ]); const agent = await createToolCallingAgent({ llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }), tools: glTools, prompt }); const result = await AgentExecutor.fromAgentAndTools({ agent, tools: glTools }).invoke({ input: "Show me all open P1 bugs in the backend-api project and create a summary issue linking them" });
Other frameworks supported
Tool reference

All 110 GitLab tools

Grouped by capability. Your agent calls tools by name — no API wrappers to write.

Projects
gitlab_projects_list
List all projects accessible to the authenticated user, with filters for ownership, membership, visibility, and search
gitlab_project_get
Get a specific project by numeric ID or URL-encoded namespace/path
gitlab_project_create
Create a new project under the user's namespace or a specified group, with visibility and default branch options
gitlab_project_update
Update project settings including name, description, visibility, merge method, and pipeline requirements
gitlab_project_delete
Permanently delete a project (async operation). Requires Owner role
gitlab_project_fork
Fork a project into a specified namespace
gitlab_project_star
Star a project
gitlab_project_unstar
Unstar a project
gitlab_project_search
Search within a project across issues, MRs, commits, code blobs, wiki, and more
gitlab_project_forks_list
List all forks of a project
gitlab_namespaces_list
List all namespaces accessible to the current user — useful for resolving where to create or fork a project
gitlab_global_search
Search globally across GitLab for projects, issues, MRs, commits, blobs, and more
Repository — Branches
gitlab_branches_list
List all branches with optional name filter and pagination
gitlab_branch_get
Get details of a specific branch including the latest commit
gitlab_branch_create
Create a new branch from a branch, tag, or commit SHA
gitlab_branch_delete
Delete a branch. Protected branches cannot be deleted without first unprotecting
Repository — Tags
gitlab_tags_list
List all tags with name filter, sort, and pagination
gitlab_tag_get
Get details of a specific tag including its commit and release notes
gitlab_tag_create
Create a lightweight or annotated tag on a branch, tag, or commit SHA
gitlab_tag_delete
Delete a tag. Protected tags cannot be deleted
Repository — Commits
gitlab_commits_list
List commits filtered by branch, file path, author, or date range
gitlab_commit_get
Get detailed commit info by SHA including stats and diff summary
gitlab_commit_diff_get
Get the full diff of a commit with all changed file hunks (paginated for large commits)
gitlab_commit_comment_create
Add an inline or general comment to a specific commit
gitlab_commit_comments_list
List all comments on a specific commit
gitlab_compare_refs
Compare two refs (branches, tags, or SHAs) and return commits and diff between them
Repository — Files & Trees
gitlab_file_get
Get a file’s raw content and metadata at a specific branch, tag, or commit
gitlab_file_create
Create a new file with a commit message. Content passed as plain string — no Base64 encoding needed
gitlab_file_update
Update an existing file. Pass last_commit_id for conflict detection
gitlab_file_delete
Delete a file with a commit message
gitlab_repository_tree_list
List files and directories at a given path and ref. Supports recursive listing
Issues
gitlab_issues_list
List issues with filters for state, labels, milestone, assignee, author, search, and date ranges
gitlab_issue_get
Get a specific issue by its project-level IID (the number shown in the UI)
gitlab_issue_create
Create an issue with title, description, labels, assignees, milestone, due date, and confidentiality
gitlab_issue_update
Update any issue field — state, labels, assignees, milestone, due date. Only supplied fields are changed
gitlab_issue_delete
Permanently delete an issue. Requires Owner role. Cannot be undone
Issue Notes (Comments)
gitlab_issue_notes_list
List all comments on an issue with sort and pagination
gitlab_issue_note_create
Add a comment to an issue. Supports Markdown and quick actions (e.g., /close, /assign @user)
gitlab_issue_note_update
Update the content of an existing issue comment
gitlab_issue_note_delete
Delete a comment from an issue
Merge Requests
gitlab_merge_requests_list
List MRs with filters for state, labels, assignee, reviewer, source/target branch, and date
gitlab_merge_request_get
Get a specific MR by its project-level IID
gitlab_merge_request_create
Create an MR with source/target branch, title, assignees, reviewers, labels, squash, and draft options
gitlab_merge_request_update
Update any MR field — title, state, assignees, labels, target branch, draft status
gitlab_merge_request_merge
Merge an open MR. Supports SHA guard, squash override, and custom merge commit message
gitlab_merge_request_approve
Approve an MR. GitLab Premium+ only — returns 403 on Free plans
gitlab_merge_request_approvals_get
Get approval state of an MR — who has approved and who is required to. GitLab Premium+ only
gitlab_merge_request_diff_get
Get the full diff of an MR with all changed file hunks (paginated)
gitlab_merge_request_commits_list
List all commits included in an MR
Merge Request Notes (Comments)
gitlab_merge_request_notes_list
List all comments on an MR with sort and pagination
gitlab_merge_request_note_create
Add a comment to an MR. Supports Markdown and quick actions
Pipelines & CI/CD
gitlab_pipelines_list
List pipelines filtered by status, branch, commit SHA, or date range
gitlab_pipeline_get
Get detailed pipeline info including status, duration, and triggering user
gitlab_pipeline_create
Trigger a new pipeline on a branch, tag, or SHA. Supports passing pipeline variables. Requires identity verification on GitLab.com
gitlab_pipeline_cancel
Cancel a running or pending pipeline
gitlab_pipeline_retry
Retry all failed jobs in a pipeline
gitlab_pipeline_delete
Delete a pipeline and its job traces and artifacts. Permanent
gitlab_pipeline_jobs_list
List all jobs in a pipeline with status, stage, name, and duration
Jobs
gitlab_jobs_list
List all jobs across all pipelines in a project, with status filter
gitlab_job_get
Get detailed info about a specific job
gitlab_job_cancel
Cancel a pending or running job
gitlab_job_retry
Retry a specific job with the same configuration
gitlab_job_log_get
Get the full trace/log output of a job as raw text
gitlab_job_artifacts_download
Download a job’s artifact archive as a binary ZIP
CI/CD Variables
gitlab_project_variables_list
List all CI/CD variables for a project. Masked variable values are hidden
gitlab_project_variable_get
Get a specific CI/CD variable by key
gitlab_project_variable_create
Create a CI/CD variable with masking, protection, and environment scope options
gitlab_project_variable_update
Update an existing CI/CD variable’s value, type, or scope
gitlab_project_variable_delete
Delete a CI/CD variable by key
Groups & Members
gitlab_groups_list
List all groups accessible to the user with search and ownership filters
gitlab_group_get
Get a group by numeric ID or URL-encoded path
gitlab_group_create
Create a group or subgroup with name, path, visibility, and optional parent
gitlab_group_update
Update group name, path, description, or visibility
gitlab_group_delete
Delete a group and all its projects. Requires Owner role. Cannot be undone
gitlab_group_projects_list
List all projects belonging to a group
gitlab_group_members_list
List group members with their access level and expiry
gitlab_group_member_add
Add a user to a group with a specified access level (10–50) and optional expiry
gitlab_group_member_remove
Remove a user from a group
gitlab_project_members_list
List members of a project with their access level
gitlab_project_member_add
Add a user to a project with a specified access level and optional expiry
gitlab_project_member_remove
Remove a user from a project
Users & SSH Keys
gitlab_current_user_get
Get the authenticated user’s profile — useful for resolving user ID and namespace before other calls
gitlab_user_get
Get a specific user’s public profile by numeric ID
gitlab_users_list
List users by search, username, or active status. Listing all users requires admin access
gitlab_user_projects_list
List all projects owned by a specific user
gitlab_current_user_ssh_keys_list
List all SSH keys for the authenticated user
gitlab_ssh_key_add
Add a new SSH public key to the authenticated user’s account with optional expiry
Milestones & Labels
gitlab_milestones_list
List project milestones filtered by state and search
gitlab_milestone_get
Get a specific milestone by its numeric ID
gitlab_milestone_create
Create a milestone with title, description, start date, and due date
gitlab_milestone_update
Update a milestone’s title, dates, or state (close or reactivate)
gitlab_milestone_delete
Delete a milestone. Linked issues and MRs lose their milestone association
gitlab_issue_labels_list
List all labels defined in a project
gitlab_label_create
Create a new label with a name, hex color, and optional description
Releases, Webhooks, Deploy Keys & Snippets
gitlab_releases_list
List project releases with pagination
gitlab_release_get
Get a specific release by tag name
gitlab_release_create
Create a release tied to a tag with name, description, and asset links
gitlab_release_update
Update release name or description
gitlab_release_delete
Delete a release (tag is preserved)
gitlab_project_webhooks_list
List all webhooks configured for a project
gitlab_project_webhook_get / create / update / delete
Full CRUD for project webhooks including event triggers and SSL verification
gitlab_deploy_keys_list / create / delete
Manage read-only or read-write SSH deploy keys for a project
gitlab_project_snippets_list / get / create
List, retrieve, and create project-level code snippets
Connector notes

GitLab-specific behavior

IID vs ID — use the wrong one and you get a silent 404
GitLab issues and merge requests each have a global id and a per-project iid. The iid is the number users see in the UI (e.g., #42). All tools that accept issue_iid or merge_request_iid expect the iid. Passing a global id returns a 404 with no explanation. When in doubt, call gitlab_issues_list or gitlab_merge_requests_list first to confirm you have the right iid.
Pipeline triggering requires identity verification on GitLab.com
On GitLab.com, calling gitlab_pipeline_create via API requires the authenticated user to have completed identity verification at gitlab.com/-/profile/verify. Without it the API returns 403 Forbidden. This check does not apply to self-managed instances. Surface a clear prompt to affected users rather than returning a generic error.
Approval tools require GitLab Premium
gitlab_merge_request_approve and gitlab_merge_request_approvals_get return 403 Forbidden on GitLab Free plans. If your agent targets mixed-tier customers, check for this error and handle it gracefully rather than treating it as an auth failure.
Self-managed GitLab instances use a different base URL
GitLab.com tools use https://gitlab.com as the base URL automatically. For self-managed instances, set your instance hostname as the base URL in your Scalekit connection configuration. The OAuth app must also be registered on the self-managed instance, not on GitLab.com.
Infrastructure decision

Why not build this yourself

The GitLab OAuth flow is documented. Token storage isn’t technically hard. But here’s what you’re actually signing up for:

PROBLEM 01
Scope selection is irreversible at authorization time — over-scope and enterprise security reviews block your app; under-scope and tools fail at runtime with no clear error
PROBLEM 02
Self-managed GitLab instances change the base URL per user — token storage, request routing, and OAuth app registration all need per-instance handling
PROBLEM 03
Proactive token refresh before expiry — not reactive on a 401 mid-pipeline or mid-MR operation, where a failed call leaves your workflow in a partial state
PROBLEM 04
Per-user token isolation across a multi-tenant system — one engineer’s GitLab credentials must never be accessible to another, even across the same group or organization

That’s one connector. Your agent product will eventually need GitHub, Jira, Slack, Notion, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.

Scalekit maintains every connector. You maintain none of them.

Ready to ship

Ship GitLab agents faster

Free to start. OAuth and token lifecycle fully handled.
GitLab
Live

Developer Tools

Code Collaboration

Status
Live
Tools
110 pre-built
Auth
OAuth 2.0
Credential storage
Sandbox support