Announcing CIMD support for MCP Client registration
Learn more

GitLab MCP vs GitLab API for AI Agents (2026)

TL;DR

  • The GitLab MCP server is Beta, Premium and Ultimate only, and runs as an endpoint on your own GitLab instance at /api/v4/mcp. It exposes 16 tools. The REST plus GraphQL API covers the full platform. The Scalekit GitLab connector maps 110 tools onto that API surface.
  • MCP auth is OAuth 2.0 with Dynamic Client Registration and browser consent, scoped to the mcp scope by default. The API also supports personal, project, and group access tokens, plus the Device Authorization Grant. GitLab has no client-credentials or JWT-bearer service grant.
  • The MCP server can create and read issues and merge requests, manage pipelines, and search. It cannot write repository files, manage branches, tags, releases, webhooks, members, or CI/CD variables, and it cannot update, merge, or approve a merge request.
  • MCP requires an interactive browser flow, so a headless agent cannot complete it. The API path uses a project or group access token (bot-user-backed) for headless and background work.
  • Neither path stores, refreshes, or revokes per-user credentials for you. Scalekit's GitLab connector handles the OAuth flow, token vault, and refresh for both paths, so the MCP versus API decision does not change your auth infrastructure.

Your agent needs to read and act on GitLab: open issues, raise merge requests, watch pipelines, update records on behalf of your developers. GitLab now ships two paths. The official GitLab MCP server reached Beta in GitLab 18.6, and the REST and GraphQL APIs your code has called for years are still there. They cover overlapping but not identical territory, they put you on different auth paths, and for headless or write-heavy agents one of those differences is a hard blocker. Here is how to pick.

What GitLab MCP and GitLab API actually are

GitLab MCP

The GitLab MCP server is built into GitLab and maintained by GitLab, not a community fork. It started as an experiment in GitLab 18.3 and moved to Beta in GitLab 18.6, with support for the 2025-03-26 and 2025-06-18 MCP protocol specifications added in 18.7.

It is exposed by your own instance at https://<your-gitlab>/api/v4/mcp, not a central hosted endpoint. It is available on Premium and Ultimate, and requires GitLab Duo and beta features to be turned on. Authentication uses OAuth 2.0 Dynamic Client Registration, so a client registers itself, the user approves a browser consent screen, and the client receives an access token.

Official docs: the GitLab MCP server and the MCP server tools.

GitLab API

The GitLab API is two surfaces: a REST API at /api/v4 organized into resource groups for projects, repositories, issues, merge requests, pipelines, members, webhooks, releases, and more, and a GraphQL API at /api/graphql with queries and mutations.

Auth is token-based. The API accepts personal access tokens, OAuth 2.0 tokens, project access tokens, group access tokens, and CI/CD job tokens, passed in the PRIVATE-TOKEN or Authorization: Bearer header. Each token carries explicit scopes such as api, read_api, read_repository, and write_repository.

Official docs: the REST API, the GraphQL API, and REST API authentication.

Comparing them where it matters for agents

What your agent can actually do

The MCP server covers the conversational and read surface well, plus issue and merge request creation and pipeline control. The API owns everything operational and every write the MCP server has not shipped.

Capability
GitLab MCP server (Beta)
GitLab REST and GraphQL API
Create and read issues
Yes (create_issue, get_issue)
Yes
Update, close, or delete an issue
No
Yes
Create and read merge requests
Yes (create_merge_request, get_merge_request)
Yes
Update, merge, or approve a merge request
No
Yes
Read MR commits and diffs
Yes
Yes
Comment on issues and work items
Yes (create_workitem_note)
Yes
Manage pipelines (create, retry, cancel, delete, list)
Yes (manage_pipeline)
Yes
Read pipeline jobs and job logs
Yes (get_pipeline_jobs, get_job_log)
Yes
Search across instance, group, project
Yes (search)
Yes
Semantic code search
Yes (semantic_code_search)
Limited, requires setup and enablement
Create, update, delete repository files (commits)
No
Yes
Create and delete branches and tags
No
Yes
Create and manage releases
No
Yes
Create and manage webhooks
No
Yes
Manage project or group members and access
No
Yes
Manage CI/CD variables
No
Yes

