Skip to content

Client configuration

Every MCP client starts or reaches the same server. What differs from one client to the next is the file it reads, the key it keeps servers under, and which of the server’s three ways to connect it can use. This page has the entry for each client in each way it supports. Install the server first (Choose a path); the Quick Start takes one client through from token to first prompt.

ModeHow the client reaches the serverWhere the token livesBest for
StdioIt starts gitlab-mcp-server as a child process and speaks JSON-RPC over its standard input and outputGITLAB_TOKEN in the entry’s environment, in ~/.gitlab-mcp-server.env, or in the file GITLAB_MCP_ENV_FILE namesOne person on one machine
HTTP with a tokenIt sends every request to a server started with --http, in the default legacy authentication modeA PRIVATE-TOKEN or Authorization: Bearer header on every requestA shared server, clients without OAuth
HTTP with OAuthIt sends every request to a server started with --auth-mode=oauth, finds GitLab through the RFC 9728 metadata and signs the user in in a browserThe access token the client obtains from GitLab, sent as Authorization: Bearer; a personal access token there works tooA shared server where nobody copies a key

Before you pick an entry:

  • Stdio needs nothing else running. GITLAB_URL defaults to https://gitlab.com; add it beside the token only for a self-managed instance. The server reads ~/.gitlab-mcp-server.env for any value its environment does not carry, one KEY=value per line, so an entry with no env block works once that file holds the token. Most entries below show the block with a placeholder; where a client can prompt for the token or expand an environment variable, its entry does that instead of writing the token into the file.
  • An HTTP entry needs a server running in HTTP mode, started as in HTTP Server Mode. The examples use https://mcp.example.com/mcp. The server answers MCP at /, at /mcp and under the path of --public-url, but in OAuth mode a client must be configured with exactly the --public-url value, because it discards metadata whose resource differs from the URL it used (OAuth mode).
  • GITLAB-URL is needed when the deployment publishes several instances, or none (--allow-any-gitlab-url), and then on every request; a deployment that publishes one ignores the header. See Publishing more than one instance.
  • Docker can be the stdio command. docker run -i --rm -e GITLAB_TOKEN ghcr.io/jmrplens/gitlab-mcp-server:latest replaces the binary’s path in a stdio entry whose env block carries GITLAB_TOKEN: each -e with no value forwards that variable from the environment the client starts docker with. A container never reads the host’s ~/.gitlab-mcp-server.env, so an entry that leaves the token in that file (Claude Code’s stdio entry, JetBrains, GitLab Duo) needs GITLAB_TOKEN added to its env block, or exported where the client starts, before it can run the image. Keep -i and pass no transport flag: the image reads its transport from standard input, and without -i it gets /dev/null and starts an HTTP listener while the client waits. Do not publish port 8080 in that mode (Docker).
  • A token never goes on a command line. No registration command on this page names one. It lives in the entry’s environment block, an input prompt, an environment variable the client expands, or the server’s own file.
ClientStdioHTTP with a tokenHTTP with OAuthClient documentation
VS Code (GitHub Copilot)YesYesYesMCP configuration
Claude CodeYesYesYesMCP
Claude Desktop and claude.aiClaude Desktop onlyPublic HTTPS URL onlyPublic HTTPS URL onlyCustom connectors
CursorYesYesYesMCP
Windsurf (Devin Desktop)YesYesNot verifiedMCP
JetBrains IDEsYesThrough mcp-remoteNoMCP
ZedYesYesNoMCP
KiroYesYesYesMCP configuration
ClineYesYesNoConfiguring MCP servers
ContinueYesYesNoMCP
OpenCodeYesYesNot verifiedMCP servers
OpenAI CodexYesYesYesMCP
Gemini CLIYesYesYesMCP servers
LM StudioYesYesYesRemote MCP and OAuth
GitLab Duo Agent PlatformYesThrough mcp-remoteNoGitLab MCP clients
mcp-remoteStarted over stdioYesYesREADME

“No” in the OAuth column means the client documents no way to give it the Application ID of a GitLab OAuth application you registered. Some of those clients run no OAuth flow at all; the others fall back to dynamic client registration, which GitLab answers with a token carrying only its mcp scope, and this server refuses that token because it reaches none of the API it calls (Dynamic client registration and the mcp scope). “Not verified” means the client takes an Application ID but does not document the callback it sends, which GitLab must have registered exactly. Those clients still work over HTTP: OAuth mode accepts a personal access token sent as Authorization: Bearer, unless the deployment admits only its own application, and legacy mode accepts PRIVATE-TOKEN as well. The client facts on this page were checked against each client’s documentation on 2026-10-05.

