Skip to main content
Glama
by Alosies
config.ts6.81 kB
import { readFileSync, existsSync } from 'fs'; import { resolve, join } from 'path'; import { homedir } from 'os'; export interface GitLabMCPConfig { // GitLab connection settings gitlab: { baseUrl?: string; token?: string; defaultProject?: string; }; // Server behavior settings server: { name?: string; version?: string; timeout?: number; }; // Default parameters for tools defaults: { perPage?: number; projectScope?: 'owned' | 'all'; }; // Feature toggles features: { enableCaching?: boolean; enableMetrics?: boolean; strictScoping?: boolean; }; } export const DEFAULT_CONFIG: GitLabMCPConfig = { gitlab: { baseUrl: 'https://gitlab.com', token: undefined, defaultProject: undefined, }, server: { name: 'gitlab-mcp-server', version: '1.3.1', timeout: 30000, }, defaults: { perPage: 20, projectScope: 'owned', }, features: { enableCaching: false, enableMetrics: false, strictScoping: true, }, }; export class ConfigManager { private config: GitLabMCPConfig; constructor(configPath?: string) { this.config = this.loadConfig(configPath); } private loadConfig(configPath?: string): GitLabMCPConfig { let loadedConfig: Partial<GitLabMCPConfig> = {}; // Try loading from specified path or standard locations const configPaths = this.getConfigPaths(configPath); for (const path of configPaths) { if (existsSync(path)) { try { console.error(`📄 Loading config from: ${path}`); const fileContent = readFileSync(path, 'utf-8'); const parsed = JSON.parse(fileContent); // Support different config formats if (parsed.gitlab || parsed.server || parsed.defaults || parsed.features) { // Direct GitLab MCP config format loadedConfig = { ...loadedConfig, ...parsed }; } else if (parsed.mcpServers?.gitlab) { // Claude Desktop style config - extract gitlab server config const gitlabServer = parsed.mcpServers.gitlab; if (gitlabServer.env) { loadedConfig.gitlab = { ...loadedConfig.gitlab, token: gitlabServer.env.NPM_CONFIG_TOKEN || gitlabServer.env.GITLAB_TOKEN, baseUrl: gitlabServer.env.GITLAB_BASE_URL, defaultProject: gitlabServer.env.GITLAB_DEFAULT_PROJECT, }; } } break; } catch (error) { console.error(`⚠️ Failed to parse config file ${path}:`, error); } } } // Override with environment variables (highest priority) const envConfig = this.loadFromEnv(); // Merge: DEFAULT_CONFIG <- file config <- env config return { ...DEFAULT_CONFIG, ...this.deepMerge(DEFAULT_CONFIG, loadedConfig), ...this.deepMerge(this.deepMerge(DEFAULT_CONFIG, loadedConfig), envConfig), }; } private getConfigPaths(configPath?: string): string[] { const paths: string[] = []; // 1. Explicit path provided if (configPath) { paths.push(resolve(configPath)); } // 2. Environment variable if (process.env.GITLAB_MCP_CONFIG) { paths.push(resolve(process.env.GITLAB_MCP_CONFIG)); } // 3. Current working directory paths.push( join(process.cwd(), 'gitlab-mcp.json'), join(process.cwd(), '.gitlab-mcp.json'), join(process.cwd(), 'gitlab-mcp.config.json'), ); // 4. User home directory paths.push( join(homedir(), '.gitlab-mcp.json'), join(homedir(), '.config', 'gitlab-mcp', 'config.json'), ); // 5. Standard MCP config locations (VS Code, Claude Desktop) if (process.platform === 'darwin') { paths.push( join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'), ); } else if (process.platform === 'win32') { paths.push( join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json'), ); } paths.push(join(process.cwd(), '.vscode', 'mcp.json')); return paths; } private loadFromEnv(): Partial<GitLabMCPConfig> { return { gitlab: { baseUrl: process.env.GITLAB_BASE_URL || process.env.NPM_CONFIG_BASE_URL, token: process.env.NPM_CONFIG_TOKEN || process.env.GITLAB_TOKEN || process.env.GITLAB_ACCESS_TOKEN, defaultProject: process.env.GITLAB_DEFAULT_PROJECT, }, server: { timeout: process.env.GITLAB_MCP_TIMEOUT ? parseInt(process.env.GITLAB_MCP_TIMEOUT, 10) : undefined, }, defaults: { perPage: process.env.GITLAB_MCP_PER_PAGE ? parseInt(process.env.GITLAB_MCP_PER_PAGE, 10) : undefined, projectScope: (process.env.GITLAB_MCP_PROJECT_SCOPE as any) || undefined, }, features: { enableCaching: process.env.GITLAB_MCP_ENABLE_CACHING === 'true', enableMetrics: process.env.GITLAB_MCP_ENABLE_METRICS === 'true', strictScoping: process.env.GITLAB_MCP_STRICT_SCOPING !== 'false', // default true }, }; } private deepMerge(target: any, source: any): any { const result = { ...target }; for (const key in source) { if (source[key] !== null && source[key] !== undefined) { if (typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = this.deepMerge(target[key] || {}, source[key]); } else { result[key] = source[key]; } } } return result; } public get(): GitLabMCPConfig { return this.config; } public getGitLabConfig() { return this.config.gitlab; } public getServerConfig() { return this.config.server; } public getDefaults() { return this.config.defaults; } public getFeatures() { return this.config.features; } public validate(): { valid: boolean; errors: string[] } { const errors: string[] = []; // GitLab token is not required in config validation - it can come from env only // URL validation if (this.config.gitlab.baseUrl) { try { new URL(this.config.gitlab.baseUrl); } catch { errors.push(`Invalid GitLab base URL: ${this.config.gitlab.baseUrl}`); } } // Numeric validations if (this.config.defaults.perPage && (this.config.defaults.perPage < 1 || this.config.defaults.perPage > 100)) { errors.push('perPage must be between 1 and 100'); } if (this.config.server.timeout && this.config.server.timeout < 1000) { errors.push('Server timeout must be at least 1000ms'); } return { valid: errors.length === 0, errors, }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Alosies/gitlab-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server