Skip to content

feat(cli): Interactive setup wizard (gitlab-mcp init) #62

@polaz

Description

@polaz

Summary

Add interactive setup wizard (gitlab-mcp init) for streamlined onboarding with support for PAT, OAuth, Docker deployment, and one-click Claude integration via Deep Links.

Motivation

Current setup requires:

  1. Reading documentation
  2. Finding token creation page
  3. Creating token with correct scopes
  4. Manually configuring environment variables
  5. Copying JSON config to MCP client
  6. Restarting MCP client

This takes 15-30 minutes and is error-prone. The wizard reduces this to ~2 minutes.
Deep Links reduce web-based setup to seconds.


MCP Client Configuration Paths

Research of popular MCP clients and their configuration methods:

Client Config Path Format Transport
Claude Desktop macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
JSON stdio, SSE, HTTP
Claude Code CLI: claude mcp add <name> <command> [args...] CLI stdio, SSE
Cursor Global: ~/.cursor/mcp.json
Project: .cursor/mcp.json
JSON stdio, SSE, HTTP
VS Code Copilot .vscode/mcp.json JSON stdio
Cline GUI → cline_mcp_settings.json JSON stdio, SSE
Roo Code Global: mcp_settings.json
Project: .roo/mcp.json
JSON stdio
Windsurf ~/.codeium/windsurf/mcp_config.json JSON stdio
JetBrains Settings → Tools → AI Assistant → MCP JSON stdio, HTTP
Zed Extensions / Command Palette JSON stdio
Codex ~/.codex/config.toml TOML stdio

Key Findings

  • 90% of clients use identical mcpServers JSON format
  • Claude Code supports CLI-based setup via claude mcp add
  • Deep Links - Claude Desktop supports claude://settings/mcp/add?config=BASE64
  • Project-level configs - Cursor (.cursor/), Roo Code (.roo/), VS Code (.vscode/)
  • Env interpolation - Cursor supports ${env:VAR}, ${workspaceFolder}

NEW: One-Click Deploy Methods

Claude Code CLI Integration

Claude Code supports adding MCP servers directly via CLI:

# Add gitlab-mcp to Claude Code
claude mcp add gitlab-mcp npx @structured-world/gitlab-mcp stdio

# With environment variables
claude mcp add gitlab-mcp \
  -e GITLAB_API_URL=https://gitlab.com \
  -e GITLAB_TOKEN=glpat-xxx \
  npx @structured-world/gitlab-mcp stdio

# With preset
claude mcp add gitlab-mcp npx @structured-world/gitlab-mcp stdio --preset developer

Implementation for gitlab-mcp install --claude-code

// src/cli/commands/install.ts
import { execSync } from "child_process";

export async function installClaudeCode(options: {
  host: string;
  token?: string;
  preset?: string;
}) {
  const args = [
    "npx",
    "@structured-world/gitlab-mcp",
    "stdio",
    ...(options.preset ? ["--preset", options.preset] : []),
  ];

  const envArgs: string[] = [];
  envArgs.push("-e", `GITLAB_API_URL=${options.host}`);
  if (options.token) {
    envArgs.push("-e", `GITLAB_TOKEN=${options.token}`);
  }

  const cmd = `claude mcp add gitlab-mcp ${envArgs.join(" ")} ${args.join(" ")}`;

  try {
    execSync(cmd, { stdio: "inherit" });
    console.log("✓ GitLab MCP added to Claude Code");
  } catch {
    console.log("Claude Code CLI not found. Install with: npm i -g @anthropic-ai/claude-code");
  }
}

Claude Desktop Deep Link

Claude Desktop supports claude:// protocol for adding MCP servers:

claude://settings/mcp/add?config=BASE64_ENCODED_CONFIG

Implementation

// src/cli/utils/deep-link.ts

interface McpServerConfig {
  command: string;
  args: string[];
  env?: Record<string, string>;
}

function generateClaudeDeepLink(config: McpServerConfig): string {
  const configJson = JSON.stringify({
    mcpServers: {
      gitlab: config
    }
  });
  const base64 = Buffer.from(configJson).toString('base64url');
  return `claude://settings/mcp/add?config=${base64}`;
}