Where the gap bites

The MCP server is built for read and review, with a narrow set of writes. Anything that changes the repository or the project configuration lives in the API. If your agent commits code, cuts a release, manages branches, or merges an approved MR, the MCP server does not expose a tool for it. That is not a temporary limitation; it reflects the tool set GitLab has shipped so far.

The auth path each one puts you on

MCP: OAuth with user identity

The MCP server uses OAuth 2.0 Dynamic Client Registration. Every user who connects completes a browser consent flow, and the resulting token is bound to that user's GitLab permissions. The default scope granted through Dynamic Client Registration is mcp, which is purpose-built for the MCP server's tools.

There is one consequence worth stating plainly: an MCP token and a full API token are not interchangeable. The mcp scope is separate from api and read_api. A credential minted for the MCP path does not grant general REST or GraphQL access, and the reverse is also true.

API: tokens for every execution model

The API gives you more options. Personal access tokens authenticate as a user. OAuth supports Authorization Code, Authorization Code with PKCE, and the Device Authorization Grant for input-limited devices. Project access tokens and group access tokens are bot-user-backed credentials scoped to a single project or group. CI/CD job tokens authenticate pipeline jobs.

One thing GitLab does not offer is a client-credentials or JWT-bearer service grant. The GitLab pattern for a non-interactive service identity is a project or group access token, not a machine OAuth flow.

The identity question that follows you either way

For a multi-tenant B2B agent acting across many customer namespaces, MCP gives you one OAuth token per user automatically. The API lets you choose: per-user OAuth, or a project or group access token that acts as a bot identity. Neither path solves storage, refresh, or revocation. That is infrastructure you own regardless of the path.

What you own in production

On the MCP path

GitLab manages the server, the tool schemas, and the protocol negotiation. You still own per-user token storage, refresh, revocation on disconnect, and tenant isolation. You also own safety: GitLab states explicitly that you are responsible for guarding against prompt injection, and advises using MCP tools only on GitLab objects you trust.

The MCP server is gated behind Premium or Ultimate, GitLab Duo, and beta feature flags. For some teams that governance is a feature. For an agent that needs to deploy without those prerequisites, it is a dependency to plan around.

On the API path

You own the full operational surface: endpoint selection, request construction, pagination, error handling, retries, and the token lifecycle. The tradeoff is control. The REST API is versioned and follows a deprecation process, and GraphQL mutations are explicit, so a deterministic pipeline is not exposed to schema changes shipped through an MCP server update.

The API also reaches every tier. Core REST endpoints are available on Free, while the MCP server is not.

When to use MCP, when to use the API

Use GitLab MCP when

  • Your agent is interactive and user-present: Cursor, Claude Code, VS Code, or a developer assistant where OAuth consent in a browser is natural.
  • The work is read and review: summarizing merge requests, retrieving diffs, inspecting pipeline jobs and logs, or semantic code search.
  • You want GitLab's permission model and Duo governance to constrain the agent by default.
  • You are prototyping and want LLM-ready tool descriptions without writing schemas.

Use the GitLab API when

  • Your agent runs headless: scheduled syncs, background pipeline watchers, release-note generators, where no browser session exists.
  • You need writes the MCP server does not expose: repository file commits, branch and tag management, releases, webhooks, members, or CI/CD variables.
  • You need to update, merge, or approve merge requests, not just create and read them.
  • You are building a deterministic pipeline and need versioned, predictable behavior.

What each path can and cannot do, from the official tool lists

The official MCP tool set

