UNPKG

@yeepay/awesome-components-mcp

Version:

MCP server providing access to awesome-components documentation and integration guides with dual-mode operation: direct fetch and GitLab MCP instruction generation

190 lines (189 loc) 7.56 kB
"use strict"; /** * GitLab Data Acquisition Service * * This service provides functionality to fetch text content (specifically llms.txt files) * from GitLab URLs using HTTP GET requests with proper error handling and authentication. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GitLabAuthError = exports.GitLabError = void 0; exports.fetchGitLabFileContent = fetchGitLabFileContent; exports.isValidGitLabUrl = isValidGitLabUrl; exports.validateAuthenticationConfig = validateAuthenticationConfig; exports.fetchGitLabFileContentSafe = fetchGitLabFileContentSafe; const config_js_1 = require("../config.js"); /** * Custom error class for GitLab-specific errors */ class GitLabError extends Error { statusCode; url; constructor(message, statusCode, url) { super(message); this.statusCode = statusCode; this.url = url; this.name = 'GitLabError'; } } exports.GitLabError = GitLabError; /** * Authentication error class for GitLab authentication failures */ class GitLabAuthError extends GitLabError { constructor(message, url) { super(message, 401, url); this.name = 'GitLabAuthError'; } } exports.GitLabAuthError = GitLabAuthError; /** * Creates authentication headers for GitLab requests * * @returns Record<string, string> - Headers object with authentication if token is available */ function createAuthHeaders() { const headers = { 'User-Agent': 'awesome-components-mcp/1.0.0' }; if (config_js_1.config.auth.gitlabToken) { headers['Authorization'] = `Bearer ${config_js_1.config.auth.gitlabToken}`; console.log('Using GitLab Personal Access Token for authentication'); } else { console.log('No GitLab token provided, attempting public access'); } return headers; } /** * Fetches text content from a GitLab URL with authentication support * * @param url - The GitLab URL to fetch content from * @returns Promise<string> - The fetched content as a string * @throws {GitLabError} - When the request fails or returns an error status * @throws {GitLabAuthError} - When authentication fails */ async function fetchGitLabFileContent(url) { try { console.log(`Fetching content from GitLab URL: ${url}`); const headers = createAuthHeaders(); const response = await fetch(url, { headers }); if (!response.ok) { // Log detailed error for server-side diagnostics (without exposing sensitive info) console.error(`GitLab request failed: ${response.status} ${response.statusText}`); // Create specific error messages based on status codes let errorMessage; switch (response.status) { case 401: errorMessage = `Authentication failed (status: 401). Please check your GitLab Personal Access Token.`; throw new GitLabAuthError(errorMessage, url); case 403: if (config_js_1.config.auth.gitlabToken) { errorMessage = `Access forbidden (status: 403). Your token may not have sufficient permissions for this repository.`; } else { errorMessage = `Access forbidden (status: 403). This repository may be private and require authentication. Please provide a GitLab Personal Access Token.`; } break; case 404: errorMessage = `File not found (status: 404). Please check if the file exists and the path is correct.`; break; case 500: case 502: case 503: case 504: errorMessage = `GitLab server error (status: ${response.status}). Please try again later.`; break; default: errorMessage = `Failed to fetch content from GitLab (status: ${response.status}).`; } // Throw an error that can be caught by the tool and handled by MCP SDK throw new GitLabError(errorMessage, response.status, url); } const content = await response.text(); console.log(`Successfully fetched ${content.length} characters from GitLab`); return content; } catch (error) { // If it's already a GitLabError or GitLabAuthError, re-throw it if (error instanceof GitLabError) { throw error; } // Handle network errors and other fetch-related errors console.error(`Network or other error fetching from GitLab:`, error); // Create a more user-friendly error message for network issues const networkError = new GitLabError(`Network error while fetching from GitLab: ${error instanceof Error ? error.message : 'Unknown error'}`, undefined, url); throw networkError; } } /** * Validates if a URL appears to be a valid GitLab URL * * @param url - The URL to validate * @returns boolean - True if the URL appears to be a valid GitLab URL */ function isValidGitLabUrl(url) { try { const urlObj = new URL(url); // Check if it's a valid HTTP/HTTPS URL if (!['http:', 'https:'].includes(urlObj.protocol)) { return false; } // Check if it contains GitLab-like patterns const hostname = urlObj.hostname.toLowerCase(); const pathname = urlObj.pathname.toLowerCase(); // Common GitLab patterns - must contain 'gitlab' in hostname const isGitLabHostname = hostname.includes('gitlab'); const hasRawPath = pathname.includes('/-/raw/'); return isGitLabHostname && hasRawPath; } catch { return false; } } /** * Validates authentication configuration for private repositories * * @param url - The GitLab URL to check * @returns boolean - True if authentication is properly configured or not needed */ function validateAuthenticationConfig(url) { // For public repositories, authentication is optional // For private repositories, authentication is required if (!config_js_1.config.auth.gitlabToken) { return { isValid: true, message: 'No authentication token provided. This will work for public repositories only.' }; } // Basic token validation const token = config_js_1.config.auth.gitlabToken.trim(); if (token.length < 10) { return { isValid: false, message: 'GitLab Personal Access Token appears to be invalid (too short).' }; } return { isValid: true, message: 'Authentication token is configured.' }; } /** * Fetches content with URL validation and authentication support * * @param url - The GitLab URL to fetch content from * @returns Promise<string> - The fetched content as a string * @throws {GitLabError} - When the URL is invalid or the request fails * @throws {GitLabAuthError} - When authentication fails */ async function fetchGitLabFileContentSafe(url) { if (!isValidGitLabUrl(url)) { throw new GitLabError(`Invalid GitLab URL format. Expected a GitLab raw file URL.`, undefined, url); } // Validate authentication configuration const authValidation = validateAuthenticationConfig(url); if (!authValidation.isValid) { throw new GitLabAuthError(authValidation.message || 'Authentication configuration is invalid', url); } return fetchGitLabFileContent(url); }