> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prisme.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# GitLab

> Let Agent Factory agents manage GitLab projects, issues, merge requests and CI/CD pipelines through MCP, with per-user OAuth resolved server-side

<img src="https://mintcdn.com/prismeai/Oqq72tWGTtxufvRl/images/connectors/gitlab.svg?fit=max&auto=format&n=Oqq72tWGTtxufvRl&q=85&s=c75ecc40ef2aaf95dddcf33e96e3bcc5" alt="GitLab" width="96" height="96" noZoom style={{ float: "left", marginRight: "1.25rem", marginBottom: "0.5rem" }} data-path="images/connectors/gitlab.svg" />

The GitLab connector exposes the [GitLab](https://gitlab.com) DevOps platform (REST v4 API) to **Agent Factory** agents (through MCP) and to Builder automations (through the `GitLab.op:` App instructions) — covering projects, issues, merge requests, pipelines, jobs, environments, branches, commits, tags, releases, labels, milestones, wikis, CI/CD variables, deploy keys, webhooks, groups and users. The 90+ GitLab operations are grouped into **16 entity tools**, each driven by an `action` argument. The MCP server runs in the **tenant app-instance context**: it resolves the installing workspace's own credentials and authorizes the calling agent against that workspace's allowlist. Authentication is per-user and supports several modes:

* **Per-user OAuth2 — central client** (`oauthCentral`, recommended) — one GitLab OAuth application is registered once by the platform maintainer; every end user signs in with their own GitLab account. Nothing to register per workspace: each one just installs the app and clicks *Connect*.
* **Per-user OAuth2 — tenant client** (`oauth`) — paste your own GitLab OAuth client ID/secret in the connector config app. Each user signs in with their own account against your client (authorization-code flow).
* **Direct access token** (`accessToken`) — a caller-managed GitLab token (e.g. a Personal Access Token with the `api` scope), used as-is with no exchange.

<CardGroup cols={3}>
  <Card title="Project Management" icon="diagram-project">
    Projects, issues, merge requests, labels, milestones and wikis
  </Card>

  <Card title="CI/CD & DevOps" icon="rocket">
    Pipelines, jobs, environments, CI variables, deploy keys and webhooks
  </Card>

  <Card title="Per-user auth" icon="key">
    Per-user OAuth (central or tenant client) resolved server-side, or a direct access token
  </Card>
</CardGroup>

## Who is this for?

This connector is used by three different roles. Jump to the section that matches yours — each one is self-contained.

<CardGroup cols={3}>
  <Card title="Agent builder" icon="robot">
    You build agents in **Agent Factory** and want them to use GitLab. → *Agent builder* tab.
  </Card>

  <Card title="Platform admin" icon="shield-halved">
    You run the platform and set up the shared GitLab OAuth client once for everyone. → *Platform admin setup* accordion below.
  </Card>

  <Card title="Workspace builder" icon="puzzle-piece">
    You write Builder automations (DSUL) that call GitLab operations directly. → *Workspace builder* tab.
  </Card>
</CardGroup>

## Prerequisites

* A **GitLab** account (gitlab.com or a self-hosted instance).
* For the **OAuth modes** — a GitLab OAuth application created at **User Settings > Applications** (`https://gitlab.com/-/user_settings/applications`) with the `api` scope (or `read_api` for read-only agents); copy its Application ID and Secret. The platform maintainer registers one app for the central mode; a workspace can also register its own for the tenant mode.
* For the **access-token mode** — a Personal Access Token created at **User Settings > Access Tokens** (`https://gitlab.com/-/user_settings/personal_access_tokens`) with the `api` scope.
* Base URL: `https://gitlab.com/api/v4`.

<Accordion title="Platform admin (Governance) — one-time platform setup" icon="shield-halved">
  **Goal:** two one-time tasks — (1) configure the shared **central OAuth client** so every workspace lets its users sign in with their own GitLab account, and (2) optionally expose GitLab as a reusable **capability** in AI Governance so agent builders can pick it without pasting endpoint URLs. Throughout, `<api-url>` is your environment's API URL (`https://api.studio.prisme.ai/v2` on production).

  ## 1. Configure the connector

  <Steps>
    <Step title="Register the OAuth application at GitLab">
      Open `https://gitlab.com/-/user_settings/applications` and click **Add new application**. Set the single authorized redirect URI (callback URL) to the **core workspace** callback:

      ```text theme={null}
      <api-url>/workspaces/slug:gitlab/webhooks/oauthCallback
      ```

      (e.g. `https://api.studio.prisme.ai/v2/workspaces/slug:gitlab/webhooks/oauthCallback` on production). Add the `api` scope (or `read_api` for read-only). Save and copy the **Application ID** and **Secret** (the secret is shown only once).
    </Step>

    <Step title="Enter the credentials through the configuration app">
      Open the central `gitlab` workspace and launch its **Configuration app** — `<studio>/apps/gitlab` (e.g. `https://studio.prisme.ai/apps/gitlab`). Loaded from the core workspace, the app shows the **maintainer view** (org owner / editor / admin only); paste the **Application ID** and **Secret** there and Save — the app stores them in the core workspace's secrets for you. Do not edit Studio's raw Secrets by hand. These credentials stay in the `gitlab` workspace and are never exposed to tenants or end users; token exchange is proxied through the core `centralTokenExchange` webhook so the client secret never leaves the core workspace.
    </Step>

    <Step title="Tell workspaces to use the central client">
      Each consuming workspace selects auth mode **`oauthCentral`** in the connector configuration app (no client id/secret to enter on their side). Their users then just click **Connect**.
    </Step>

    <Step title="(Optional) Publish to the Capabilities catalog">
      From that same maintainer view, the **Add to catalog** button publishes GitLab to the org-wide **Capabilities catalog** in one click (org owner / admin only; it stays disabled until the central client is saved). Once published, every agent builder in the org can enable GitLab from the catalog without pasting an endpoint — see *Option A* in the *Agent builder* tab.
    </Step>
  </Steps>

  ## 2. Declare the capability in AI Governance

  As an alternative to (or in addition to) the catalog button, you can declare GitLab as a named **capability** in AI Governance. Agent builders then enable that capability on their agents instead of pasting a raw MCP endpoint.

  <Steps>
    <Step title="Open AI Governance > Capabilities">
      Create (or edit) the **GitLab** capability.
    </Step>

    <Step title="Point it at the MCP endpoint">
      Set the capability's MCP server URL to the connector's **MCP Endpoint**, and set its **Scope** to:

      ```text theme={null}
      context_id,agent_id,user_id
      ```

      The `agent_id` in the scope is what lets the connector identify and authorize the calling agent.
    </Step>

    <Step title="Make it available to agent builders">
      Once created, the capability appears in the capability picker for agent builders in your organization, who enable it on their agents. Access to the catalog follows your organization's existing roles; there is no per-capability role grant.
    </Step>

    <Step title="Smoke-test">
      From an agent that has the capability, in a workspace configured for `oauthCentral`, trigger any tool (e.g. `users` with `action: "getCurrent"`). The user is prompted to connect once (GitLab sign-in); subsequent calls reuse the stored token transparently and refresh it automatically.
    </Step>
  </Steps>

  <Warning>
    Declaring the capability makes the connector **available**; it does not by itself authorize a specific agent. This connector follows the **tenant-context model** — which agents may actually call it is gated per-workspace by the **authorized-agents allowlist** in the configuration app (see the *Workspace builder* tab). There is also **no OAuth auth-config JSON** to attach in Governance: connect / status / disconnect are handled by the connector's own webhooks, wired automatically.
  </Warning>
</Accordion>

***

<Tabs>
  <Tab title="Agent builder (Agent Factory)">
    ## Agent builder

    **Goal:** let an agent you build in Agent Factory use GitLab through MCP tools.

    <Note>
      Before an agent can call the connector, a *Workspace builder* must have installed and configured the GitLab app in a workspace (see the *Workspace builder* tab), and — for the central OAuth mode — a *Platform admin* must have provisioned the shared OAuth client (see the *Platform admin setup* accordion above).
    </Note>

    This connector runs in the **tenant app-instance context**: your agent is authorized two ways at once — it is identified by the `agent_id` that Agent Factory injects through the capability *Scope*, and that agent must appear in the connector's **authorized-agents allowlist** (managed in the configuration app). The GitLab access token itself is resolved server-side from the configured auth mode.

    There are two ways to wire it up. Pick based on how much isolation you need.

    ## Option A — Enable the shared capability from the catalog

    The fastest path: a *Platform admin* has already published a **GitLab** capability to the Capabilities catalog (see the *Platform admin setup* accordion above — the **Add to catalog** button, or §2), so you just pick it from the catalog.

    <Steps>
      <Step title="Open your agent in Agent Factory">
        Open the agent you want to extend and go to its capabilities / tools.
      </Step>

      <Step title="Add the GitLab capability">
        Browse the capability catalog, select **GitLab**, and enable it. The MCP endpoint URL and the *Scope* (`context_id,agent_id,user_id`) are already wired by the admin — nothing to paste, and the shared instance accepts every agent, so there is no allowlist step on your side.
      </Step>

      <Step title="Connect a GitLab account">
        On the first tool call, an unconnected user is prompted to sign in — Agent Factory surfaces a `connect_url` to GitLab's authorization page. After sign-in the per-user token is stored and reused on subsequent calls; refresh is automatic.
      </Step>
    </Steps>

    <Note>
      Convenient, but your agent runs against a **shared, platform-managed instance**: its GitLab credentials are owned by someone else and the instance accepts every agent that is granted the capability. Prefer Option B for anything beyond quick experiments.
    </Note>

    ## Option B — Run it from your own workspace (recommended)

    For production agents, install the connector in **your own workspace** and point the agent at *that* workspace's MCP endpoint.

    <Warning>
      **Prefer this mode for security.** Because the MCP runs in *your* app-instance context, the GitLab credentials, the per-user OAuth tokens and the authorized-agents allowlist are all **scoped to your workspace** — not shared platform-wide. You decide exactly which agents may call it and which GitLab account / auth mode backs them, and a misconfiguration elsewhere can never expose your data. The shared catalog capability (Option A) is a broad surface many agents can reach; your own workspace is an isolated, least-privilege boundary.
    </Warning>

    <Steps>
      <Step title="Install and configure the connector in your workspace">
        Follow the *Workspace builder* tab: install **GitLab** in your workspace, open its **Configuration app**, choose the auth mode and connect a GitLab account.
      </Step>

      <Step title="Allowlist your agent">
        In that workspace's config app, open **Authorized agents** and tick your agent (the **Install capability** button does this for you), or enable **Allow all agents**.
      </Step>

      <Step title="Add the MCP capability to your agent">
        In your agent, add a capability pointing at **your workspace's MCP Endpoint** URL, and set its **Scope** to:

        ```text theme={null}
        context_id,agent_id,user_id
        ```

        The `agent_id` is what lets the connector identify and authorize your agent — without it, every call is rejected with an explicit "agent could not be identified" message. This *Scope* is separate from the GitLab OAuth scopes.
      </Step>

      <Step title="Connect a GitLab account">
        On the first tool call, the user is prompted to sign in (or uses **Connect** in the config app). The per-user token is stored and reused; refresh is automatic.
      </Step>
    </Steps>

    ## Brief the agent in its system prompt

    Whichever option you pick, wiring the capability is not enough — the agent must know the connector exists and when to reach for it. Add a short paragraph to the agent's system prompt. Copy-pasteable starter:

    ```text theme={null}
    You have access to the GitLab MCP server. Use it whenever the user asks about GitLab content — projects, issues, merge requests, pipelines, jobs, branches, commits, tags, releases, milestones or members. Examples: "List my open merge requests", "What's failing in pipeline #1234?", "Create an issue in project X with label bug", "Show the latest release of my main project". Each tool is an entity that takes an `action` argument. Prefer calling tools over guessing, and confirm with the user before any destructive action (close an issue, merge or revert an MR, delete a branch, cancel a pipeline).
    ```

    Refine the trigger keywords (project names, group names, label conventions) so the agent reliably picks up the right intent in your context.

    <Note>
      **Legacy AI Knowledge agents** (no native MCP picker): add the connector under **Advanced > Tools > MCP** and paste the **MCP Endpoint** URL. The agent still has to be allowlisted in the config app and its identity propagated so the connector can read its `agent_id`.
    </Note>

    <Note>
      **Restricting to read-only (least privilege).** GitLab tools cover both reads and writes (create/update issues, merge requests, etc.). The connector requests the GitLab `api` scope (full read + write) by default. To allow only read access, set the **Scopes** field in the configuration app to `read_api`. With central OAuth (`oauthCentral`) you do **not** create your own GitLab application — keep `oauthCentral` and enter `read_api`; your tenant scope overrides the platform default (the central application must allow `read_api`). Write calls are then rejected by GitLab with `403 Forbidden`. The scope is set at the workspace level (a workspace editor can widen it); a GitLab personal access token limited to `read_api` is the hard-guarantee alternative.
    </Note>

    ## Available Tools

    Tools are grouped by resource. Each MCP tool takes an `action` argument selecting the operation, plus the parameters for that action. Every tool also accepts an `outputFormat` parameter (see *Output Formats*).

    ### Projects

    | Tool       | Description                                                                                                                                                                                                                                                                                                                                                                                     |
    | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `projects` | Projects (repositories) and their settings, members, webhooks and CI/CD variables. Actions: `list` (pass `membership:true` or `owned:true` for *my* projects), `get`, `create`, `update`, `delete`, `fork`, `archive`, `unarchive`, `listMembers`, `addMember`, `removeMember`, `listHooks`, `createHook`, `deleteHook`, `listVariables`, `createVariable`, `updateVariable`, `deleteVariable`. |

    ### Issues

    | Tool     | Description                                                                                                                                                                                                                                                                                                 |
    | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `issues` | Issues and their notes. Actions: `list` (one project), `listAll` (across all accessible projects — use for "my issues" with `scope:assigned_to_me`/`created_by_me`), `listGroup`, `get`, `create`, `update`, `close`, `reopen`, `delete`, `listNotes`, `getNote`, `createNote`, `updateNote`, `deleteNote`. |

    ### Merge Requests

    | Tool            | Description                                                                                                                                                                                                                                                                     |
    | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `mergeRequests` | Merge requests and their notes. Actions: `list`, `listAll` (use for "my MRs" / reviews via `scope` + `reviewer_username`), `listGroup`, `get`, `create`, `update`, `delete`, `approve`, `unapprove`, `merge`, `listNotes`, `getNote`, `createNote`, `updateNote`, `deleteNote`. |

    ### Repository

    | Tool       | Description                                                                                       |
    | ---------- | ------------------------------------------------------------------------------------------------- |
    | `branches` | Repository branches. Actions: `list`, `get`, `create`, `delete`.                                  |
    | `commits`  | Repository commits. Actions: `list`, `get`, `create` (multi-file action), `cherryPick`, `revert`. |
    | `tags`     | Repository tags. Actions: `list`, `get`, `create`.                                                |
    | `releases` | Project releases. Actions: `list`, `get`, `create`, `delete`.                                     |

    ### CI/CD

    | Tool           | Description                                                                           |
    | -------------- | ------------------------------------------------------------------------------------- |
    | `pipelines`    | CI/CD pipelines. Actions: `list`, `get`, `create` (trigger a run), `retry`, `cancel`. |
    | `pipelineJobs` | CI/CD jobs within a pipeline. Actions: `list`, `get`, `retry`.                        |
    | `environments` | CI/CD environments. Actions: `list`, `get`, `stop`.                                   |
    | `deployKeys`   | Project deploy keys. Actions: `list`, `create`, `delete`.                             |

    ### Planning

    | Tool         | Description                                                     |
    | ------------ | --------------------------------------------------------------- |
    | `labels`     | Project labels. Actions: `list`, `create`, `update`, `delete`.  |
    | `milestones` | Project milestones. Actions: `list`, `get`, `create`, `update`. |
    | `wikiPages`  | Project wiki pages. Actions: `list`, `get`, `create`.           |

    ### Organization

    | Tool     | Description                                                                                          |
    | -------- | ---------------------------------------------------------------------------------------------------- |
    | `groups` | Groups (namespaces). Actions: `list` (pass `owned:true` for *my* groups), `get`, `create`, `update`. |
    | `users`  | Users. Actions: `list`, `get`, `search`, `getCurrent`.                                               |

    ## Output Formats

    Every tool accepts an `outputFormat` parameter that controls the MCP response shape:

    * **`verbose`** (default) — human-readable text for LLM consumption
    * **`structured`** — machine-readable JSON in `structuredContent`
    * **`both`** — both text and structured content

    ## Tool Details

    ### issues (create)

    Open an issue in a GitLab project.

    ```json theme={null}
    {
      "name": "issues",
      "arguments": {
        "action": "create",
        "id": "123456",
        "title": "Investigate 500s on /api/v1/search",
        "description": "Spike of 500s since 14:00 UTC.",
        "labels": "BUG,Incident",
        "assignee_ids": ["987654"]
      }
    }
    ```

    | Parameter      | Required | Description                                             |
    | -------------- | -------- | ------------------------------------------------------- |
    | `action`       | Yes      | `create`                                                |
    | `id`           | Yes      | Project ID (numeric) or URL-encoded `namespace/project` |
    | `title`        | Yes      | Issue title                                             |
    | `description`  | No       | Markdown body                                           |
    | `labels`       | No       | Comma-separated label names                             |
    | `assignee_ids` | No       | Array of user IDs                                       |
    | `milestone_id` | No       | Milestone ID to associate                               |
    | `confidential` | No       | `true` to make the issue confidential                   |
    | `due_date`     | No       | `YYYY-MM-DD`                                            |

    ### mergeRequests (create)

    ```json theme={null}
    {
      "name": "mergeRequests",
      "arguments": {
        "action": "create",
        "id": "123456",
        "source_branch": "feature/auto-deploy",
        "target_branch": "main",
        "title": "Automate staging deploy",
        "description": "Closes #42",
        "remove_source_branch": true
      }
    }
    ```

    `source_branch`, `target_branch` and `title` are required. Accepts `assignee_ids`, `reviewer_ids`, `labels`, `milestone_id`, `squash`.

    ### pipelines (create)

    Trigger a new pipeline on a given ref. Requires a `.gitlab-ci.yml` at the ref.

    ```json theme={null}
    {
      "name": "pipelines",
      "arguments": {
        "action": "create",
        "id": "123456",
        "ref": "main",
        "variables": [
          { "key": "DEPLOY_ENV", "value": "staging" }
        ]
      }
    }
    ```

    | Parameter   | Required | Description                                       |
    | ----------- | -------- | ------------------------------------------------- |
    | `action`    | Yes      | `create`                                          |
    | `id`        | Yes      | Project ID                                        |
    | `ref`       | Yes      | Branch or tag to run against                      |
    | `variables` | No       | Array of `{key, value, variable_type?}` overrides |

    ### projects (list)

    List accessible projects. By default returns ALL accessible projects including public ones. Pass `membership: true` or `owned: true` to limit to the user's own projects.

    ```json theme={null}
    {
      "name": "projects",
      "arguments": {
        "action": "list",
        "membership": true,
        "order_by": "last_activity_at",
        "sort": "desc",
        "per_page": 20
      }
    }
    ```

    | Parameter    | Required | Description                                    |
    | ------------ | -------- | ---------------------------------------------- |
    | `action`     | Yes      | `list`                                         |
    | `membership` | No       | `true` → only projects the user is a member of |
    | `owned`      | No       | `true` → only projects the user owns           |
    | `search`     | No       | Free-text filter on project name/path          |
    | `visibility` | No       | `public` / `internal` / `private`              |
    | `order_by`   | No       | e.g. `last_activity_at`, `created_at`, `name`  |
    | `sort`       | No       | `asc` / `desc`                                 |
    | `per_page`   | No       | 1-100 (default 20)                             |

    ### mergeRequests (merge)

    Merge an open merge request. The MR must be in a mergeable state.

    ```json theme={null}
    {
      "name": "mergeRequests",
      "arguments": {
        "action": "merge",
        "id": "123456",
        "iid": "1",
        "squash": true,
        "should_remove_source_branch": true
      }
    }
    ```

    Accepts `merge_commit_message`, `squash_commit_message`, `merge_when_pipeline_succeeds`, `sha` (to fail if HEAD changed).
  </Tab>

  <Tab title="Workspace builder (DSUL)">
    ## Workspace builder

    **Goal:** install the connector in a workspace, configure authentication and the agent allowlist, and call GitLab operations from your automations.

    ## Installation

    1. Go to **Apps** in your workspace
    2. Search for **GitLab** and install it
    3. Open the **Configuration app** (the link auto-populated on install) to choose the auth mode, provide credentials, connect a GitLab account, and allow the agents that may call the connector

    ## Configuration

    | Field                 | Description                                                                                                                                                                      |
    | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | **Configuration app** | Auto-populated on install — open this link to configure authentication (mode + credentials), connect a GitLab account (OAuth modes), and manage the authorized-agents allowlist. |

    The configuration app drives everything; the app instance itself has no per-field credential form. From it you pick one of:

    | Auth mode      | What you provide                                                                                                                                                       | Best for                                                                            |
    | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
    | `oauthCentral` | Nothing — just click **Connect**                                                                                                                                       | Workspaces on a platform where the maintainer set up the central GitLab client      |
    | `oauth`        | Your own GitLab **Application ID + Secret** (redirect URI shown in the config app: `<api-url>/workspaces/slug:<your-workspace>/webhooks/<app-instance>.oauthCallback`) | You manage your own GitLab OAuth app                                                |
    | `accessToken`  | A pre-minted GitLab **access token** / Personal Access Token with the `api` scope                                                                                      | Short-lived or caller-managed tokens, service-account / non-interactive automations |

    <Note>
      Credentials are stored in the workspace's Secrets (`gitlabAuth`) and resolved server-side. The agent allowlist (**Authorized agents**, secret `gitlabAuthorizedAgents`) gates which Agent Factory agents may call the MCP endpoint — see the *Agent builder* tab. Builder automations calling `GitLab.op:` instructions resolve the same workspace credentials.
    </Note>

    ## Available Instructions

    Every instruction resolves credentials from the workspace configuration. The MCP server exposes the same operations behind the 16 entity tools (see the *Agent builder* tab).

    ### Projects

    | Instruction        | Description                                                                                                                                | Returns                                                  |
    | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- |
    | `listProjects`     | List accessible projects; pass `membership`/`owned` for *my* projects, plus `search`, `visibility`, `order_by`, `sort`, `per_page`, `page` | `[ { id, name, path_with_namespace, visibility, ... } ]` |
    | `createProject`    | Create a project by `name` (+ `path`, `namespace_id`, `description`, `visibility`, `default_branch`, `initialize_with_readme`)             | `{ id, name, web_url, ... }`                             |
    | `getProject`       | Get a project by `id`                                                                                                                      | `{ id, name, namespace, ... }`                           |
    | `updateProject`    | Update a project by `id` (`name`, `path`, `description`, `visibility`, `default_branch`)                                                   | `{ id, name, ... }`                                      |
    | `deleteProject`    | Delete a project by `id`                                                                                                                   | Empty (HTTP 202)                                         |
    | `archiveProject`   | Archive a project by `id`                                                                                                                  | `{ id, archived: true }`                                 |
    | `unarchiveProject` | Unarchive a project by `id`                                                                                                                | `{ id, archived: false }`                                |
    | `forkProject`      | Fork a project by `id` into a `namespace`/`namespace_id` (+ `name`, `path`)                                                                | `{ id, forked_from_project, ... }`                       |

    ### Issues

    | Instruction       | Description                                                                                                                  | Returns                            |
    | ----------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
    | `listIssues`      | List issues of project `id` (`state`, `labels`, `milestone`, `search`, `assignee_username`, `per_page`, `page`)              | `[ { iid, title, state, ... } ]`   |
    | `createIssue`     | Create an issue in `id` with `title` (+ `description`, `labels`, `assignee_ids`, `milestone_id`, `confidential`, `due_date`) | `{ iid, title, web_url, ... }`     |
    | `getIssue`        | Get issue `iid` of project `id`                                                                                              | `{ iid, title, description, ... }` |
    | `updateIssue`     | Update issue `iid` of `id` (`title`, `description`, `labels`, `assignee_ids`, `milestone_id`, `state_event`)                 | `{ iid, ... }`                     |
    | `deleteIssue`     | Delete issue `iid` of `id`                                                                                                   | Empty (HTTP 204)                   |
    | `closeIssue`      | Close issue `iid` (state\_event=close)                                                                                       | `{ iid, state: "closed" }`         |
    | `reopenIssue`     | Reopen issue `iid` (state\_event=reopen)                                                                                     | `{ iid, state: "opened" }`         |
    | `listIssueNotes`  | List notes of issue `iid` (`sort`, `order_by`, `per_page`)                                                                   | `[ { id, body, author, ... } ]`    |
    | `createIssueNote` | Add a note (`body`) on issue `iid` (+ `confidential`)                                                                        | `{ id, body, ... }`                |
    | `getIssueNote`    | Get note `note_id` of issue `iid`                                                                                            | `{ id, body, ... }`                |
    | `updateIssueNote` | Update note `note_id` body of issue `iid`                                                                                    | `{ id, body, ... }`                |
    | `deleteIssueNote` | Delete note `note_id` of issue `iid`                                                                                         | Empty (HTTP 204)                   |

    ### Merge Requests

    | Instruction              | Description                                                                                                                                                                  | Returns                             |
    | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
    | `listMergeRequests`      | List MRs of `id` (`state`, `source_branch`, `target_branch`, `labels`, `search`, `per_page`)                                                                                 | `[ { iid, title, state, ... } ]`    |
    | `createMergeRequest`     | Create an MR in `id` (`source_branch`, `target_branch`, `title` + `description`, `assignee_ids`, `reviewer_ids`, `labels`, `milestone_id`, `remove_source_branch`, `squash`) | `{ iid, title, web_url, ... }`      |
    | `getMergeRequest`        | Get MR `iid` of `id`                                                                                                                                                         | `{ iid, title, merge_status, ... }` |
    | `updateMergeRequest`     | Update MR `iid` of `id` (`title`, `description`, `target_branch`, `assignee_ids`, `labels`, `milestone_id`, `state_event`)                                                   | `{ iid, ... }`                      |
    | `deleteMergeRequest`     | Delete MR `iid` of `id`                                                                                                                                                      | Empty (HTTP 204)                    |
    | `mergeMergeRequest`      | Merge MR `iid` of `id` (`merge_commit_message`, `squash`, `should_remove_source_branch`, `merge_when_pipeline_succeeds`, `sha`)                                              | `{ iid, state: "merged", ... }`     |
    | `approveMergeRequest`    | Approve MR `iid` of `id` (+ `sha`)                                                                                                                                           | `{ id, approved_by, ... }`          |
    | `unapproveMergeRequest`  | Remove your approval on MR `iid` of `id`                                                                                                                                     | Empty (HTTP 204)                    |
    | `listMergeRequestNotes`  | List notes of MR `iid` (`sort`, `order_by`, `per_page`)                                                                                                                      | `[ { id, body, ... } ]`             |
    | `createMergeRequestNote` | Add a note (`body`) on MR `iid`                                                                                                                                              | `{ id, body, ... }`                 |
    | `getMergeRequestNote`    | Get note `note_id` of MR `iid`                                                                                                                                               | `{ id, body, ... }`                 |
    | `updateMergeRequestNote` | Update note `note_id` body of MR `iid`                                                                                                                                       | `{ id, body, ... }`                 |
    | `deleteMergeRequestNote` | Delete note `note_id` of MR `iid`                                                                                                                                            | Empty (HTTP 204)                    |

    ### Branches & Commits

    | Instruction        | Description                                                                        | Returns                               |
    | ------------------ | ---------------------------------------------------------------------------------- | ------------------------------------- |
    | `listBranches`     | List branches of `id` (`search`, `per_page`)                                       | `[ { name, commit, protected } ]`     |
    | `createBranch`     | Create `branch` from `ref` in `id`                                                 | `{ name, commit, ... }`               |
    | `getBranch`        | Get `branch` of `id`                                                               | `{ name, commit, merged, ... }`       |
    | `deleteBranch`     | Delete `branch` of `id`                                                            | Empty (HTTP 204)                      |
    | `listCommits`      | List commits of `id` (`ref_name`, `since`, `until`, `path`, `author`, `per_page`)  | `[ { id, title, author_name, ... } ]` |
    | `createCommit`     | Create a commit on `branch` of `id` with `commit_message` + `actions` (multi-file) | `{ id, short_id, web_url, ... }`      |
    | `getCommit`        | Get commit `sha` of `id`                                                           | `{ id, title, stats, ... }`           |
    | `cherryPickCommit` | Cherry-pick `sha` onto `branch` of `id` (+ `dry_run`, `message`)                   | `{ id, short_id, ... }`               |
    | `revertCommit`     | Revert `sha` on `branch` of `id` (+ `dry_run`)                                     | `{ id, short_id, ... }`               |

    ### Tags & Releases

    | Instruction     | Description                                                                             | Returns                               |
    | --------------- | --------------------------------------------------------------------------------------- | ------------------------------------- |
    | `listTags`      | List tags of `id` (`search`, `order_by`, `sort`, `per_page`)                            | `[ { name, target, commit } ]`        |
    | `createTag`     | Create `tag_name` from `ref` in `id` (+ `message`)                                      | `{ name, target, ... }`               |
    | `getTag`        | Get `tag_name` of `id`                                                                  | `{ name, target, commit }`            |
    | `listReleases`  | List releases of `id` (`order_by`, `sort`, `per_page`)                                  | `[ { tag_name, name, released_at } ]` |
    | `createRelease` | Create a release for `tag_name` in `id` (+ `name`, `description`, `ref`, `released_at`) | `{ tag_name, name, ... }`             |
    | `getRelease`    | Get release by `tag_name` of `id`                                                       | `{ tag_name, name, assets, ... }`     |
    | `deleteRelease` | Delete release `tag_name` of `id`                                                       | `{ tag_name, ... }`                   |

    ### Pipelines & Jobs

    | Instruction        | Description                                                                                 | Returns                           |
    | ------------------ | ------------------------------------------------------------------------------------------- | --------------------------------- |
    | `listPipelines`    | List pipelines of `id` (`status`, `ref`, `sha`, `username`, `order_by`, `sort`, `per_page`) | `[ { id, status, ref, sha } ]`    |
    | `createPipeline`   | Trigger a pipeline on `ref` of `id` (+ `variables`)                                         | `{ id, status, web_url, ... }`    |
    | `getPipeline`      | Get pipeline `pipeline_id` of `id`                                                          | `{ id, status, duration, ... }`   |
    | `retryPipeline`    | Retry failed/canceled jobs of pipeline `pipeline_id`                                        | `{ id, status: "running", ... }`  |
    | `cancelPipeline`   | Cancel pipeline `pipeline_id` of `id`                                                       | `{ id, status: "canceled", ... }` |
    | `listPipelineJobs` | List jobs of pipeline `pipeline_id` (`scope`, `per_page`)                                   | `[ { id, name, status, stage } ]` |
    | `getJob`           | Get job `job_id` of `id`                                                                    | `{ id, name, status, ... }`       |
    | `retryJob`         | Retry job `job_id` of `id`                                                                  | `{ id, status: "pending", ... }`  |

    ### Environments

    | Instruction        | Description                                                        | Returns                                 |
    | ------------------ | ------------------------------------------------------------------ | --------------------------------------- |
    | `listEnvironments` | List environments of `id` (`name`, `search`, `states`, `per_page`) | `[ { id, name, state, external_url } ]` |
    | `getEnvironment`   | Get `environment_id` of `id`                                       | `{ id, name, last_deployment, ... }`    |
    | `stopEnvironment`  | Stop environment `environment_id` of `id`                          | `{ id, state: "stopped" }`              |

    ### Labels & Milestones

    | Instruction       | Description                                                                                     | Returns                              |
    | ----------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------ |
    | `listLabels`      | List labels of `id` (`with_counts`, `search`, `per_page`)                                       | `[ { id, name, color, ... } ]`       |
    | `createLabel`     | Create label `name` with `color` in `id` (+ `description`, `priority`)                          | `{ id, name, color }`                |
    | `updateLabel`     | Update label (by `name` or `label_id`) of `id` (`new_name`, `color`, `description`, `priority`) | `{ id, name, color }`                |
    | `deleteLabel`     | Delete label (by `name` or `label_id`) of `id`                                                  | Empty (HTTP 204)                     |
    | `listMilestones`  | List milestones of `id` (`state`, `search`, `per_page`)                                         | `[ { id, title, state, due_date } ]` |
    | `createMilestone` | Create milestone `title` in `id` (+ `description`, `due_date`, `start_date`)                    | `{ id, title, ... }`                 |
    | `getMilestone`    | Get `milestone_id` of `id`                                                                      | `{ id, title, state, ... }`          |
    | `updateMilestone` | Update `milestone_id` of `id` (`title`, `description`, `due_date`, `start_date`, `state_event`) | `{ id, title, ... }`                 |

    ### Wiki

    | Instruction      | Description                                                      | Returns                       |
    | ---------------- | ---------------------------------------------------------------- | ----------------------------- |
    | `listWikiPages`  | List wiki pages of `id` (+ `with_content`)                       | `[ { slug, title, format } ]` |
    | `createWikiPage` | Create a wiki page in `id` with `title` + `content` (+ `format`) | `{ slug, title, content }`    |
    | `getWikiPage`    | Get wiki page by `slug` of `id` (+ `render_html`, `version`)     | `{ slug, title, content }`    |

    ### Webhooks & CI/CD Variables

    | Instruction             | Description                                                                                                                 | Returns                                  |
    | ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
    | `listProjectHooks`      | List webhooks of `id` (+ `per_page`)                                                                                        | `[ { id, url, ...events } ]`             |
    | `createProjectHook`     | Add a webhook `url` to `id` (+ per-event boolean flags, `token`, `enable_ssl_verification`)                                 | `{ id, url, ... }`                       |
    | `deleteProjectHook`     | Delete webhook `hook_id` of `id`                                                                                            | Empty (HTTP 204)                         |
    | `listProjectVariables`  | List CI/CD variables of `id` (+ `per_page`)                                                                                 | `[ { key, value, variable_type, ... } ]` |
    | `createProjectVariable` | Create variable `key`=`value` in `id` (+ `variable_type`, `protected`, `masked`, `raw`, `environment_scope`, `description`) | `{ key, value, ... }`                    |
    | `updateProjectVariable` | Update variable `key` of `id` (same params, `filter[environment_scope]`)                                                    | `{ key, value, ... }`                    |
    | `deleteProjectVariable` | Delete variable `key` of `id` (+ `filter[environment_scope]`)                                                               | Empty (HTTP 204)                         |

    ### Deploy Keys & Members

    | Instruction           | Description                                                     | Returns                              |
    | --------------------- | --------------------------------------------------------------- | ------------------------------------ |
    | `listDeployKeys`      | List deploy keys of `id` (+ `per_page`)                         | `[ { id, title, key, can_push } ]`   |
    | `createDeployKey`     | Add deploy key `title`/`key` to `id` (+ `can_push`)             | `{ id, title, ... }`                 |
    | `deleteDeployKey`     | Delete deploy key `key_id` of `id`                              | Empty (HTTP 204)                     |
    | `listProjectMembers`  | List direct members of `id` (`query`, `per_page`)               | `[ { id, username, access_level } ]` |
    | `addProjectMember`    | Add user `user_id` to `id` with `access_level` (+ `expires_at`) | `{ id, username, access_level }`     |
    | `removeProjectMember` | Remove member `user_id` from `id`                               | Empty (HTTP 204)                     |

    ### Users & Groups

    | Instruction      | Description                                                                       | Returns                         |
    | ---------------- | --------------------------------------------------------------------------------- | ------------------------------- |
    | `getCurrentUser` | Get the currently authenticated user                                              | `{ id, username, name, email }` |
    | `getUser`        | Get a user by `user_id`                                                           | `{ id, username, name, ... }`   |
    | `listUsers`      | List users (`active`, `blocked`, `username`, `per_page`, `page`)                  | `[ { id, username, name } ]`    |
    | `searchUsers`    | Search users by `search` (name/username/email) (+ `per_page`)                     | `[ { id, username, name } ]`    |
    | `listGroups`     | List groups (`search`, `owned`, `min_access_level`, `top_level_only`, `per_page`) | `[ { id, name, full_path } ]`   |
    | `createGroup`    | Create a group `name`/`path` (+ `description`, `visibility`, `parent_id`)         | `{ id, name, web_url, ... }`    |
    | `getGroup`       | Get group by `id` (or URL-encoded path)                                           | `{ id, name, projects, ... }`   |
    | `updateGroup`    | Update group `id` (`name`, `path`, `description`, `visibility`)                   | `{ id, name, ... }`             |

    <Note>
      `Returns` shows the shape of the operation output (the underlying GitLab API resource).
    </Note>

    ## DSUL Examples

    ### List the user's own projects

    ```yaml theme={null}
    - GitLab.listProjects:
        membership: true
        order_by: last_activity_at
        sort: desc
        per_page: 20
        output: myProjects
    ```

    ### Create an issue with labels and an assignee

    ```yaml theme={null}
    - GitLab.createIssue:
        id: '{{projectId}}'
        title: Investigate 500s on /api/v1/search
        description: |
          Spike of 500s since 14:00 UTC. Logs in Kibana (link).
        labels: BUG,Incident
        assignee_ids:
          - '{{oncallUserId}}'
        output: issue
    ```

    ### Open a merge request from a feature branch

    ```yaml theme={null}
    - GitLab.createBranch:
        id: '{{projectId}}'
        branch: feature/auto-deploy
        ref: main
        output: branch
    - GitLab.createMergeRequest:
        id: '{{projectId}}'
        source_branch: feature/auto-deploy
        target_branch: main
        title: Automate staging deploy
        description: Closes #{{issue.iid}}
        remove_source_branch: true
        output: mr
    ```

    ### Trigger a pipeline on a specific ref

    ```yaml theme={null}
    - GitLab.createPipeline:
        id: '{{projectId}}'
        ref: main
        variables:
          - key: DEPLOY_ENV
            value: staging
        output: pipeline
    ```
  </Tab>
</Tabs>

***

## Error Handling

| HTTP Status | Error        | Solution                                                                    |
| ----------- | ------------ | --------------------------------------------------------------------------- |
| 400         | Bad request  | Check argument shapes (IDs, dates, enum values)                             |
| 401         | Unauthorized | Sign in again (OAuth modes), or verify the access token has the `api` scope |
| 403         | Forbidden    | The token lacks permission on this project/group — check the access level   |
| 404         | Not Found    | Verify the project `id` (numeric or URL-encoded path) and resource `iid`    |
| 409         | Conflict     | Common on merge conflicts or concurrent updates                             |
| 429         | Rate Limited | GitLab.com enforces \~2000 req/min per token; back off and retry            |
| 500         | Server Error | Transient GitLab issue; retry after a few seconds                           |

### Common Issues

**"This agent is not authorized to use this connector"** — The calling agent is not in the allowlist. Open the configuration app → **Authorized agents** → tick this agent (id is shown in the error) or enable **Allow all agents**, and Save. The **Install capability** button does this for you.

**"The calling agent could not be identified"** — The MCP capability *Scope* does not declare `agent_id`, so Agent Factory never injects the agent identity. Set the Scope to `context_id,agent_id,user_id` on the capability (this is separate from the GitLab OAuth scopes), then allow the agent in the config app.

**"GitLab is not connected for this user"** — No per-user OAuth token is stored for the caller. Open the configuration app (OAuth mode) and click **Connect**, or use the agent's connect flow.

**"GitLab token refresh failed … must reconnect"** — The stored refresh token was rejected by GitLab (revoked / expired). The connection is dropped automatically; the user must reconnect from the config app.

**"GitLab is not authenticated / OAuth is not configured"** — Neither a tenant OAuth client nor the central platform client is available. Set the OAuth client ID/secret in the config app (tenant mode), or ask the platform maintainer to provision the central OAuth client from the core workspace's config app.

**"Can't approve own MR"** — A user counts as the MR author and cannot approve its own merge requests. Connect with a different GitLab account (or use an `accessToken` from another user) for the approval step.

**"pipeline creation 404"** — The target ref has no `.gitlab-ci.yml` at its root. Commit a CI config first, or pick a ref that has one.

## External Resources

<CardGroup cols={2}>
  <Card title="GitLab REST API" icon="book" href="https://docs.gitlab.com/ee/api/">
    Official GitLab REST v4 API reference
  </Card>

  <Card title="Tool Agents" icon="robot" href="/products/agent-factory/capabilities">
    Learn how Agent Factory agents consume MCP tools in Prisme.ai.
  </Card>
</CardGroup>