The GitLab MCP server tools page lists 16 tools: get_mcp_server_version, create_issue, get_issue, create_merge_request, get_merge_request, get_merge_request_commits, get_merge_request_diffs, get_merge_request_pipelines, get_pipeline_jobs, get_job_log, manage_pipeline, create_workitem_note, get_workitem_notes, search, search_labels, and semantic_code_search.

What that list tells you is missing

Read the list and the gaps are concrete. There is no tool to write a file or create a commit, no tool to create or delete a branch or tag, no tool to cut a release, no tool to register a webhook, no tool to add a project or group member, and no tool to set a CI/CD variable. Issue and merge request coverage is create-and-read only; there is no update, close, merge, or approve.

What the API path covers instead

The REST API documents all of these as first-class resources, including the Commits API for file operations, Project webhooks, and the full REST resource index. The Scalekit GitLab connector turns that surface into 110 named tools, which is the practical way to see the difference: it includes gitlab_file_create, gitlab_branch_create, gitlab_tag_create, gitlab_release_create, gitlab_project_webhook_create, gitlab_merge_request_merge, and gitlab_merge_request_approve, none of which exist on the official MCP server.

The credential problem that exists on both paths

Both paths hand you a credential, not a vault

Whether you choose MCP or the API, every user in a multi-tenant agent has their own GitLab credential. Fifty customers means fifty credential lifecycles. The MCP OAuth flow gives you a token per user. The API's OAuth flow gives you an access token per user, and a project or group access token gives you a bot credential per namespace.

The lifecycle is yours to run

OAuth access tokens expire after two hours and must be refreshed. Personal, project, and group access tokens expire at a set date, default 365 days, and can be revoked by the user at any time. In every case the token has to live somewhere encrypted, isolated per tenant, and revocable, and your agent has to detect a revoked or expired token rather than failing silently.

Where Scalekit fits

Scalekit's GitLab connector handles the OAuth flow, per-user token storage in the Token Vault, and automatic refresh for both paths. The same auth infrastructure works whether you chose the MCP path or the direct API path. The path decision does not change what you need at the credential layer.

Building a GitLab agent with Scalekit

What the connector gives you

The Scalekit GitLab connector is built on the REST and GraphQL surface, so it covers the writes the official MCP server does not. With the api scope it exposes all 110 tools; with read_api you get a read-only agent. Scalekit acts as the OAuth client, redirects the user to GitLab, stores the token, and refreshes it before expiry. Your code only ever passes a connection_name and a user identifier.

Token Vault and the value for agent builders

Credentials never touch your agent runtime. The Token Vault stores them per user and per tool, rotates them, and injects them at call time. For a multi-tenant GitLab agent, that means every tool call runs as the specific user who authorized it, with their exact project and group permissions. What the user cannot do in GitLab, the agent cannot do.

Python with Claude

Create the connected account, load the scoped tool surface, then run the Claude tool-use loop. Note that the connection_name must match the connection you created in the dashboard exactly, including case.

import os import anthropic import scalekit.client from dotenv import load_dotenv load_dotenv() scalekit_client = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), ) actions = scalekit_client.actions identifier = os.getenv("USER_IDENTIFIER", "user_gl_456") # 1. Resolve this user's GitLab connected account response = actions.get_or_create_connected_account( connection_name="gitlab", identifier=identifier, ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link(connection_name="gitlab", identifier=identifier) print("Authorize GitLab:", link.link) exit(0) # 2. Load only the tools this user is authorized to call scoped_tools, _ = actions.tools.list_scoped_tools(identifier=identifier) # 3. Claude picks a tool; you execute it as this user client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) result = actions.execute_tool( identifier=identifier, tool_name="gitlab_merge_request_create", tool_input={ "id": "acme%2Fbackend", "source_branch": "fix/login", "target_branch": "main", "title": "Fix login bug", }, )

Node.js

The Node SDK follows the same shape. Use connectionName and identifier, list the scoped surface, then execute.