Every OAuth entry needs the GitLab OAuth application of OAuth application: its Application ID goes in the client, and the callback the client sends goes in the application’s redirect URIs, listed per client in Step 2. The client asks GitLab for the scope the deployment advertises, api, or read_api on a deployment started with --read-only or --safe-mode, and the application must have that scope checked (Which scope to check).

The public instance at https://mcp.jmrp.io/gitlab takes the same entries as a deployment of your own, with two differences: the URL is fixed, and its GitLab OAuth application already exists, so you point the client at that application’s ID instead of creating one. The server card publishes the ID next to a copy-paste entry for Claude Code, Cursor and VS Code. For Claude Code the OAuth entry is:

Terminal window
claude mcp add gitlab \
--transport http \
--client-id CLIENT_ID_FROM_THE_SERVER_CARD \
--callback-port 8090 \
https://mcp.jmrp.io/gitlab

Every OAuth entry below works the same way with that URL and the card’s ID in place of yours, and Claude Desktop adds it as a custom connector with the card’s ID as its OAuth client. Without OAuth, send a GitLab.com personal access token as Authorization: Bearer; the endpoint runs in OAuth mode, so PRIVATE-TOKEN is refused there, and GITLAB-URL is ignored because the instance is fixed to https://gitlab.com. A read_api token, or a client pinned to the read_api scope, is admitted and served the read-only tool surface. The endpoint’s properties and caveats are on Hosted endpoint.

VS Code reads .vscode/mcp.json in the workspace, or the mcp.json of your user profile, and keeps servers under servers rather than mcpServers. An inputs entry with "password": true prompts for the token and keeps it out of the file.

.vscode/mcp.json
{
"servers": {
"gitlab": {
"type": "stdio",
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "${input:gitlab-token}"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "gitlab-token",
"description": "GitLab Personal Access Token",
"password": true
}
]
}

The same entry with the Docker image as the command; each -e with no value forwards that variable from the entry’s env block into the container:

.vscode/mcp.json
{
"servers": {
"gitlab": {
"type": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITLAB_TOKEN",
"-e",
"GITLAB_MCP_SKIP_TLS_VERIFY",
"ghcr.io/jmrplens/gitlab-mcp-server:latest"
],
"env": {
"GITLAB_TOKEN": "${input:gitlab-token}",
"GITLAB_MCP_SKIP_TLS_VERIFY": "false"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "gitlab-token",
"description": "GitLab Personal Access Token",
"password": true
}
]
}

Claude Code keeps a server for the current project in ~/.claude.json by default; --scope project writes it to .mcp.json in the project root instead, and --scope user makes it available in every project.

Terminal window
# Prompts for the token without echoing it, so it stays out of shell history
printf 'GitLab token: ' && read -rs t && printf 'GITLAB_TOKEN=%s\n' "$t" > ~/.gitlab-mcp-server.env && unset t && echo
chmod 600 ~/.gitlab-mcp-server.env
claude mcp add gitlab \
--transport stdio \
-- /path/to/gitlab-mcp-server

Add GITLAB_URL=https://gitlab.example.com to the same file for a self-managed instance, or write the whole file in an editor. Neither command names the token, so it stays out of argv, out of shell history and out of Claude Code’s own configuration file.

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS), %APPDATA%\Claude\claude_desktop_config.json (Windows) or ~/.config/Claude/claude_desktop_config.json (the Linux beta):

claude_desktop_config.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
}
}

Cursor reads .cursor/mcp.json in the project root, or ~/.cursor/mcp.json for every project. It expands ${env:NAME} in command, args, env, url and headers; VS Code’s ${input:...} prompts are not part of its syntax.

.cursor/mcp.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "${env:GITLAB_TOKEN}"
}
}
}
}

Export GITLAB_TOKEN in the environment Cursor starts from. To keep the token in ~/.gitlab-mcp-server.env instead, leave the env block out: the server reads that file only for variables its environment does not carry, and a variable the entry sets, even to an empty value, is carried.

