# gitlab

> Free hosted GitLab MCP endpoint, no account beyond your own gitlab.com token, never written to disk. Over 700 operations: projects, MRs, pipelines.
> This page as plain markdown. Index: https://mcp.jmrp.io/llms.txt

Canonical: https://mcp.jmrp.io/servers/gitlab/
Language: en
Alternate: https://mcp.jmrp.io/es/servers/gitlab/index.md
Updated: 2026-09-22T17:10:10+02:00
License: https://mcp.jmrp.io/license/


## Overview

- Endpoint: `https://mcp.jmrp.io/gitlab` (POST only; GET answers 401)
- Transport: streamable HTTP, stateless JSON-RPC 2.0
- Version: 3.1.0
- Authentication: `Authorization`
- Health: `https://mcp.jmrp.io/gitlab/health`
- Repository: https://github.com/jmrplens/gitlab-mcp-server
- Documentation: https://jmrp.io/docs/gitlab-mcp-server

## Connect it to your client

### Sign in with OAuth (recommended)

Your client opens gitlab.com in the browser, you authorize it there, and it stores the token itself — you never paste one. The client ID has to be configured explicitly: without it these clients fall back to dynamic registration, which GitLab answers with a scope this server cannot use.

**Claude Code**

```sh
claude mcp add gitlab --transport http \
  --client-id c9431f281376dab9390349f60bed0503285786e19577df14a9c291c588b85941 \
  --callback-port 8090 \
  https://mcp.jmrp.io/gitlab
```

**Cursor — ~/.cursor/mcp.json**

```json
{
  "mcpServers": {
    "gitlab": {
      "type": "http",
      "url": "https://mcp.jmrp.io/gitlab",
      "oauth": {
        "clientId": "c9431f281376dab9390349f60bed0503285786e19577df14a9c291c588b85941",
        "scopes": [
          "api"
        ]
      }
    }
  }
}
```

**VS Code — .vscode/mcp.json**

```json
{
  "servers": {
    "gitlab": {
      "type": "http",
      "url": "https://mcp.jmrp.io/gitlab",
      "oauth": {
        "clientId": "c9431f281376dab9390349f60bed0503285786e19577df14a9c291c588b85941",
        "scopes": [
          "api"
        ]
      }
    }
  }
}
```

### Or paste a token

**Claude Code**

```sh
claude mcp add --transport http gitlab \
  https://mcp.jmrp.io/gitlab \
  --header "Authorization: Bearer <your token>"
```

**Cursor — ~/.cursor/mcp.json**

```json
{
  "mcpServers": {
    "gitlab": {
      "url": "https://mcp.jmrp.io/gitlab",
      "headers": {
        "Authorization": "Bearer ${env:GITLAB_TOKEN}"
      }
    }
  }
}
```