import { ScalekitClient } from "@scalekit-sdk/node"; const sk = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET, ); const identifier = "user_gl_456"; await sk.actions.getOrCreateConnectedAccount({ connectionName: "gitlab", identifier, }); const { tools } = await sk.tools.listScopedTools(identifier, { filter: { connectionNames: ["gitlab"], toolNames: ["gitlab_issues_list", "gitlab_merge_requests_list", "gitlab_pipelines_list"], }, pageSize: 100, }); const { data } = await sk.tools.executeTool({ toolName: "gitlab_issue_create", identifier, params: { id: "acme%2Fbackend", title: "Flaky test in CI" }, });

LangChain

Scalekit has a native LangChain adapter. The agent decides what to call; Scalekit handles auth on every invocation, so there is 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."), ("human", "{input}"), MessagesPlaceholder("agent_scratchpad"), ]) agent = create_tool_calling_agent(ChatAnthropic(model="claude-opus-4-8"), gl_tools, prompt) executor = AgentExecutor(agent=agent, tools=gl_tools)

Hermes and any OpenAI-compatible runtime

Scalekit is framework-agnostic; the scoped tool schemas plug into any LLM API. A Hermes model served through an OpenAI-compatible endpoint consumes the same Scalekit tools. You point the OpenAI client at the Hermes endpoint, list the scoped tools, and execute the one the model selects.

import os from openai import OpenAI import scalekit.client scalekit_client = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), ) actions = scalekit_client.actions identifier = "user_gl_456" # Point an OpenAI-compatible client at your Hermes endpoint client = OpenAI(base_url=os.getenv("HERMES_BASE_URL"), api_key=os.getenv("HERMES_API_KEY")) # Same scoped surface, OpenAI tool format scoped_tools, _ = actions.tools.list_scoped_tools(identifier=identifier) # Execute the tool the model selected, as this user result = actions.execute_tool( identifier=identifier, tool_name="gitlab_pipelines_list", tool_input={"id": "acme%2Fbackend", "status": "failed"}, )

If you need the connector wired to a runtime Scalekit does not document a native adapter for yet, ask in the Scalekit Slack community or talk to Scalekit's engineers.

Connect through Scalekit's Virtual MCP endpoint

If your client expects an MCP server, point it at Scalekit's per-user GitLab endpoint instead of standing up your own. The endpoint is static; the identity behind it is per-user.

{ "mcpServers": { "gitlab": { "url": "https://mcp.scalekit.com/gitlab", "headers": { "Authorization": "Bearer $SCALEKIT_TOKEN" } } } }

Observability for multi-tool, multi-tenant agents

Auth logs are where multi-tenant agents become operable. Scalekit records who authorized, which agent acted, which tool ran, what scope applied, and what came back. Logs are queryable and exportable to Datadog, Splunk, or any SIEM, with 90-day retention by default.

That matters downstream. When an agent spans GitLab, Slack, and Jira for one user, every tool call resolves to that user's credential and lands in the audit trail attributed to them, not a shared bot account. When a developer leaves, you revoke at the connected-account level and the agent stops acting as them across every connector at once.

Which one to build against

If your agent is developer-facing and interactive, and its job is to read, review, search, and create the occasional issue or merge request, the GitLab MCP server is the faster path, with GitLab's permission model and Duo governance applied by default. Account for the Beta status, the Premium or Ultimate requirement, and the browser-only OAuth flow.

If your agent runs headless, writes to the repository, manages releases or webhooks, or has to merge and approve, build against the API directly. The credential management problem is the same either way, and that is the part that needs production-grade infrastructure.

Browse the Scalekit GitLab connector: scalekit.com/agent-connector/gitlab. Or talk to us about your GitLab agent.

No items found.
Agent Auth Quickstart
On this page
Share this article
Agent Auth Quickstart

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
1 million Monthly Active Users
100 Monthly Active Organizations
1 SSO connection
1 SCIM connection
10K Connected Accounts
Unlimited Dev & Prod environments