← Browse

@jmrplens/gitlab-mcp-server

gitlab-mcp-server — GitLab MCP Server in Go

instructionscopilot

Install

agr install @jmrplens/gitlab-mcp-server --target copilot

Writes 1 file into .github/copilot-instructions.md, pinned to git-f9030482.

  • .github/copilot-instructions.md

Document

gitlab-mcp-server — GitLab MCP Server in Go

Project Overview

This project implements a Model Context Protocol (MCP) server that exposes GitLab operations as MCP tools. It is written in Go using the official github.com/modelcontextprotocol/go-sdk package and communicates with the GitLab REST API v4 (primary) and GraphQL API (for domains without REST coverage — see ADR-0006).

Architecture

  • Language: Go 1.26.5
  • MCP SDK: github.com/modelcontextprotocol/go-sdk/mcp v1.7.0
  • GitLab Client: gitlab.com/gitlab-org/api/client-go/v2 v2.42.0 (official client, migrated from deprecated xanzy/go-gitlab)
  • Transport: stdio (primary), HTTP (optional)
  • Cross-platform: Windows, Linux & macOS, amd64 & arm64

Project Structure

gitlab-mcp-server/
├── cmd/                    # 20 dev utility binaries — see docs/development/cmd-utilities.md for the full reference
│   ├── server/             # MCP server entry point (+ --shutdown flag)
│   ├── audit_1to1/         # 1:1 SDK↔API parity audit (-scope structs|actions|metadata; -validate-docs)
│   ├── audit_catalog_first/        # Catalog-first registration invariants (ADR-0004)
│   ├── audit_discovery_completeness/ # Discovery-metadata quality audit (META-001)
│   ├── audit_doc_coverage/ # docs/tools/*.md vs catalog coverage gaps (DOC-002)
│   ├── audit_dynamic_aliases/ # Dynamic-toolset alias governance
│   ├── audit_edition_tier/ # Doc-grounded licensing tier vs binary gating
│   ├── audit_surface_quality/ # MCP surface metadata + output quality (-view; was audit_tools + audit_output)
│   ├── audit_tokens/       # Token overhead; -footprint, --compare-schemas (was audit_meta_schema); cl100k_base tokenizer (tokens.go)
│   ├── audit_metrics/      # MCP tool/resource/prompt metrics
│   ├── audit_test_names/   # Test naming convention (+ -apply/-dry-run)
│   ├── audit_string_dupes/ # Duplicated string literals missing constants
│   ├── godoc_tool/         # Godoc auditor + fixer (audit/fix; was audit_godocs + add_docs)
│   ├── format_md_tables/   # Markdown pipe-table normalizer
│   ├── gen_action_catalog_manifest/ # ActionSpec group-builder manifest
│   ├── gen_docker_tools/   # Docker MCP Registry tools.json
│   ├── gen_llms/           # llms.txt / llms-full.txt
│   ├── gen_stats/          # README repository-statistics section (was inside gen_readme)
│   ├── gen_testing_docs/   # docs/development/testing/testing.md test-metrics block
│   └── eval_mcp_surfaces/  # Model-behavior evaluation across MCP surfaces
├── internal/
│   ├── config/             # Configuration loading (.env, flags)
│   ├── gitlab/             # GitLab API client wrapper
│   ├── serverpool/         # HTTP mode: per-token+URL server pool & LRU cache
│   ├── toolutil/           # Shared tool utilities (errors, pagination, markdown, logging)
│   ├── testutil/           # Shared test helpers (NewTestClient, RespondJSON)
│   ├── tools/              # Tool orchestration layer + 176 internal/tools packages
│   │   ├── register.go     # RegisterAll() — projects individual tools from the canonical action catalog
│   │   ├── register_meta.go # RegisterAllMeta() — registers catalog-backed meta groups and standalone surfaces
│   │   ├── dynamic/        # Low-token dynamic find/execute surface
│   │   ├── branches/       # Branch & protected branch tools
│   │   ├── commits/        # Commit tools
│   │   ├── issues/         # Issue CRUD tools
│   │   ├── mergerequests/  # Merge request CRUD tools
│   │   ├── projects/       # Project CRUD tools
│   │   └── ...             # 176 internal/tools packages total
│   ├── resources/          # MCP resource implementations
│   └── prompts/            # MCP prompt implementations
├── docs/                   # Documentation, ADRs, specs
│   ├── adr/
│   ├── spec/
│   ├── oauth-app-setup.md  # Creating GitLab OAuth applications for MCP clients
│   └── ide-configuration.md # Per-IDE MCP JSON configuration (stdio, HTTP legacy, OAuth)
├── plan/                   # Implementation plans
├── .github/                # Copilot agents, skills, instructions
├── .env                    # Local dev secrets (gitignored)
├── .gitignore
├── go.mod
├── go.sum
├── Makefile
└── README.md

Development Conventions

Go Standards

  • Follow idiomatic Go and the repository's consolidated golangci-lint configuration (goimports, gofumpt, gci, govet, staticcheck, gosec, and related checks)
  • Prefer standard library over third-party when equivalent
  • All exported types and functions must have doc comments
  • Error wrapping with fmt.Errorf("context: %w", err)
  • Use context.Context consistently for cancellation/timeouts
  • Table-driven tests with t.Run() subtests

MCP Patterns

  • Each GitLab operation is defined once as a typed ActionSpec and projected into meta, dynamic, gitlab://tools, and individual surfaces
  • Use jsonschema struct tags for tool input documentation
  • Register runtime surfaces from the canonical action catalog only; ordinary GitLab actions must not add package-local RegisterTools functions or package-level meta registration paths
  • Resources for read-only data (project info, user info, etc.)
  • Graceful shutdown via signal handling
  • Dynamic mode (TOOL_SURFACE=dynamic) exposes gitlab_find_action and gitlab_execute_action over the canonical action catalog shared with meta-tools. It is the default tool surface; set TOOL_SURFACE=meta for consolidated domain meta-tools.
  • When adding GitLab actions, add or update domain-local ActionSpecs and the generated/audited catalog manifest. Meta-tools, dynamic find/execute, gitlab://tools resources, LLM files, and individual tool projection consume that catalog. Do not add package-local RegisterTools functions for ordinary GitLab API actions.
  • For the detailed developer architecture of individual tools, meta-tools, dynamic mode, and the canonical action core, see docs/development/tool-surfaces-and-action-core.md.

GitLab Integration

  • Stdio mode uses GITLAB_URL; HTTP mode uses --gitlab-url when fixed, or per-request GITLAB-URL headers when omitted
  • Authentication via GITLAB_TOKEN (Personal Access Token)
  • Self-signed TLS certificates: skip verification when GITLAB_SKIP_TLS_VERIFY=true
  • All API calls must respect context.Context for cancellation
  • Rate limiting awareness and retry logic

Testing

  • Unit tests for every tool handler
  • Use httptest for mocking GitLab API responses in unit tests
  • Test naming: TestToolName_Scenario_ExpectedResult
  • Aim for >80% coverage on tool handlers
  • After completing a test-focused phase or milestone, run go run ./cmd/gen_testing_docs/ or make gen-testing-docs to refresh docs/development/testing/testing.md, then verify with go run ./cmd/gen_testing_docs/ --check

Verification After Changes

After implementing changes, run targeted analysis on the changed files/packages only:

# Go files — run on affected packages (replace path with changed package)
go test ./internal/tools/{domain}/ -count=1
golangci-lint run --build-tags e2e ./internal/tools/{domain}/

# Markdown files — run on specific changed .md files
npx markdownlint-cli2 path/to/changed.md

# README.md/docs tables — normalize pipe tables, or verify with --check
go run ./cmd/format_md_tables/
go run ./cmd/format_md_tables/ --check
  • 3 analysis gates available: golangci-lint (v2; includes Go linters and formatters such as goimports, gofumpt, gci, govet, modernize, gosec, and staticcheck), govulncheck, and markdownlint-cli2
  • Configuration: .golangci.yml (Go linters/formatters), .markdownlint-cli2.jsonc (Markdown rules)
  • Markdown table formatting: when creating or editing pipe tables in README.md or docs/, use go run ./cmd/format_md_tables/ to normalize column padding and alignment markers, then verify with go run ./cmd/format_md_tables/ --check
  • Formatting: always run make analyze-fix before committing to apply configured Go formatters (goimports, gofumpt, gci) and Markdown fixes
  • Full project: make analyze (all analysis gates), make analyze-fix (auto-fix), make analyze-report (LLM report)
  • See docs/development/static-analysis.md for full documentation

End-to-End Tests

E2E tests run against a real GitLab instance via in-memory MCP transport (build tag e2e):

# Run full E2E suite
go test -v -tags e2e -timeout 300s ./test/e2e/suite/
make test-e2e

# Docker mode (ephemeral GitLab CE with CI runner and fixture service)
docker compose -f test/e2e/docker-compose.yml up -d
./test/e2e/scripts/wait-for-gitlab.sh && ./test/e2e/scripts/setup-gitlab.sh && ./test/e2e/scripts/register-runner.sh
set -a && source test/e2e/.env.docker && set +a
go test -v -tags e2e -timeout 600s ./test/e2e/suite/
docker compose -f test/e2e/docker-compose.yml down -v

