UNPKG

sourcegraph-mcp-server

Version:

A Model Context Protocol (MCP) server that connects AI assistants to Sourcegraph code search with natural language capabilities

218 lines (217 loc) 7.39 kB
"use strict"; /** * Repository Content Service * * Provides access to Sourcegraph's API for retrieving repository content such as: * - File content * - File blame information */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.formatFileBlameResults = exports.formatFileContentResults = exports.getFileBlameQuery = exports.getFileContentQuery = exports.executeSourcegraphQuery = exports.getSourcegraphConfig = void 0; const axios_1 = __importDefault(require("axios")); const dotenv_1 = __importDefault(require("dotenv")); // Load environment variables dotenv_1.default.config(); /** * Get Sourcegraph configuration from environment or provided config */ const getSourcegraphConfig = (config) => { const url = config?.url || process.env.SOURCEGRAPH_URL; const token = config?.token || process.env.SOURCEGRAPH_TOKEN; if (!url || !token) { throw new Error('Sourcegraph URL or token not configured. Please set SOURCEGRAPH_URL and SOURCEGRAPH_TOKEN environment variables.'); } return { url, token }; }; exports.getSourcegraphConfig = getSourcegraphConfig; /** * Execute a GraphQL query against the Sourcegraph API */ async function executeSourcegraphQuery(graphqlQuery, variables, sourcegraphConfig) { // Get configuration const config = (0, exports.getSourcegraphConfig)(sourcegraphConfig); // Headers for Sourcegraph API const headers = { 'Authorization': `token ${config.token}`, 'Content-Type': 'application/json' }; try { // Make the request to Sourcegraph API const response = await axios_1.default.post(`${config.url}/.api/graphql`, { query: graphqlQuery, variables }, { headers }); return response.data; } catch (error) { // Format error for better debugging if (error.response) { throw new Error(`Sourcegraph API error (${error.response.status}): ${JSON.stringify(error.response.data)}`); } else if (error.request) { throw new Error(`No response from Sourcegraph API: ${error.message}`); } else { throw new Error(`Error setting up request: ${error.message}`); } } } exports.executeSourcegraphQuery = executeSourcegraphQuery; /** * Get GraphQL query for file content */ function getFileContentQuery() { return ` query FileContent($repository: String!, $path: String!, $revision: String) { repository(name: $repository) { commit(rev: $revision, default: "HEAD") { blob(path: $path) { content byteSize binary } } } } `; } exports.getFileContentQuery = getFileContentQuery; /** * Get GraphQL query for file blame information */ function getFileBlameQuery() { return ` query FileBlame($repository: String!, $path: String!, $startLine: Int!, $endLine: Int!) { repository(name: $repository) { commit(rev: "HEAD") { blob(path: $path) { blame(startLine: $startLine, endLine: $endLine) { startLine endLine author email date message commit { oid abbrevOid message author { person { name email } date } } } } } } } `; } exports.getFileBlameQuery = getFileBlameQuery; /** * Format file content results to readable output */ function formatFileContentResults(data, params) { // Handle error cases if (!data?.repository?.commit?.blob) { return "File not found or repository access error."; } const blob = data.repository.commit.blob; if (blob.binary) { return `Binary file detected (${blob.byteSize} bytes). Cannot display binary file content.`; } // Format for display let result = `## File Content: ${params.repository}:${params.path}`; if (params.revision) { result += ` @ ${params.revision}`; } result += "\n\n"; // Add file size info result += `File size: ${formatFileSize(blob.byteSize)}\n\n`; // Get file extension for syntax highlighting const fileExtension = params.path.split('.').pop() || ''; // Add the content with proper syntax highlighting result += "```" + fileExtension + "\n"; result += blob.content; result += "\n```"; return result; } exports.formatFileContentResults = formatFileContentResults; /** * Format file blame results to readable output */ function formatFileBlameResults(data, params) { // Handle error cases if (!data?.repository?.commit?.blob?.blame) { return "Blame information not available or repository access error."; } const blameEntries = data.repository.commit.blob.blame; if (blameEntries.length === 0) { return "No blame information available for the specified lines."; } // Format for display let result = `## Blame Information: ${params.repository}:${params.path} (Lines ${params.startLine + 1}-${params.endLine + 1})\n\n`; // Format blame entries with table result += "| Lines | Author | Date | Commit | Message |\n"; result += "|-------|--------|------|--------|---------|\n"; blameEntries.forEach((entry) => { const startLine = entry.startLine + 1; // Convert to 1-indexed for display const endLine = entry.endLine + 1; // Convert to 1-indexed for display const lineRange = startLine === endLine ? `${startLine}` : `${startLine}-${endLine}`; const author = entry.commit?.author?.person?.name || entry.author || "Unknown"; const date = formatDate(entry.commit?.author?.date || entry.date); const commitId = entry.commit?.abbrevOid || "Unknown"; const message = formatCommitMessage(entry.commit?.message || entry.message || "No message"); result += `| ${lineRange} | ${author} | ${date} | ${commitId} | ${message} |\n`; }); return result; } exports.formatFileBlameResults = formatFileBlameResults; /** * Format file size to human-readable format */ function formatFileSize(bytes) { if (bytes < 1024) { return `${bytes} bytes`; } else if (bytes < 1024 * 1024) { return `${(bytes / 1024).toFixed(2)} KB`; } else if (bytes < 1024 * 1024 * 1024) { return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; } else { return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; } } /** * Format date to readable format */ function formatDate(dateStr) { if (!dateStr) return "Unknown"; try { const date = new Date(dateStr); return date.toISOString().split('T')[0]; // YYYY-MM-DD format } catch (e) { return dateStr; } } /** * Format commit message (truncate if too long) */ function formatCommitMessage(message) { if (!message) return "No message"; // Extract first line and truncate if needed const firstLine = message.split('\n')[0]; if (firstLine.length > 50) { return firstLine.substring(0, 47) + "..."; } return firstLine; }