/**
* @fileoverview Configuration management for DeepSource MCP Server
* @packageDocumentation
*/
import { createLogger } from '../utils/logging/logger.js';
import { MCPErrorFactory } from '../utils/error-handling/index.js';
const logger = createLogger('DeepSourceMCP:Config');
/**
* Configuration interface for the DeepSource MCP Server
*/
export interface DeepSourceConfig {
/** DeepSource API key for authentication */
apiKey: string;
/** Base URL for DeepSource API */
apiBaseUrl: string;
/** Request timeout in milliseconds */
requestTimeout: number;
/** Log file path (optional) */
logFile?: string;
/** Log level */
logLevel: string;
}
/**
* Default configuration values
*/
const defaultConfig: Partial<DeepSourceConfig> = {
apiBaseUrl: 'https://api.deepsource.io/graphql/',
requestTimeout: 30000,
logLevel: 'DEBUG',
};
/**
* Validates the configuration
* @throws MCPError if configuration is invalid
*/
function validateConfig(config: Partial<DeepSourceConfig>): DeepSourceConfig {
if (!config.apiKey) {
throw MCPErrorFactory.configuration('DeepSource API key is required but not configured', {
environmentVariable: 'DEEPSOURCE_API_KEY',
documentation:
'Please set the DEEPSOURCE_API_KEY environment variable with your DeepSource API key',
});
}
// Validate timeout is a positive number
if (
config.requestTimeout &&
(typeof config.requestTimeout !== 'number' || config.requestTimeout <= 0)
) {
throw MCPErrorFactory.configuration('Request timeout must be a positive number', {
provided: config.requestTimeout,
environmentVariable: 'DEEPSOURCE_REQUEST_TIMEOUT',
});
}
return config as DeepSourceConfig;
}
/**
* Loads configuration from environment variables
*/
function loadConfigFromEnv(): Partial<DeepSourceConfig> {
const config: Partial<DeepSourceConfig> = {};
if (process.env.DEEPSOURCE_API_KEY) {
config.apiKey = process.env.DEEPSOURCE_API_KEY;
}
if (process.env.DEEPSOURCE_API_BASE_URL) {
config.apiBaseUrl = process.env.DEEPSOURCE_API_BASE_URL;
}
if (process.env.DEEPSOURCE_REQUEST_TIMEOUT) {
config.requestTimeout = parseInt(process.env.DEEPSOURCE_REQUEST_TIMEOUT, 10);
}
if (process.env.LOG_FILE) {
config.logFile = process.env.LOG_FILE;
}
if (process.env.LOG_LEVEL) {
config.logLevel = process.env.LOG_LEVEL;
}
return config;
}
/**
* Gets the current configuration
* @returns The validated configuration
* @throws MCPError if configuration is invalid
*/
export function getConfig(): DeepSourceConfig {
const envConfig = loadConfigFromEnv();
// Merge with defaults
const mergedConfig = {
...defaultConfig,
...Object.fromEntries(Object.entries(envConfig).filter(([, value]) => value !== undefined)),
};
logger.debug('Loading configuration', {
hasApiKey: Boolean(mergedConfig.apiKey),
apiBaseUrl: mergedConfig.apiBaseUrl,
requestTimeout: mergedConfig.requestTimeout,
logLevel: mergedConfig.logLevel,
});
return validateConfig(mergedConfig);
}
/**
* Checks if the API key is configured
* @returns True if API key is set
*/
export function hasApiKey(): boolean {
return Boolean(process.env.DEEPSOURCE_API_KEY);
}
/**
* Gets the API key from configuration
* @returns The API key
* @throws MCPError if API key is not set
*/
export function getApiKey(): string {
logger.debug('getApiKey() called', {
hasEnvVar: Boolean(process.env.DEEPSOURCE_API_KEY),
envVarLength: process.env.DEEPSOURCE_API_KEY?.length,
keyPrefix: process.env.DEEPSOURCE_API_KEY?.substring(0, 8),
});
const config = getConfig();
logger.debug('API key retrieved from config', {
hasApiKey: Boolean(config.apiKey),
apiKeyLength: config.apiKey?.length,
keyPrefix: config.apiKey?.substring(0, 8),
});
return config.apiKey;
}
// Re-export feature flags
export {
getFeatureFlags,
isFeatureEnabled,
getEnabledFeatures,
type FeatureFlags,
} from './features.js';
// Re-export default configurations
export {
DEFAULT_SERVER_CONFIG,
DEFAULT_API_CONFIG,
DEFAULT_DISCOVERY_CONFIG,
getEnvironmentConfig,
ToolCategory,
ToolTag,
type ServerConfig,
type ApiConfig,
type DiscoveryConfig,
} from './default.js';