Windsurf was renamed Devin Desktop in June 2026. The file below is the one Windsurf, and Devin Desktop’s Cascade agent, read MCP servers from (Cascade’s MCP page). Devin Desktop 3.9.19 removed Cascade, and the agent it kept, Devin Local, reads MCP servers from the Devin CLI’s files instead: ~/.config/devin/mcp_config.json (%APPDATA%\devin\mcp_config.json on Windows) for every project, or .devin/mcp_config.json in one (Devin’s MCP configuration). Those keep servers under mcpServers too, with the same stdio entry, but a remote server takes url rather than serverUrl. The Cascade file expands ${env:VAR_NAME} in command, args, env, serverUrl, url and headers, which keeps the token out of it; an unset variable expands to an empty string.

~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "${env:GITLAB_TOKEN}"
}
}
}
}

Export GITLAB_TOKEN in the environment Devin Desktop starts from, or leave the env block out and keep the token in ~/.gitlab-mcp-server.env.

Cascade’s documentation names no setting for an OAuth client ID. Devin Local takes a pre-registered one in a remote entry’s oauthClientId, but its documentation does not say which callback it sends, and GitLab must have that callback registered exactly. Until you have checked which one your version sends, against a deployment in OAuth mode send a personal access token as "Authorization": "Bearer ${env:GITLAB_TOKEN}".

The AI Assistant of IntelliJ IDEA, GoLand, PyCharm and the other JetBrains IDEs adds a server from Settings > Tools > AI Assistant > Model Context Protocol (MCP):

  1. Select Add, then the STDIO transport.
  2. Paste the JSON configuration below.
  3. Choose the server level: the current project, or every project.
  4. Select OK, then Apply.
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"args": []
}
}
}

JetBrains documents the entry as a command and its args, so put the token in ~/.gitlab-mcp-server.env, with GITLAB_URL beside it for a self-managed instance. The server reads that file on its own.

JetBrains also connects to a remote server over streamable HTTP or SSE, but documents that entry as a url alone, with no header and no OAuth flow, and this server needs a credential on every request. To reach an HTTP deployment, add mcp-remote as an STDIO server, which carries the token for it.

Zed keeps servers under context_servers in its settings.json.

settings.json
{
"context_servers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"args": [],
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
}
}

Kiro reads .kiro/settings/mcp.json in the workspace, or ~/.kiro/settings/mcp.json for every workspace. It expands ${VARIABLE_NAME}, but only for variables you have approved.

.kiro/settings/mcp.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"args": [],
"env": {
"GITLAB_TOKEN": "${GITLAB_TOKEN}"
}
}
}
}

The Kiro IDE asks you to approve GITLAB_TOKEN the first time it expands it; the Kiro CLI reads it from the shell you start it from.

In the Cline panel, select the MCP Servers icon, open the Configure tab and select Configure MCP Servers; that opens the settings file. In VS Code the file is:

  • macOS: ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
  • Linux: ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
  • Windows: %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json
cline_mcp_settings.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
}
}

Continue reads ~/.continue/config.yaml, and a workspace can add servers as YAML files in .continue/mcpServers/. Reload the Continue window after editing.

~/.continue/config.yaml
mcpServers:
- name: gitlab
command: /path/to/gitlab-mcp-server
env:
GITLAB_TOKEN: glpat-xxxxxxxxxxxxxxxxxxxx

OpenCode reads opencode.json in the project root, or ~/.config/opencode/opencode.json for every project. Its servers sit under mcp rather than mcpServers, each with a type of local or remote, and it expands {env:NAME} in a value.

The command is an array, and the environment block is called environment:

opencode.json
{
"mcp": {
"gitlab": {
"type": "local",
"command": ["/path/to/gitlab-mcp-server"],
"enabled": true,
"environment": {
"GITLAB_TOKEN": "{env:GITLAB_TOKEN}"
}
}
}
}

OpenCode’s oauth block also takes a clientId, but its documentation does not say which callback it sends, and GitLab must have that callback registered exactly. Until you have checked which one your version sends, use a token.

Codex reads ~/.codex/config.toml. default_tools_approval_mode = "approve" pre-approves the server’s tools: without it, Codex asks before every tool that is not read-only (on the default surface, gitlab_execute_action), and a non-interactive codex exec run cancels those calls.

