Content

GitLab AI API and CI Agents

A personal access token gives your agent your permissions across every project you can see. A project access token gives it one project. That choice is the whole security model.

Chaitrali Kakde

DevRel Engineer

EngineeringOct 24, 2026

Most of the risk in a GitLab agent is decided before you write any code, in the thirty seconds where you choose which kind of token to generate. Get it wrong and every guardrail afterwards is decoration.

The token decision, which is the actual security model

A personal access token carries your permissions. All of them, across every project and group you can see. If you are a maintainer on forty repos, so is your agent. This is the easy path and the one most prototypes take, because it is the token sitting in your settings already.

A project access token is scoped to one project. This is what an agent should almost always have. If it goes wrong, it goes wrong in one repo.

A group access token covers every project in the group, which is occasionally what you want for a reporting agent and rarely what you want for anything that writes.

There are also CI job tokens, which work only against specific endpoints and are the right choice for something running inside a pipeline, and impersonation tokens for administrators. Deploy tokens cannot be used with the public API at all, which catches people who assume they are a lightweight option.

And one to simply never give an agent: the sudo scope, which lets an administrator act as another user. An agent that can impersonate arbitrary users has no meaningful audit trail, because every action it takes looks like somebody else did it.

Scopes: the api / read_api split matters

GitLab's scopes include api, read_api, read_user, sudo, and more recently mcp and mcp_orbit.

The useful property: most endpoints accept read_api only for GET and HEAD requests. So a read_api token is a genuinely read-only credential, enforced by GitLab rather than by your own discipline. For any analysis, reporting or review agent, that is the scope to use, and it means a bug in your code cannot write.

Insufficient scope returns a 403 with an insufficient_scope error, which is a pleasantly specific thing to see in a log, and the token needs one of the listed scopes rather than all of them.

Headers and the base path

The API is at /api/v4 on your instance, so https://gitlab.example.com/api/v4/projects for self-managed. The recommended header for personal, project and group tokens is PRIVATE-TOKEN, though Authorization: Bearer also works and is standard for OAuth tokens.

One trap: OAuth tokens can go in an access_token query parameter, and sending the token in both the header and the query string returns a 400. Query-string credentials also land in logs and proxies, so use the header and leave the parameter alone.

Rate limits are higher for authenticated requests, vary by plan on GitLab.com, and come back as 429 with a Retry-After header you should actually honour. Backoff on 429 is automatic; respecting the header is the difference between recovering and getting throttled harder.

Setting it up

setup.sh
npx swytchcode
swy login
swy init --mode=sandbox

swy get gitlab
swy auth connect gitlab          # paste a PROJECT access token with read_api
swy auth status

swy discover "list merge requests" --project gitlab
swy discover "list pipelines for a merge request" --project gitlab
swy discover "create a merge request note" --project gitlab
swy list methods gitlab

swy info <canonical_id>
swy add method <canonical_id>    # reads + MR comments. Not merge, not delete.

The allowlist we would ship for a code-review agent: read projects, merge requests, diffs, pipelines and jobs freely; post a comment on a merge request, because feedback is the point; and nothing that merges, approves, deletes a branch, or edits CI variables. Merging in particular should stay human, not because an agent cannot evaluate a diff but because approval is a social act with accountability attached.

Protected CI variables are the quiet one to watch. An agent that can read or edit them can exfiltrate deploy credentials, which turns a code-review bot into a supply-chain risk. Leave those methods out entirely; the reasoning generalises in API access control for AI agents.

The token is stored outside your project, encrypted at rest, and injected at call time, so it never enters the model's context and the right header is used for you. See the authentication docs. Keep --project gitlab on discover, and swy get before swy info.

Merge request pipelines, and why they confuse agents

GitLab can run a pipeline against the source branch, or against a merged result that does not exist as a commit anywhere. For a human reading the UI that distinction is obvious from context. For an agent asked "did CI pass on this MR," it is a real ambiguity, and answering from the wrong pipeline produces a confident wrong answer about whether code is safe to merge.

Be explicit about which pipeline you are reading, and have the agent report which one it checked rather than just a verdict. "Pipeline 4471 on the merged result passed" is a useful sentence; "CI passed" is not.

Policy: scope the writes you do allow

policies.json
{
  "id": "comments-only-on-open-mrs",
  "target": ["<gitlab create-mr-note canonical_id>"],
  "when": { "field": "id", "operator": "!=", "value": "<allowed-project-id>" },
  "action": {
    "type": "DENY",
    "message": "This agent may only comment on its own project"
  }
}

Belt and braces on top of a project-scoped token: even if someone later swaps in a broader credential, the policy keeps the agent in one project. Use whichever field identifies the project on your allowed method, which swy info will show. Schema in the policy reference, and keeping it in git means widening the scope is a reviewed change rather than an afternoon decision.

Idempotency: the duplicate review comment

Posting a note is not idempotent, and the failure is familiar: the comment lands, the response is lost, the agent retries, and your merge request now has the same review twice. Harmless but it makes the bot look broken, and developers stop reading its comments, which defeats the point.

Writes are only auto-retried when an idempotency key is set, so an unprotected note fails visibly instead of duplicating. Key on the MR ID plus the commit SHA plus the kind of comment, so a re-run after a new push is a new comment and a retry of the same run is not. And read existing notes first: an agent that checks whether it already reviewed this SHA is also an agent that does not re-review unchanged code. Background in API idempotency: the complete guide.

From Claude Code or Cursor

This is where a GitLab integration earns its keep: you are mid-change and want the MR discussion, the failing job output, or the diff of what someone else shipped, without leaving the editor. Connect through the MCP server and the read_api token plus your allowlist means that session can look at everything and change almost nothing. Setup in the MCP guide, reference in the MCP docs, and skills.md for a coding agent wiring it up.

What you can build

  1. A review assistant that comments, never approves. Read the diff, flag the things humans miss (missing error handling, a changed migration, a new dependency), and post one comment. Approval stays with a person.
  2. A pipeline failure explainer. When an MR pipeline fails, fetch the failing job's log, summarise the actual cause, and post it on the MR so nobody has to open the job output. Pairs with the CircleCI guide if you run both.
  3. A stale-MR sweeper. Find merge requests with no activity for two weeks, summarise what they were for and what is blocking them, and post a digest. Read-only, and it surfaces the work that quietly died.
  4. Release notes from merged MRs. Read what merged since the last tag and draft notes into Notion for editing. A read-only token is enough for the whole job.

FAQ

Which token should I actually create?

A project access token with read_api for anything that analyses, and a separate project token with api for the one agent that needs to post comments. Two tokens, two projects in Swytchcode, and the read-only agent is read-only by construction rather than by intention.

Can I use a CI job token for an agent running in a pipeline?

Yes, and it is a good fit because the token's lifetime matches the job and it only works against a limited set of endpoints. Check that the endpoints you need are among them before committing to it.

Self-managed or GitLab.com, does it matter?

The API shape is the same; the base URL is your instance, and rate limits differ since GitLab.com's depend on plan. If you are self-managed with strict egress rules, the fact that the execution kernel runs on your own infrastructure with no proxy hop is usually the detail that matters to your security team.

Should the agent ever merge?

We would leave it out of the allowlist. Even with good judgement on the diff, merging carries accountability that belongs to a person, and the review comment is where the agent's value actually is. If you do automate it, gate every merge behind approval rather than a rule about which branches; the pattern is in human-in-the-loop approval for AI agent tool calls.

More content