UNPKG

gitlab-activity-mcp

Version:

GitLab Activity MCP Server - Generate professional activity reports and analysis

140 lines (138 loc) 5.42 kB
import { logger } from 'mcp-framework'; import { cacheService } from './CacheService.js'; export class GitLabService { baseUrl; accessToken; user; constructor() { // 从环境变量获取配置 this.baseUrl = process.env.GITLAB_BASE_URL || ''; this.accessToken = process.env.GITLAB_ACCESS_TOKEN || ''; logger.info(`[GitLabAuthService] 初始化 GitLabAuthService ${this.baseUrl}`); } /** * 验证配置是否完整 */ async validateConfig() { if (!this.baseUrl || !this.accessToken) { throw new Error(`GitLab 配置缺失。请在 MCP 配置中设置以下环境变量: - GITLAB_BASE_URL: GitLab 实例的 API 基础 URL (例如: https://gitlab.com/api/v4) - GITLAB_ACCESS_TOKEN: GitLab 访问令牌 (需要 read_user 或 api 权限) 配置示例: { "mcpServers": { "gitlab-activity": { "command": "node", "args": ["/path/to/gitlab-activity-mcp-server/dist/index.js"], "env": { "GITLAB_BASE_URL": "https://gitlab.com/api/v4", "GITLAB_ACCESS_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx", "GITLAB_CACHE_PATH": "./cache/gitlab-cache.json" } } } }`); } try { this.user = await this.getCurrentUser(); logger.info(`[GitLabService] 验证配置成功 ${JSON.stringify(this.user)}`); } catch (error) { throw new Error(`GitLab 配置错误。请检查在 MCP 配置中设置以下的环境变量: - GITLAB_BASE_URL: GitLab 实例的 API 基础 URL (例如: https://gitlab.com/api/v4) - GITLAB_ACCESS_TOKEN: GitLab 访问令牌 (需要 read_user 或 api 权限) - ${error}`); } } /** * 执行认证的 GitLab API 请求 */ async fetchGitLab(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; logger.info(`[GitLabService] 执行 GitLab API 请求 ${url} ${JSON.stringify(options)}`); // 创建 AbortController 用于超时控制 const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); try { const response = await fetch(url, { method: 'GET', ...options, signal: controller.signal, headers: { Authorization: `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', ...options.headers, }, }); clearTimeout(timeoutId); if (!response.ok) { this.handleApiError(response); } return response.json(); } catch (error) { clearTimeout(timeoutId); if (error instanceof Error) { if (error.name === 'AbortError') { throw new Error('GitLab API 请求超时'); } if (error.name === 'TypeError') { throw new Error('网络错误:无法连接到 GitLab 实例'); } } throw error; } } /** * 处理 API 错误 */ handleApiError(response) { switch (response.status) { case 401: throw new Error(`认证失败:访问令牌无效或已过期。请检查您的 GitLab 访问令牌是否有效且具有 read_user 或 api 权限。`); case 403: throw new Error(`权限不足:您的访问令牌没有足够的权限访问用户事件。请确保令牌具有 read_user 或 api 权限。`); case 404: throw new Error(`资源不存在:当前用户或请求的资源不存在。请检查访问令牌是否有效,GitLab 实例是否正确。`); case 429: throw new Error(`请求过于频繁:已达到 GitLab API 速率限制,请稍后重试。`); case 500: case 502: case 503: case 504: throw new Error(`GitLab 服务器错误 (${response.status}):服务器暂时不可用,请稍后重试。`); default: throw new Error(`GitLab API 错误 (${response.status}): ${response.statusText}`); } } async getUserEvents(userId, after, before) { const params = new URLSearchParams({ action: 'pushed', after: after || '', before: before || '', per_page: '500', }); const endpoint = `/users/${userId}/events?${params.toString()}`; return this.fetchGitLab(endpoint); } async getCurrentUser() { if (this.user) return this.user; const user = await this.fetchGitLab('/user'); return user; } async getProject(projectId) { const cacheKey = projectId.toString(); // 尝试从缓存获取 const cachedProject = await cacheService.getProject(cacheKey); if (cachedProject) { return cachedProject; } // 从 API 获取并缓存 const project = await this.fetchGitLab(`/projects/${projectId}`); await cacheService.setProject(cacheKey, project); return project; } } export const gitLabService = new GitLabService(); gitLabService.validateConfig();