# Or via Makefile
make test-e2e-docker

# Compile-only check (no GitLab needed)
go test -tags e2e -c -o NUL ./test/e2e/suite/       # Windows
go test -tags e2e -c -o /dev/null ./test/e2e/suite/  # Linux
  • Requires .env with GITLAB_URL, GITLAB_TOKEN (user needs create/delete project permissions)
  • Two sequential workflows: TestFullWorkflow (~174 subtests, individual tools) and TestMetaToolWorkflow (~151 subtests, meta-tools)
  • Dynamic surface coverage lives in TestDynamicToolSurface_* and validates the default two-tool find/execute workflow against the same E2E GitLab fixture. To run only that workflow in Docker mode, run set -a && source test/e2e/.env.docker && set +a after the Docker GitLab setup scripts complete, then use E2E_MODE=docker go test -v -tags e2e -timeout 600s -run '^TestDynamicToolSurface_' ./test/e2e/suite/.
  • Covers: user, project CRUD, commits, branches, tags, releases, issues, labels, milestones, members, upload, MR lifecycle, notes, discussions, search, groups, pipelines, packages, wikis, CI variables, environments, issue links, deploy keys, snippets, pipeline schedules, badges, access tokens, award emoji, elicitation
  • Docker mode also writes E2E_FIXTURE_URL and E2E_GITLAB_INTERNAL_URL for deterministic webhook, custom emoji, and push mirror tests without public Internet dependencies
  • Not covered (needs Docker mode): pipeline CRUD (CI runner), job tools

Surface Evaluator (Docker)

Use these Makefile targets for model-backed surface evaluation with the Docker GitLab fixture:

# CE case set
make eval-surfaces-docker SURFACE=dynamic
make eval-surfaces-docker SURFACE=meta

# Enterprise case set on GitLab EE runtime
make eval-surfaces-docker-enterprise SURFACE=dynamic
make eval-surfaces-docker-enterprise SURFACE=meta

# CE + Enterprise case set together on GitLab EE runtime
make eval-surfaces-docker-enterprise-all SURFACE=dynamic
make eval-surfaces-docker-enterprise-all SURFACE=meta
  • SURFACE must be dynamic or meta.
  • Add PRESET=... to run a single Docker preset.
  • eval-surfaces-docker-enterprise-all sets EVAL_SURFACE_CASE_SET=all and is the standard full validation command for CE+Enterprise regression checks.

Build & Cross-Compilation

# Build for current platform
go build -o dist/gitlab-mcp-server ./cmd/server

# Cross-compile all targets
GOOS=linux GOARCH=amd64 go build -o dist/gitlab-mcp-server-linux-amd64 ./cmd/server
GOOS=linux GOARCH=arm64 go build -o dist/gitlab-mcp-server-linux-arm64 ./cmd/server
GOOS=windows GOARCH=amd64 go build -o dist/gitlab-mcp-server-windows-amd64.exe ./cmd/server
GOOS=windows GOARCH=arm64 go build -o dist/gitlab-mcp-server-windows-arm64.exe ./cmd/server
GOOS=darwin GOARCH=amd64 go build -o dist/gitlab-mcp-server-darwin-amd64 ./cmd/server
GOOS=darwin GOARCH=arm64 go build -o dist/gitlab-mcp-server-darwin-arm64 ./cmd/server

Release Process

When creating a new release and uploading binaries to GitHub Releases:

  1. Build cross-platform binaries with make release (uses GoReleaser locally, flattens dist/ to match GitHub Release asset names)
  2. Release link names MUST be exact filenames (e.g. checksums.txt.asc, gitlab-mcp-server-linux-amd64). Never add descriptive suffixes like (GPG signature)go-selfupdate matches asset names exactly and will fail to find files with decorated names

Git Workflow

  • Use conventional commits: feat:, fix:, docs:, test:, refactor:, chore:
  • Develop on feature branches: feature/tool-name, fix/description
  • Main branch protected, merge via pull requests

Key Environment Variables

