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.
Overview
Section titled “Overview”The server is a single static binary that:
- Receives MCP tool calls from the AI client (e.g., “list open merge requests”)
- Translates them into GitLab REST API v4 or GraphQL requests with proper authentication
- Executes the API calls against your GitLab instance
- Returns results in dual format: structured JSON for the AI to reason about, and formatted Markdown for display to the user
Transport modes
Section titled “Transport modes”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.
Stdio mode (default)
Section titled “Stdio mode (default)”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.
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
HTTP mode (multi-user)
Section titled “HTTP mode (multi-user)”For team deployments where a single server instance serves multiple users. Each user authenticates with their own GitLab token.
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:
./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.
Tool architecture
Section titled “Tool architecture”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.
Canonical action catalog
Section titled “Canonical action catalog”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.actionID through two visible tools. - Meta-tools (
GITLAB_MCP_TOOL_SURFACE=meta) group related actions behind domain tools such asgitlab_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.
Tier-gating
Section titled “Tier-gating”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.
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.
Dynamic toolset (default)
Section titled “Dynamic toolset (default)”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.
Dynamic mode is the default low-token surface and is documented in Dynamic toolset. Meta-tools remain available with GITLAB_MCP_TOOL_SURFACE=meta.
Meta-tool mode
Section titled “Meta-tool mode”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:
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.
Individual tool mode
Section titled “Individual tool mode”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.
Optional components
Section titled “Optional components”The server includes several optional capabilities that can be enabled or disabled:
Elicitation (interactive wizards)
Section titled “Elicitation (interactive wizards)”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.
Resources
Section titled “Resources”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://toolsmanifest and per-action schemas - Static workflow guides (Git workflow, MR hygiene, code review, conventional commits, pipeline troubleshooting)
Prompts
Section titled “Prompts”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
Tool output format
Section titled “Tool output format”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 includesnext_stepshints and conforms to the tool’s declared output schema when one is presentcontent— 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.
Security model
Section titled “Security model”- 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.
Design decisions
Section titled “Design decisions”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.
| Decision | What it settled | Trade-off accepted |
|---|---|---|
| ADR-0004 — modular sub-packages | One Go package per GitLab domain under internal/tools/, rather than one large package | More packages to navigate, in exchange for isolated tests and no import cycles |
| ADR-0005 — meta-tool consolidation | Grouping operations into domain dispatchers that route on an action parameter | An extra indirection in the call shape, in exchange for a tool list clients can actually load |
| ADR-0011 — dynamic toolset | Making find/execute the default surface instead of listing every tool | One discovery call per task, in exchange for a 448× smaller startup context |
| ADR-0014 — catalog-first runtime | Projecting all three surfaces from one canonical action catalog | A build-time projection step, in exchange for identical behaviour and safety across surfaces |
| ADR-0007 — rich error semantics | Returning actionable, classified errors instead of raw API failures | More 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).
External references
Section titled “External references”- Model Context Protocol specification — the protocol the server speaks to AI clients
- JSON-RPC 2.0 specification — the wire format used over stdio and HTTP
- GitLab REST API v4 and GraphQL API — the GitLab API surfaces the server translates tool calls into
modelcontextprotocol/go-sdk— the official Go MCP SDK that implements tool, resource and prompt registration and both transportsgitlab-org/api/client-go— GitLab’s own Go API client, used for every REST call; domains it does not wrap are issued as raw GraphQLtiktoken-go/tokenizer— the pure-Go cl100k_base tokenizer behind the published token-footprint measurements