Skip to content

detailobsessed/efficient-gitlab-mcp

 
 

Repository files navigation

Efficient GitLab MCP

npm version CI License: MIT Bun MCP GitLab TypeScript Biome

Token-Efficient GitLab Server Management — An enhanced fork of zereight/gitlab-mcp with progressive disclosure pattern for dramatic token savings.

What's Different From Upstream?

This fork builds on zereight/gitlab-mcp with a redesigned architecture focused on token efficiency and maintainability. We regularly review upstream commits and port new features and bugfixes while keeping our own structure.

Architecture at a Glance

Area Upstream This Fork
Architecture Single index.ts (~10K lines) Modular src/ with 15 tool modules
Tool Discovery All 140+ tools exposed at once SDK-native progressive disclosure (2 meta-tools)
Configuration Flat individual exports Typed ServerConfig interface with loadConfig()
Logging console.log Structured MCP protocol logger for agent observability
Runtime Node.js + npm Bun (faster builds, native TypeScript)
Linting ESLint + Prettier Strict Biome rules (noExplicitAny, noNonNullAssertion, cognitive complexity)
CI/CD Basic GitHub Actions (lint, build, test, semantic-release)
Pre-commit None prek hooks (typos, formatting, build verification)
Feature Flags USE_PIPELINE, USE_MILESTONE, USE_GITLAB_WIKI required None — all categories available via progressive disclosure

Key Improvements

  • Progressive Disclosure — 2 meta-tools instead of 140+ individual tools (~90% token reduction). Uses the MCP SDK's native enable()/disable() API so tools are registered but hidden until the LLM activates a category.
  • Modular Tool Organization — Each GitLab domain (issues, merge requests, pipelines, etc.) lives in its own file under src/tools/, making it easy to find, test, and extend individual tools without navigating a monolithic file.
  • Typed Configuration — A ServerConfig interface ensures all config values are validated at startup, with IDE autocompletion and compile-time safety.
  • MCP Protocol Logging — Structured logs sent to LLM clients for agent observability, not just developer console output.
  • HTTP Transport Security — DNS rebinding protection, configurable allowed hosts/origins.
  • Read-Only Mode & PAT Safety — Automatic PAT scope detection, explicit read-only mode, and actionable 403 error messages. Uses readOnlyHint annotations on all 146 tools to filter write operations.
  • Comprehensive Test Suite — 160+ tests covering registry, config, logger, MCP integration, read-only mode, and meta-tools.
  • Strict Code Quality — Zero any types, no non-null assertions, enforced cognitive complexity limits.
  • Modern Tooling — Bun for fast builds, Biome for linting, prek for pre-commit hooks.
  • No Feature Flags Needed — Upstream requires USE_PIPELINE, USE_MILESTONE, and USE_GITLAB_WIKI env vars to enable core tools. Progressive disclosure eliminates this — all 15 categories are registered but dormant until activated, so there's zero token cost and zero config overhead.
  • Automated Releases — Semantic versioning with conventional commits.

Upstream Tracking

We maintain main as a read-only mirror of upstream. New features and bugfixes from upstream are reviewed and ported into our architecture as needed — we don't blindly rebase, since the codebases have structurally diverged. If you're looking for a specific upstream feature, check our releases or open an issue.


How It Works

Instead of exposing 140+ individual tools, this server exposes 2 meta-tools:

Meta-Tool Purpose
list_categories Discover available tool categories and their activation status
activate_tools Enable all tools in one or more categories

Token Savings

Approach Tools Exposed Approximate Token Cost
Traditional 140+ tools ~20,000+ tokens
Progressive Disclosure 2 meta-tools ~1,500 tokens

~90% reduction in tool definition tokens!

Example Workflow

1. LLM calls list_categories() → sees "merge-requests" category (20 tools, 0 active)
2. LLM calls activate_tools(categories: ["merge-requests"]) → 20 tools now appear in tool list
3. LLM calls create_merge_request({project_id: "123", title: "Fix bug", source_branch: "fix", target_branch: "main"})

Available Operations

All GitLab operations organized by category:

Category Description
repositories Search, create, fork repos. Get files, push files, manage branches
merge-requests Create, update, merge MRs. Discussions, threads, diffs
issues Create, update, delete issues. Links, discussions
pipelines List, create, retry, cancel pipelines. Job output
projects Project details, members, labels
commits List commits, get diffs
namespaces List, get, verify namespaces
users User details, search users, audit/project events, file uploads
search Global, project, and group search across code, issues, MRs, commits
wiki Wiki page management for projects and groups
milestones Create, edit, delete milestones. Burndown events
releases List, create, update, delete releases. Download assets
webhooks List project webhooks and recent events
work-items GraphQL work items: create, update, hierarchy, notes, incidents
graphql Execute arbitrary GraphQL queries

Quick Start

Prerequisites

  • Node.js 18+ (for npx) or Bun 1.0+ (for bunx)
  • A GitLab Personal Access Token — scope determines what the server can do:
    • api — Full access (create issues, merge MRs, manage pipelines, etc.)
    • read_api — Read-only (server auto-detects and hides write tools)
  • Or CI_JOB_TOKEN — automatically detected in GitLab CI pipelines

Full Access (recommended for most users)

Use an api scope PAT to get all 146 tools across 15 categories:

Claude Code CLI:

# With npx (Node.js)
claude mcp add gitlab \
  -e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
  -e GITLAB_API_URL=https://gitlab.com \
  -- npx efficient-gitlab-mcp-server

# With bunx (Bun)
claude mcp add gitlab \
  -e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
  -e GITLAB_API_URL=https://gitlab.com \
  -- bunx efficient-gitlab-mcp-server

MCP client config (Claude Desktop, IDE extensions, etc.):

{
  "mcpServers": {
    "gitlab": {
      "command": "npx",
      "args": ["efficient-gitlab-mcp-server"],
      "env": {
        "GITLAB_PERSONAL_ACCESS_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx",
        "GITLAB_API_URL": "https://gitlab.com"
      }
    }
  }
}

Read-Only Mode (security-conscious setup)

Use a read_api scope PAT — the server auto-detects the limited scope and only exposes read tools. No extra config needed:

Claude Code CLI:

# With npx (Node.js)
claude mcp add gitlab \
  -e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-your-read-only-token \
  -e GITLAB_API_URL=https://gitlab.com \
  -- npx efficient-gitlab-mcp-server

# With bunx (Bun)
claude mcp add gitlab \
  -e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-your-read-only-token \
  -e GITLAB_API_URL=https://gitlab.com \
  -- bunx efficient-gitlab-mcp-server

Or force read-only mode explicitly (regardless of token scopes):

# With npx (Node.js)
claude mcp add gitlab \
  -e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
  -e GITLAB_API_URL=https://gitlab.com \
  -e GITLAB_READ_ONLY_MODE=true \
  -- npx efficient-gitlab-mcp-server

# With bunx (Bun)
claude mcp add gitlab \
  -e GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
  -e GITLAB_API_URL=https://gitlab.com \
  -e GITLAB_READ_ONLY_MODE=true \
  -- bunx efficient-gitlab-mcp-server

For self-hosted GitLab, update GITLAB_API_URL to your instance URL.

Install from Source (Development)

git clone https://github.com/detailobsessed/efficient-gitlab-mcp.git
cd efficient-gitlab-mcp
bun install
bun run build
bun start

Features

Read-Only Mode & PAT Safety

The server provides three layers of protection for users with limited-scope Personal Access Tokens:

1. Explicit read-only mode — Set GITLAB_READ_ONLY_MODE=true to restrict the server to read-only tools. Write tools won't appear in list_categories counts or be activated by activate_tools. This is controlled by the readOnlyHint annotation on every tool.

2. Automatic PAT scope detection — On startup, the server calls GitLab's GET /personal_access_tokens/self to inspect your token's scopes. If the token lacks the api scope (e.g., only has read_api), read-only mode is automatically enabled. No configuration needed — it just works.

3. Actionable 403 error messages — If a tool call hits a 403 Forbidden error, the error message includes specific guidance about which PAT scopes are needed, so the LLM can inform the user rather than retrying blindly.