VariableDescriptionExample
GITLAB_URLGitLab instance URL. In HTTP mode, optional via --gitlab-url; when set it fixes the GitLab instance, and when omitted clients must send GITLAB-URL per requesthttps://gitlab.example.com
GITLAB_TOKENPersonal Access Token (stdio mode)glpat-...
GITLAB_SKIP_TLS_VERIFYSkip TLS certificate verificationtrue
META_TOOLSDeprecated compatibility selector; prefer TOOL_SURFACE for new configs(unset)
TOOL_SURFACEExplicit tool catalog selector: dynamic, meta, or individual; overrides legacy META_TOOLSdynamic (default when unset)
CAPABILITY_SURFACEResource and prompt catalog selector: full or minimal; pair minimal with dynamic experiments when startup context must be tinyfull (default)
META_PARAM_SCHEMAMeta-tool input-schema strategy: opaque (default), compact (~5x), or full (~10x). Independent of META_TOOLS. Per-action call shapes and input schemas are discoverable through gitlab://tools and gitlab://tools/{id} for every surfaceopaque (default)
GITLAB_READ_ONLYRead-only mode: disables all mutating toolsfalse (default)
GITLAB_SAFE_MODESafe mode: intercepts mutating tools and returns a JSON previewfalse (default)
AUTO_UPDATEEnable auto-update: true (default), check, falsetrue (default)
AUTO_UPDATE_REPOGitHub repository slug for release assets (owner/repo)jmrplens/gitlab-mcp-server
AUTO_UPDATE_INTERVALPeriodic check interval, HTTP mode1h (default)
AUTO_UPDATE_TIMEOUTStartup/background update timeout (range 5s–10m)60s (default)
GITLAB_ENTERPRISEDeprecated — use GITLAB_TIER. Honored for back-compat only when GITLAB_TIER is unset (trueultimate, falsefree); logs a deprecation warningfalse (default)
GITLAB_TIERLicensing tier selector: free/ce, premium, or ultimate. When set, used verbatim; when unset, detected from GET /license (fallback free). Tier gates Enterprise/Premium tools AND per-field schema pruning (see pruneSchemaFieldsByTier in internal/tools/action_catalog.go)free (default)
EVAL_SURFACE_ENTERPRISEcmd/eval_mcp_surfaces: run the enterprise case set on top of the base corpusfalse (default)
EVAL_SURFACE_CASE_SETcmd/eval_mcp_surfaces: case-set selector — ce (CE only), all (CE+Enterprise)ce (default)
EVAL_SURFACE_FIXTURE_SMOKEcmd/eval_mcp_surfaces: limit the run to fixture-smoke cases (fast smoke check)false (default)
--max-output-retriescmd/eval_mcp_surfaces: re-runs a task when it fails solely due to malformed model tool-call output2 (default)
MAX_HTTP_CLIENTSMax client sessions, HTTP mode (also --max-http-clients flag)100 (default)
SESSION_TIMEOUTIdle session timeout, HTTP mode (also --session-timeout flag)30m (default)
RATE_LIMIT_RPSPer-server tools/call rate limit in req/s (also --rate-limit-rps flag; 0 = disabled)0 (default)
RATE_LIMIT_BURSTToken-bucket burst size when RPS > 0 (also --rate-limit-burst flag)40 (default)
AUTH_MODEHTTP mode auth: legacy (default) or oauth (RFC 9728 Bearer verification)legacy (default)
OAUTH_CACHE_TTLOAuth token identity cache TTL (also --oauth-cache-ttl flag)15m (default)

HTTP-only flags (no environment variable equivalent):

FlagDescriptionDefault
--trusted-proxy-headerHTTP header with real client IP for rate limiting behind proxies (e.g. Fly-Client-IP, X-Forwarded-For)(empty)

General flags (both stdio and HTTP modes):

FlagDefaultDescription
--shutdownfalseTerminate all running instances of this binary and exit. Used by external updaters (pe-agnostic-store) before replacing the binary on disk.

AI Assistance Infrastructure

This project includes 7 agents, 18 skills, and 7 instruction files in .github/ for AI-assisted development. See CLAUDE.md at the project root for a comprehensive catalog of all agents, skills, workflows, and when to use each one.

Key agents: go-mcp-expert (primary coding), test-expert (testing, coverage, false-pass detection), plan-expert (strategic planning), debug (debugging), se-reviewer (OWASP + architecture), documentation-writer (project docs with Context7 + web research).

Language Policy

All project artifacts must be written in English without exception.

ArtifactLanguage
Source code (all .go files)English
Comments and doc commentsEnglish
Commit messagesEnglish
Documentation (README, docs/, plan/)English
MCP tool names, descriptions, error messagesEnglish
Test names and assertionsEnglish
ADRs, specs, instructionsEnglish
Git branch namesEnglish

Conversations with the developer may be in any language, but every file committed to this repository must be in English.

Trust

Not scanned yet. Artifacts are graded after they are crawled, so a recently discovered one may have no result for a while.

Versions

  • git-f90304820b452026-08-04