~/.codex/config.toml
[mcp_servers.gitlab]
command = "/path/to/gitlab-mcp-server"
args = ["--transport", "stdio"]
startup_timeout_sec = 60
default_tools_approval_mode = "approve"
[mcp_servers.gitlab.env]
GITLAB_TOKEN = "glpat-xxxxxxxxxxxxxxxxxxxx"

Codex gives a server 10 seconds to start unless startup_timeout_sec says otherwise. The server answers the handshake at once but holds tools/list until its catalog is built, which comes after its first requests to GitLab, so a slow instance can need longer. Add GITLAB_URL to the env table for a self-managed instance.

What else to know about Codex:

  • The server recognizes a Codex session from the client name it reports and rounds the fractional priority of content annotations to 0 or 1, which the Codex builds bundled with ChatGPT.app require. Everything else is delivered unchanged, and GITLAB_MCP_CLIENT_COMPAT=off turns the rewrite off (Per-client compatibility profiles). Over HTTP, a Codex speaking protocol 2025-11-25 or earlier to the default stateless transport reports no client name on the calls after initialize, since each one is a session of its own, so the server recognizes those calls by the codex-mcp-client/ User-Agent Codex sends on every request instead.
  • When a result carries structuredContent, Codex hands its model only that JSON and drops the Markdown content blocks (openai/codex#10334).
  • Keep GITLAB_MCP_META_PARAM_SCHEMA at its opaque default: Codex trims tool schemas larger than about 5 KB.
  • In its default protocol mode Codex reads only the first page of tools/list. The server lists up to 2000 tools per page, more than its largest catalog, so every tool surface arrives in one page.

Gemini CLI reads ~/.gemini/settings.json, or .gemini/settings.json in a project. It expands $VAR and ${VAR} in the values of env.

~/.gemini/settings.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "$GITLAB_TOKEN"
}
}
}
}

LM Studio follows Cursor’s mcp.json notation (MCP in LM Studio). Open it from the Program tab of the right-hand sidebar with Install > Edit mcp.json. The one-click button on the Quick Start writes the Docker form of the stdio entry.

mcp.json
{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
}
}

GitLab Duo Agentic Chat and the Software Development Flow are MCP clients too, in VS Code and VSCodium through the GitLab extension, in the JetBrains IDEs through the GitLab Duo plugin, and in the GitLab Duo CLI, all through the GitLab Language Server (GitLab MCP clients). Duo has GitLab tools of its own; this server adds its whole action catalog beside them.

First allow it: in the top-level group where GitLab Duo is configured, select Allow external MCP tools in the group’s GitLab Duo settings. Then add the server to .gitlab/duo/mcp.json in the workspace, or to ~/.gitlab/duo/mcp.json (%APPDATA%\GitLab\duo\mcp.json on Windows) for every workspace, and restart the IDE or the CLI.

~/.gitlab/duo/mcp.json
{
"mcpServers": {
"gitlab-extended": {
"type": "stdio",
"command": "/path/to/gitlab-mcp-server",
"args": []
}
}
}

Give the command an absolute path, and put the token in ~/.gitlab-mcp-server.env, which the server reads on its own. Duo asks before each tool call; an approvedTools list in the entry, or true, pre-approves tools.

mcp-remote is a small Node.js program that a client starts as a stdio server and that forwards everything to a remote one. It is the way to reach an HTTP deployment from a client that connects only over stdio, or only without a header, such as Claude Desktop’s configuration file, a JetBrains IDE or GitLab Duo. mcp-remote expands ${NAME} in its arguments from its own environment, so the token sits in the entry’s env block and never on the command line:

{
"mcpServers": {
"gitlab": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.example.com/mcp",
"--header",
"Authorization:${GITLAB_AUTH}"
],
"env": {
"GITLAB_AUTH": "Bearer glpat-xxxxxxxxxxxx"
}
}
}
}

Write the header with no space after the colon and keep the space inside the variable: some clients do not escape a space inside args when they start npx. For a deployment in legacy mode, "PRIVATE-TOKEN:${GITLAB_TOKEN}" with the bare token in GITLAB_TOKEN works the same way. For OAuth, --static-oauth-client-info '{"client_id":"YOUR_GITLAB_APPLICATION_ID"}' gives mcp-remote the Application ID; register the callback it listens on, which its README describes, exactly.