// Usage
const deepLink = generateClaudeDeepLink({
  command: 'npx',
  args: ['@structured-world/gitlab-mcp', 'stdio'],
  env: {
    GITLAB_API_URL: 'https://gitlab.com',
    GITLAB_TOKEN: '${GITLAB_TOKEN}' // Placeholder
  }
});

Web Landing Page

Create a simple landing page at gitlab-mcp.sw.foundation/setup:

<!DOCTYPE html>
<html>
<head>
  <title>GitLab MCP - One-Click Setup</title>
</head>
<body>
  <h1>Add GitLab MCP to Claude</h1>

  <div class="setup-options">
    <!-- Option 1: gitlab.com -->
    <div class="option">
      <h3>gitlab.com (Public)</h3>
      <p>For personal projects on gitlab.com</p>
      <a href="claude://settings/mcp/add?config=..." class="btn">
        Add to Claude Desktop
      </a>
    </div>

    <!-- Option 2: Self-hosted -->
    <div class="option">
      <h3>Self-Hosted GitLab</h3>
      <form id="self-hosted-form">
        <input type="url" placeholder="https://gitlab.company.com" id="host">
        <button type="submit">Generate Link</button>
      </form>
      <a href="#" id="self-hosted-link" class="btn hidden">
        Add to Claude Desktop
      </a>
    </div>
  </div>

  <h2>After Adding</h2>
  <ol>
    <li>Set your GitLab token: <code>export GITLAB_TOKEN=glpat-xxx</code></li>
    <li>Restart Claude Desktop</li>
    <li>Ask Claude: "List my GitLab projects"</li>
  </ol>
</body>
</html>

README Badge