# Explicit read-only mode
GITLAB_READ_ONLY_MODE=true

# Or just use a read_api token — auto-detected!
GITLAB_PERSONAL_ACCESS_TOKEN=glpat-your-read-only-token

MCP Protocol Logging

The server supports MCP protocol logging for agent observability. When connected, LLM clients can receive structured log messages showing what the server is doing:

  • Tool execution logs
  • GitLab API call details
  • Error information with context

This helps agents understand server behavior and debug issues.

HTTP Transport Security

When using HTTP transport (STREAMABLE_HTTP=true), the server includes security features:

Environment Variable Default Description
HTTP_ALLOWED_HOSTS localhost,127.0.0.1 Comma-separated list of allowed Host headers
HTTP_ALLOWED_ORIGINS (any) Comma-separated list of allowed Origin headers
HTTP_ENABLE_DNS_REBINDING_PROTECTION true Enable DNS rebinding attack protection

Example for production:

HTTP_ALLOWED_HOSTS=api.example.com,localhost \
HTTP_ALLOWED_ORIGINS=https://app.example.com \
STREAMABLE_HTTP=true \
bun start

Development

# Run tests
bun test

# Run tests with coverage
bun test --coverage

# Lint and format
bun run check

# Build
bun run build

Configuration

Core Settings

Variable Required Default Description
GITLAB_PERSONAL_ACCESS_TOKEN Yes* - GitLab personal access token (takes priority over CI_JOB_TOKEN)
CI_JOB_TOKEN No - GitLab CI job token (auto-detected in CI pipelines)
GITLAB_API_URL No https://gitlab.com GitLab instance URL
GITLAB_PROJECT_ID No - Default project ID when tools omit project_id
GITLAB_ALLOWED_PROJECT_IDS No - Restrict tools to these projects (comma-separated). With a single project, acts as default. With multiple, project_id is required per call
GITLAB_READ_ONLY_MODE No false Only expose read-only tools. Auto-detected from PAT scopes if not set
GITLAB_IS_OLD No false For older GitLab instances

*PAT is recommended. CI_JOB_TOKEN is auto-detected in GitLab CI pipelines when no PAT is set. OAuth support is planned (see DET-44).

Transport Settings

Variable Required Default Description
STREAMABLE_HTTP No false Enable HTTP transport
SSE No false Enable SSE transport
PORT No 3002 HTTP server port
HOST No 127.0.0.1 HTTP server host

Logging & Security

Variable Required Default Description
LOG_LEVEL No info debug, info, warn, error
LOG_FORMAT No pretty json, pretty
HTTP_ALLOWED_HOSTS No localhost,127.0.0.1 Allowed Host headers
HTTP_ALLOWED_ORIGINS No (any) Allowed Origin headers

Remote Authorization (Multi-tenant)

Variable Required Default Description
REMOTE_AUTHORIZATION No false Enable remote auth
ENABLE_DYNAMIC_API_URL No false Allow dynamic GitLab URLs
SESSION_TIMEOUT_SECONDS No 3600 Session timeout
MAX_SESSIONS No 1000 Maximum concurrent sessions
MAX_REQUESTS_PER_MINUTE No 60 Rate limit per session

*Or use CI_JOB_TOKEN in GitLab CI pipelines. OAuth authentication is planned — see OAuth Setup Guide for the design.


Security

  • Never commit tokens — Use .env files (gitignored)
  • Rotate tokens — Regenerate periodically
  • Least privilege — Only grant necessary API scopes
  • Audit logs — Monitor API access

Acknowledgments

This project is a fork of zereight/gitlab-mcp. Thanks to the original author for the comprehensive GitLab API implementation.


Resources


License

MIT License — See LICENSE for details.


Efficient GitLab MCP
AI-Powered GitLab Management with Token Efficiency
Built with Bun and the Model Context Protocol

About

Token-efficient GitLab MCP server — 93% fewer tokens via progressive disclosure (5 meta-tools instead of 77). Bun runtime, Biome linting, semantic-release, 120+ tests.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • TypeScript 98.3%
  • JavaScript 1.7%