**VS Code — .vscode/mcp.json**

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "gitlab-token",
      "description": "Your gitlab.com credential, sent as Bearer: an OAuth access token, or a personal access token used the same way. Never written to disk or logged on the server.",
      "password": true
    }
  ],
  "servers": {
    "gitlab": {
      "type": "http",
      "url": "https://mcp.jmrp.io/gitlab",
      "headers": {
        "Authorization": "Bearer ${input:gitlab-token}"
      }
    }
  }
}
```

## Before you rely on this

This endpoint is a personal service, run by one person and offered as-is: no SLA, no support channel, and no promise it is still here — or unchanged — next week. Both servers are open source and ship as a single static binary, so anything you cannot afford to lose is better run on your own instance.

It adds no quota of its own beyond the site-wide one: every call is spent against gitlab.com's limits, under your own token.

Behind the endpoint are three instances of this server. A consistent hash keeps sending the same client back to the same one, and each instance leaves for the outside world through its own exit node, in Spain or the United Kingdom: How a request is routed, hop by hop: https://mcp.jmrp.io/internals/

What is logged and for how long, where a request appears to come from, and the legal footing under all of it are set out in full: Privacy, logging and legal position: https://mcp.jmrp.io/policies/

## Usage instructions

What the server itself tells every client on connect (server/discover), quoted verbatim.

gitlab-mcp-server exposes GitLab projects, merge requests, issues, branches, tags, releases, repositories, commits, files, groups, members, and uploads.

FINDING TOOLS. This server exposes two tools that reach the whole GitLab API:
1. Call gitlab_find_action with a natural-language description of the task to get matching action IDs and their input schemas.
2. Call gitlab_execute_action with that action ID, its parameters under 'params'.
3. Every action="..." named below is a canonical action ID: pass it to gitlab_execute_action directly, no find step needed.

PROJECT DISCOVERY. To find the project_id needed for most operations:
1. Read the .git/config file from the workspace to find [remote "origin"] url = ...
2. Call action="discover_project.resolve" with that URL to get the project_id.
3. Alternatively, use action="project.list" (owned=true) or action="search.projects" to find projects by name.

DEFAULT BRANCH. When generating URLs to repository files or branches:
1. Call action="project.get" to retrieve the project metadata, which includes the default_branch field.
2. ALWAYS use the returned default_branch value (e.g. develop, master) instead of assuming 'main'.
3. Projects can use any branch as default, so NEVER hardcode 'main' in URLs.

PACKAGE + RELEASE WORKFLOW. When uploading packages and linking them to releases:
1. Preferred: Use action="package.publish_and_link" to upload a file and create the release link in one step.
2. Alternative: Use action="package.publish" first, then use the 'url' field from its response as the URL for action="release.link_create".
3. NEVER construct package download URLs manually. Always use the actual URL returned by the publish tool.
4. RELEASE LINK NAMING: The link_name MUST be the exact filename (e.g. 'checksums.txt.asc'), NEVER add descriptive suffixes like '(GPG signature)'. go-selfupdate and other tools match asset names exactly.

RELEASE CREATION. When creating releases:
1. You do NOT need to create the tag first. Provide 'ref' (branch or SHA) in action="release.create" and GitLab auto-creates the tag.
2. The response includes 'assets_sources' with auto-generated tar.gz/zip archive URLs. Use those, never construct source archive URLs.
3. Use 'tag_message' to create an annotated tag instead of a lightweight one.

ID vs IID. GitLab uses two identifiers for issues and merge requests:
1. IID is the project-scoped number shown in URLs and UI (e.g. issue #3, MR !5). Most operations expect IID.
2. ID is the global numeric identifier. Only use action="issue.get_by_id" when you have a global ID from another API response.

WATCHING RESOURCES. Instead of re-reading a resource in a loop to detect change:
1. Single-object resources (a pipeline, an issue, a merge request, a file, a wiki page, ...) can be watched for change notifications via MCP subscriptions/listen, which protocol revision 2026-07-28 introduced (a client speaking an earlier revision cannot watch resources on this transport: the legacy resources/subscribe is refused here, because each request's session ends with its response); collections (issue lists, branch lists) cannot.
2. Example: subscribe to gitlab://project/{project_id}/pipelines/latest to be notified when a pipeline's state changes, instead of polling it yourself. The server watches GitLab and sends notifications/resources/updated only when the content actually changed.

## Tools

- 2 tools
- 37 prompts
- 8 resources
- 37 resource templates

## Action catalog

The tools above front a catalog of 765 actions across 29 domains: they are found and called through those tools, not exposed one by one. Counted with a Free-tier token — both the tier and the token's permissions move the figure. Index: https://mcp.jmrp.io/servers/gitlab/actions.json. One reference page per domain under https://mcp.jmrp.io/servers/gitlab/actions/.

## Full catalog

Every tool, prompt, resource and template of this server — with what each one takes and returns — is listed in https://mcp.jmrp.io/llms-full.txt, and served live by the server itself at `https://mcp.jmrp.io/gitlab/.well-known/mcp/server-card.json`.