[![Add to Claude](https://img.shields.io/badge/Add%20to-Claude-blue?logo=anthropic)](https://gitlab-mcp.sw.foundation/setup)

NPX One-Liner with Deep Link

# Generate and open deep link
npx @structured-world/gitlab-mcp setup --claude-link

# Output:
# Opening Claude Desktop with GitLab MCP configuration...
#
# If browser didn't open, copy this link:
# claude://settings/mcp/add?config=eyJtY3BTZXJ2ZXJz...
#
# After adding:
# 1. Set GITLAB_TOKEN environment variable
# 2. Restart Claude Desktop

Commands

gitlab-mcp init - Global Setup Wizard

One-time setup for connecting to GitLab.

$ gitlab-mcp init

╭─────────────────────────────────────────────────────────────╮
│                   GitLab MCP Setup                          │
╰─────────────────────────────────────────────────────────────╯

? Where is your GitLab instance?
  ● gitlab.com
  ○ Self-hosted (enter URL)
  ○ Detect from current directory (.git)

? Authentication method:
  ● Personal Access Token (PAT) - simple, single GitLab
  ○ OAuth - multiple GitLabs, team sharing, persistent sessions

Opening browser to create PAT token...
  → https://gitlab.com/-/user_settings/personal_access_tokens?name=gitlab-mcp&scopes=api,read_user

? Paste your token: glpat-xxxxxxxxxxxxxxxxxxxx

? Select your role:
  ○ readonly   - Safe exploration, no modifications
  ● developer  - Standard development workflow
  ○ devops     - CI/CD and infrastructure
  ○ admin      - Full access

Testing connection... ✓ Connected as @username

? Install to MCP client?
  ● Claude Desktop
  ○ Claude Code
  ○ Cursor
  ○ VS Code
  ○ Cline
  ○ Roo Code
  ○ Windsurf
  ○ Show config only

✓ Config added to Claude Desktop
✓ Please restart Claude Desktop

Done! Try: "List my GitLab projects"

gitlab-mcp init with Docker + OAuth

For multi-GitLab setups with OAuth authentication.

$ gitlab-mcp init

? Authentication method:
  ○ Personal Access Token (PAT)
  ● OAuth - multiple GitLabs, team sharing

✓ Docker detected (v24.0.7)

? How to run OAuth server?
  ● Docker container (recommended)
  ○ System service
  ○ Manual setup

? Port: 3333

Setting up Docker...
  ✓ Creating ~/.config/gitlab-mcp/docker-compose.yml
  ✓ Pulling ghcr.io/structured-world/gitlab-mcp:latest
  ✓ Starting container on localhost:3333

? Add GitLab instance:
  Host: gitlab.com

  Opening browser to create OAuth Application...
  → https://gitlab.com/-/admin/applications/new

  Required settings:
    Name: GitLab MCP (local)
    Redirect URI: http://localhost:3333/auth/callback
    Scopes: [x] api [x] read_user
    Confidential: [x] Yes

? OAuth Client ID: abc123
? OAuth Client Secret: ****

Testing OAuth...
  → Opening http://localhost:3333/auth/login?instance=gitlab.com
  ✓ Logged in as @username

? Add another GitLab instance? [Y/n] y
  Host: gitlab.company.com
  ...

╭─────────────────────────────────────────────────────────────╮
│ ✓ Setup complete!                                           │
│                                                             │
│ Instances: gitlab.com, gitlab.company.com                  │
│ Endpoint: http://localhost:3333/sse                        │
│ Auth: http://localhost:3333/auth                           │
╰─────────────────────────────────────────────────────────────╯

gitlab-mcp init-project - Project Configuration

Creates .gitlab-mcp/ directory with project-specific config.

$ cd /path/to/my-project
$ gitlab-mcp init-project

Detected: [email protected]:myteam/backend.git

? Restrict scope to this project? [Y/n]

? Project type:
  ○ Frontend (web/mobile)
  ● Backend (API/services)
  ○ DevOps (infrastructure)
  ○ Library (shared code)

Creating .gitlab-mcp/
  ✓ preset.yaml (scope: myteam/backend)
  ✓ profile.yaml (extends: senior-dev, +pipelines, +variables)

? Add to .gitignore? [Y/n]

gitlab-mcp doctor - Health Check

Diagnose configuration and connection issues.

$ gitlab-mcp doctor

╭─────────────────────────────────────────────────────────────╮
│                   GitLab MCP Health Check                   │
╰─────────────────────────────────────────────────────────────╯

Configuration:
  ✓ Profile: work (gitlab.company.com)
  ✓ Preset: developer (24 tools)
  ✓ Project config: .gitlab-mcp/

Connection:
  ✓ API: https://gitlab.company.com/api/v4
  ✓ Token valid (expires: 2025-12-31)
  ✓ User: @username (Developer role)

MCP Clients:
  ✓ Claude Desktop - configured
  ✓ Claude Code - configured
  ✗ Cursor - not configured

Recommendations:
  ⚠ Token expires in 30 days
  ℹ Run: gitlab-mcp install --cursor

gitlab-mcp docker - Container Management

Manage Docker-based OAuth server.

gitlab-mcp docker status      # Container + instances status
gitlab-mcp docker logs        # Tail container logs
gitlab-mcp docker restart     # Restart container
gitlab-mcp docker upgrade     # Pull latest image + restart
gitlab-mcp docker stop        # Stop container
gitlab-mcp docker add-instance <host>  # Add GitLab instance

gitlab-mcp install - MCP Client Installation

Install configuration to MCP clients.

gitlab-mcp install --claude-desktop  # Claude Desktop (JSON config)
gitlab-mcp install --claude-code     # Claude Code (via `claude mcp add`)
gitlab-mcp install --cursor          # Cursor
gitlab-mcp install --vscode          # VS Code Copilot
gitlab-mcp install --cline           # Cline
gitlab-mcp install --roo-code        # Roo Code
gitlab-mcp install --windsurf        # Windsurf
gitlab-mcp install --all             # All detected clients
gitlab-mcp install --show            # Show config only

NEW: gitlab-mcp setup - Quick Setup Commands

# Generate Claude deep link and open
gitlab-mcp setup --claude-link

# Generate deep link for specific host
gitlab-mcp setup --claude-link --host gitlab.company.com

# Output URL only (for scripts/docs)
gitlab-mcp setup --claude-link --url-only

# Generate QR code for mobile/sharing
gitlab-mcp setup --claude-link --qr

Implementation

CLI Framework: @clack/prompts

Using clack for minimalist, performant CLI UX.

import * as p from '@clack/prompts';

async function init() {
  p.intro('GitLab MCP Setup');

  const host = await p.select({
    message: 'Where is your GitLab instance?',
    options: [
      { value: 'gitlab.com', label: 'gitlab.com' },
      { value: 'self-hosted', label: 'Self-hosted (enter URL)' },
      { value: 'detect', label: 'Detect from current directory' },
    ],
  });

  // ... rest of flow
}

Deep Link Generator

// src/cli/utils/deep-link.ts
import open from 'open';

export function generateClaudeDeepLink(options: {
  host: string;
  transport?: 'stdio' | 'sse';
  preset?: string;
}): string {
  const { host, transport = 'stdio', preset } = options;

  const config = transport === 'stdio'
    ? {
        mcpServers: {
          gitlab: {
            command: 'npx',
            args: [
              '@structured-world/gitlab-mcp',
              'stdio',
              ...(preset ? ['--preset', preset] : [])
            ],
            env: {
              GITLAB_API_URL: host.startsWith('http') ? host : `https://${host}`,
              GITLAB_TOKEN: '${GITLAB_TOKEN}'
            }
          }
        }
      }
    : {
        mcpServers: {
          gitlab: {
            url: `http://localhost:3333/sse`
          }
        }
      };

  const base64 = Buffer.from(JSON.stringify(config)).toString('base64url');
  return `claude://settings/mcp/add?config=${base64}`;
}

export async function openClaudeDeepLink(options: Parameters<typeof generateClaudeDeepLink>[0]) {
  const link = generateClaudeDeepLink(options);

  console.log('Opening Claude Desktop...');
  console.log('');
  console.log('If browser didn't open, copy this link:');
  console.log(link);

  await open(link);
}

Docker Compose Template

# ~/.config/gitlab-mcp/docker-compose.yml
version: '3.8'
services:
  gitlab-mcp:
    image: ghcr.io/structured-world/gitlab-mcp:latest
    container_name: gitlab-mcp
    ports:
      - "${PORT:-3333}:3333"
    environment:
      - TRANSPORT=sse
      - PORT=3333
      - OAUTH_ENABLED=true
      - OAUTH_SESSION_SECRET=${OAUTH_SESSION_SECRET}
      - DATABASE_URL=file:/data/sessions.db
    volumes:
      - gitlab-mcp-data:/data
      - ./instances.yml:/app/config/instances.yml:ro
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3333/health"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  gitlab-mcp-data:

Multi-Instance Configuration

# ~/.config/gitlab-mcp/instances.yml
instances:
  gitlab.com:
    name: "GitLab.com"
    oauth:
      client_id: "abc123"
      client_secret_env: "GITLAB_COM_SECRET"
    default_preset: "gitlab-com"

  gitlab.company.com:
    name: "Company GitLab"
    oauth:
      client_id: "xyz789"
      client_secret_env: "GITLAB_COMPANY_SECRET"
    default_preset: "developer"

File Structure

src/
├── cli/
│   ├── index.ts           # CLI entry point
│   ├── commands/
│   │   ├── init.ts        # gitlab-mcp init
│   │   ├── init-project.ts # gitlab-mcp init-project
│   │   ├── doctor.ts      # gitlab-mcp doctor
│   │   ├── docker.ts      # gitlab-mcp docker *
│   │   ├── install.ts     # gitlab-mcp install
│   │   └── setup.ts       # gitlab-mcp setup (deep links)
│   ├── templates/
│   │   ├── docker-compose.yml
│   │   └── mcp-configs/
│   │       ├── claude-desktop.json
│   │       ├── cursor.json
│   │       ├── vscode.json
│   │       ├── cline.json
│   │       ├── roo-code.json
│   │       └── windsurf.json
│   └── utils/
│       ├── deep-link.ts   # Claude deep link generator
│       ├── claude-code.ts # Claude Code CLI integration
│       ├── detect-docker.ts
│       ├── detect-git.ts
│       ├── open-browser.ts
│       └── mcp-client-installer.ts
├── web/                    # Landing page (optional)
│   └── setup/
│       ├── index.html
│       └── setup.js

Quick Start Documentation

## Quick Setup

### One-Click (Fastest)

Click the button or visit: **[gitlab-mcp.sw.foundation/setup](https://gitlab-mcp.sw.foundation/setup)**

[![Add to Claude](https://img.shields.io/badge/Add%20to-Claude-blue?logo=anthropic)](https://gitlab-mcp.sw.foundation/setup)

### Command Line

```bash
# Interactive wizard
npx @structured-world/gitlab-mcp init

# Generate Claude deep link
npx @structured-world/gitlab-mcp setup --claude-link

# Add directly to Claude Code
claude mcp add gitlab-mcp npx @structured-world/gitlab-mcp stdio

One-Liner (for scripts)

# gitlab.com + developer preset
npx @structured-world/gitlab-mcp init \
  --host gitlab.com \
  --preset developer \
  --install claude-desktop

# Self-hosted + Docker OAuth
npx @structured-world/gitlab-mcp init \
  --host gitlab.company.com \
  --oauth \
  --docker \
  --install all

Let Claude Set It Up

Ask your AI assistant:

"Setup GitLab MCP for my projects"
"Add GitLab MCP to Claude Desktop for gitlab.com"


---

## Tasks

### Phase 1: Core Wizard
- [ ] Add @clack/prompts dependency
- [ ] Implement `gitlab-mcp init` base flow
- [ ] PAT authentication flow
- [ ] Browser auto-open for token creation
- [ ] Connection testing
- [ ] Role → preset mapping
- [ ] Config file generation

### Phase 2: MCP Client Installation
- [ ] Detect installed MCP clients
- [ ] Claude Desktop config installer (JSON)
- [ ] **Claude Code installer (via `claude mcp add`)**
- [ ] Cursor config installer
- [ ] VS Code Copilot config installer
- [ ] Cline config installer
- [ ] Roo Code config installer
- [ ] Windsurf config installer
- [ ] Backup existing configs before modification

### Phase 3: Docker + OAuth
- [ ] Docker detection
- [ ] docker-compose.yml generation
- [ ] Container startup/management
- [ ] Multi-instance configuration
- [ ] OAuth flow testing
- [ ] `gitlab-mcp docker` subcommands

### Phase 4: Project Configuration
- [ ] Implement `gitlab-mcp init-project`
- [ ] Git remote detection
- [ ] Project type → profile mapping
- [ ] .gitlab-mcp/ scaffold generation

### Phase 5: Diagnostics
- [ ] Implement `gitlab-mcp doctor`
- [ ] Config validation
- [ ] Connection testing
- [ ] Token expiration check
- [ ] MCP client detection

### Phase 6: One-Click Deploy (Deep Links)
- [ ] Implement `generateClaudeDeepLink()` function
- [ ] Add `gitlab-mcp setup --claude-link` command
- [ ] Support custom host in deep link
- [ ] Support preset selection in deep link
- [ ] Create web landing page (gitlab-mcp.sw.foundation/setup)
- [ ] Add "Add to Claude" badge to README
- [ ] QR code generation for sharing

### Phase 7: Documentation
- [ ] Quick Start guide with deep link
- [ ] One-liner examples
- [ ] Troubleshooting section
- [ ] Video/GIF demos

### Phase 8: Client-Specific Guides
- [ ] docs/clients/claude-desktop.md
- [ ] docs/clients/claude-code.md
- [ ] docs/clients/cursor.md
- [ ] docs/clients/vscode.md
- [ ] docs/clients/cline.md
- [ ] docs/clients/roo-code.md
- [ ] docs/clients/windsurf.md
- [ ] docs/clients/jetbrains.md

---

## Acceptance Criteria

- [ ] `gitlab-mcp init` completes setup in <2 minutes
- [ ] Browser opens correct pages for token/OAuth creation
- [ ] Connection is tested before saving config
- [ ] MCP client configs are correctly generated
- [ ] **`gitlab-mcp install --claude-code` uses `claude mcp add` CLI**
- [ ] Docker container starts and persists across restarts
- [ ] Multi-GitLab OAuth works seamlessly
- [ ] `gitlab-mcp doctor` identifies common issues
- [ ] **Deep link opens Claude Desktop and adds server config**
- [ ] **Web landing page generates correct deep links**
- [ ] Works on macOS, Linux, Windows

## Priority

**HIGH** - Critical for user adoption

## Dependencies

- #54 - Profiles infrastructure ✅
- #55 - Built-in presets ✅
- #61 - Project-level configs

## Related

- #24 - Documentation (Quick Start section)
- #57 - Auto-discovery (reuse git detection)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature, new MCP tool, new capability

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions