Skip to content

Architecture

GitLab MCP Server sits between your AI client and your GitLab instance, translating natural language requests into GitLab API calls via the Model Context Protocol.

natural language

MCP tool calls

REST v4 + GraphQL

JSON

structured result + markdown

formatted answer

User

AI Client

GitLab MCP Server

GitLab Instance

The server is a single static binary that:

  1. Receives MCP tool calls from the AI client (e.g., “list open merge requests”)
  2. Translates them into GitLab REST API v4 or GraphQL requests with proper authentication
  3. Executes the API calls against your GitLab instance
  4. Returns results in dual format: structured JSON for the AI to reason about, and formatted Markdown for display to the user

GitLab MCP Server supports two transport modes — stdio and HTTP — and you pick between them based on whether one user or many share the server. Stdio is the default for a single user on a local machine; HTTP serves a whole team from one process with per-user session isolation.

The standard mode for single-user setups. The AI client spawns the server as a child process and communicates via stdin/stdout using JSON-RPC.

GitLab APIGitLab MCP ServerAI ClientUserGitLab APIGitLab MCP ServerAI ClientUser"Show open MRs in my-project"tools/call: gitlab_execute_action {action: "merge_request.list", params: {project_id: "my-project", state: "opened"}}GET /api/v4/projects/my-project/merge_requests?state=opened200 OK [{id: 1, title: "..."}]{content: [structured JSON + markdown]}"Found 3 open merge requests..."

The call above is the default dynamic surface; with GITLAB_MCP_TOOL_SURFACE=meta the same request is gitlab_merge_request with {action: "list", params: {...}}.

Characteristics:

  • One server process per AI client session
  • Token configured via environment variable
  • Maximum security — token never leaves the local machine
  • Zero network exposure

For team deployments where a single server instance serves multiple users. Each user authenticates with their own GitLab token.

GitLab APIServer PoolHTTP ServerUser BUser AGitLab APIServer PoolHTTP ServerUser BUser AMCP request + Token A + URL XGet/create session for (Token A, URL X)API call with Token AResponseResultMCP responseMCP request + Token B + URL YGet/create session for (Token B, URL Y)API call with Token BResponseResultMCP response

Characteristics:

  • Single server process handles multiple users
  • Per-token+URL session isolation via LRU pool
  • Configurable session limits and timeouts
  • Suitable for team/organization deployments

Start HTTP mode with:

Terminal window
./gitlab-mcp-server --http --http-addr=0.0.0.0:8080 --gitlab-url=https://gitlab.com
# Or, for a single-user local deployment only, let each request name its own
# instance in the GITLAB-URL header (the server refuses to start with neither flag)
./gitlab-mcp-server --http --http-addr=127.0.0.1:8080 --allow-any-gitlab-url

--gitlab-url is required in HTTP mode unless --allow-any-gitlab-url is passed. It can be repeated to publish several instances, in which case the GITLAB-URL header selects among them and is required.

See HTTP Server Mode for detailed configuration.

GitLab MCP Server defines every GitLab operation once and presents it through three interchangeable tool surfaces. A single canonical action catalog is the source of truth, and meta-tools, individual tools, and the dynamic find/execute tools are all projections of it — so behavior and safety stay identical no matter which surface a client uses.

Every ordinary GitLab operation is defined once in the canonical action catalog. The visible tool surfaces are projections of that catalog:

  • Dynamic tools (the default) find and execute the catalog entries by domain.action ID through two visible tools.
  • Meta-tools (GITLAB_MCP_TOOL_SURFACE=meta) group related actions behind domain tools such as gitlab_issue.
  • Individual tools (GITLAB_MCP_TOOL_SURFACE=individual) project one visible MCP tool per action for compatibility and testing.

Because all surfaces share the same catalog entry, schemas, read-only filtering, destructive-action confirmations, safe-mode previews, scope filtering, Markdown formatting, and JSON output stay consistent across modes.

Catalog entries are also tier-aware: each action and input/output schema is tagged with the lowest GitLab edition (free, premium, or ultimate) that exposes it. When a server is built (at startup in stdio mode, per token+URL pool entry in HTTP mode) the tier is resolved (GITLAB_MCP_TIER / --tier, or auto-detection via GET /license, falling back to free), the catalog filter drops premium/ultimate-only actions above it, and pruneSchemaFieldsByTier (in internal/tools/action_catalog.go) prunes the per-field schema entries that are gated by edition. This keeps meta-tools, individual tools, and the dynamic surface consistent: a Premium-only action is hidden everywhere once the tier resolves to free.

Canonical action catalog

Meta-tools
gitlab_issue, gitlab_project, ...
+ gitlab_orbit on GitLab.com Premium/Ultimate

Individual tools
gitlab_issue_list, gitlab_project_create, ...
+ 6 gitlab_orbit_* on GitLab.com Premium/Ultimate

Dynamic tools
gitlab_find_action + gitlab_execute_action
+ orbit.* domain IDs on GitLab.com Premium/Ultimate

Orbit is projected through the same catalog as every other domain, gated to a https://gitlab.com connection on the Premium or Ultimate tier: in meta mode it appears as the gitlab_orbit meta-tool with six actions; in individual mode it appears as six gitlab_orbit_* tools; in dynamic mode its actions are discoverable as orbit.status, orbit.schema, orbit.tools, orbit.dsl, orbit.query, and orbit.graph_status through gitlab_find_action/gitlab_execute_action.

By default (GITLAB_MCP_TOOL_SURFACE unset, or GITLAB_MCP_TOOL_SURFACE=dynamic to make it explicit), the server exposes only gitlab_find_action and gitlab_execute_action. The same canonical action catalog remains available and is shared with meta-tools, so dynamic mode changes discovery rather than GitLab behavior.

Domain ActionSpecs

Canonical action catalog

gitlab_find_action

gitlab_execute_action

Shared ActionRoute

Existing typed handler

GitLab API

Dynamic mode is the default low-token surface and is documented in Dynamic toolset. Meta-tools remain available with GITLAB_MCP_TOOL_SURFACE=meta.

With GITLAB_MCP_TOOL_SURFACE=meta, the server exposes a baseline of 34 meta-tools instead of the individual catalog: 29 catalog-backed tools (28 GitLab domain dispatchers plus gitlab_server), gitlab_discover_project, and the four interactive creation flows. The Premium tier adds 6 meta-tools for 40 total, Ultimate 11 more for 51 total on a self-managed instance, and GitLab.com adds gitlab_orbit (GitLab.com’s Knowledge Graph feature) on Premium and Ultimate alike, for 52 total on GitLab.com Ultimate. Each meta-tool groups related operations:

gitlab_issue

list

get

create

update

delete

move

subscribe

note_create

The AI sends an action parameter to select the operation and nests the operation’s own parameters under params (meta-tools accept only those two top-level keys):

{
"tool": "gitlab_issue",
"arguments": {
"action": "create",
"params": {
"project_id": "my-org/backend",
"title": "Fix N+1 query in /users",
"labels": ["bug", "performance"]
}
}
}

This reduces token usage and improves AI tool selection accuracy compared to exposing each operation as a separate tool.

With GITLAB_MCP_TOOL_SURFACE=individual, all individual tools are exposed (e.g., gitlab_issue_list, gitlab_issue_create): 1085 on self-managed Ultimate, or 1091 on GitLab.com Ultimate with Orbit. This may be useful for testing but is not recommended for production.

The server includes several optional capabilities that can be enabled or disabled:

Interactive creation flows that collect user input step-by-step:

  • Project creation wizard — guided project setup
  • Issue creation wizard — structured issue filing
  • Merge request wizard — assisted MR creation
  • Release creation wizard: tag, name and notes, then confirmation

Requires the AI client to support MCP elicitation capability.

45 read-only MCP resources that provide contextual data:

  • Current user profile and accessible groups
  • Project, group, issue, merge request, pipeline and repository templates
  • The surface-aware gitlab://tools manifest and per-action schemas
  • Static workflow guides (Git workflow, MR hygiene, code review, conventional commits, pipeline troubleshooting)

37 pre-built prompt templates for common workflows:

  • Project health reports
  • Cross-project analysis
  • Team activity summaries
  • Release note generation
  • Git workflow quality checks
  • Audit and compliance reports

Successful tool calls return a dual-format response:

{
"structuredContent": {
"iid": 42,
"title": "Fix N+1 query",
"state": "opened",
"web_url": "https://gitlab.example.com/my-org/backend/-/issues/42",
"next_steps": ["View issue details", "Add labels", "Assign to user"]
},
"content": [
{
"type": "text",
"text": "## Issue #42: Fix N+1 query\n\n**State:** opened\n**Author:** @alice\n..."
}
]
}
  • structuredContent — The handler’s typed output serialized as JSON (the fields above are a subset of the issue output), for the AI to parse and reason about; it includes next_steps hints and conforms to the tool’s declared output schema when one is present
  • content — Formatted Markdown for human display

This dual format ensures the AI can make follow-up decisions while presenting clean output to the user. Tool execution errors set isError: true and can return only Markdown content so clients do not treat the error as a successful structured result.

  • No server-side token storage — In stdio mode, the token exists only in the process environment
  • Per-session isolation — In HTTP mode, each user’s session is isolated in the server pool
  • Read-only mode — Disable all writes with GITLAB_MCP_READ_ONLY=true
  • TLS by default — All GitLab API calls use HTTPS (with opt-in skip for self-signed certs)
  • No data persistence — The server is stateless; no data is stored between requests

Frequently asked questions

What is the difference between stdio and HTTP transport modes?

Stdio mode is the default for single-user setups: the AI client spawns the server as a child process and communicates over stdin/stdout using JSON-RPC, with the token supplied as an environment variable that never leaves the local machine. HTTP mode serves multiple users from one process, isolating each user's session by token and URL in an LRU pool with configurable session limits and timeouts. Both modes expose the same tools and call the same GitLab APIs; they differ only in how the AI client connects.

What does GitLab MCP Server do?

GitLab MCP Server is a single static binary that sits between an AI client and a GitLab instance. It receives MCP tool calls, translates them into authenticated GitLab REST API v4 or GraphQL requests, executes them, and returns a dual-format response: structured JSON for the AI to reason about and formatted Markdown for the user. The server adds no GitLab features of its own — it exposes existing GitLab operations as MCP tools through the Model Context Protocol.

What is the canonical action catalog?

The canonical action catalog is the single internal definition of every ordinary GitLab operation. All three visible tool surfaces — meta-tools, individual tools, and the dynamic find/execute tools — are projections of this catalog, so they share identical schemas, read-only filtering, destructive-action confirmations, safe-mode previews, scope filtering, and Markdown formatting. Catalog entries are tier-aware: each action is tagged with the lowest GitLab edition (free, premium, or ultimate) that exposes it, and premium or ultimate-only actions are pruned when the resolved tier is lower.

How does GitLab MCP Server keep my GitLab token secure?

GitLab MCP Server never stores tokens server-side. In stdio mode the token exists only in the process environment and never leaves the local machine. In HTTP mode each user's session is isolated in the server pool by token and URL. All GitLab API calls use HTTPS by default, with opt-in skipping for self-signed certificates, and the server is stateless — no data is persisted between requests. Read-only mode (GITLAB_MCP_READ_ONLY=true) disables every mutating operation.

The architecture above is the result of a handful of recorded decisions. Each Architectural Decision Record states the problem, the alternatives that were rejected, and the consequences accepted in exchange — useful when you want to know why the server is shaped this way rather than just how.

DecisionWhat it settledTrade-off accepted
ADR-0004 — modular sub-packagesOne Go package per GitLab domain under internal/tools/, rather than one large packageMore packages to navigate, in exchange for isolated tests and no import cycles
ADR-0005 — meta-tool consolidationGrouping operations into domain dispatchers that route on an action parameterAn extra indirection in the call shape, in exchange for a tool list clients can actually load
ADR-0011 — dynamic toolsetMaking find/execute the default surface instead of listing every toolOne discovery call per task, in exchange for a 448× smaller startup context
ADR-0014 — catalog-first runtimeProjecting all three surfaces from one canonical action catalogA build-time projection step, in exchange for identical behaviour and safety across surfaces
ADR-0007 — rich error semanticsReturning actionable, classified errors instead of raw API failuresMore error-handling code per tool, in exchange for errors a model can act on

The full ADR index covers the remaining decisions, including GraphQL migration strategy and the polled resource-subscriptions design (ADR-0015).