Skip to main content
Glama

MCP Gemini Server

by bsmi021
ConfigurationManager.ts30.6 kB
import * as path from "path"; import { ExampleServiceConfig, GeminiServiceConfig, ModelConfiguration, ModelCapabilitiesMap, } from "../types/index.js"; import { FileSecurityService } from "../utils/FileSecurityService.js"; import { ModelMigrationService } from "../services/gemini/ModelMigrationService.js"; import { logger } from "../utils/logger.js"; // Define the structure for all configurations managed interface ManagedConfigs { exampleService: Required<ExampleServiceConfig>; geminiService: GeminiServiceConfig; github: { apiToken: string; }; allowedOutputPaths: string[]; mcpConfig: { host: string; port: number; connectionToken: string; clientId: string; logLevel?: "debug" | "info" | "warn" | "error"; transport?: "stdio" | "sse"; enableStreaming?: boolean; sessionTimeoutSeconds?: number; }; urlContext: { enabled: boolean; maxUrlsPerRequest: number; defaultMaxContentKb: number; defaultTimeoutMs: number; allowedDomains: string[]; blocklistedDomains: string[]; convertToMarkdown: boolean; includeMetadata: boolean; enableCaching: boolean; cacheExpiryMinutes: number; maxCacheSize: number; rateLimitPerDomainPerMinute: number; userAgent: string; }; modelConfiguration: ModelConfiguration; } /** * Centralized configuration management for all services. * Implements singleton pattern to ensure consistent configuration. */ export class ConfigurationManager { private static instance: ConfigurationManager | null = null; private static instanceLock = false; private config: ManagedConfigs; private constructor() { // Initialize with default configurations this.config = { exampleService: { // Define defaults for ExampleService greeting: "Hello", enableDetailedLogs: false, }, geminiService: { apiKey: "", defaultModel: undefined, defaultImageResolution: "1024x1024", maxImageSizeMB: 10, supportedImageFormats: ["image/jpeg", "image/png", "image/webp"], defaultThinkingBudget: undefined, }, modelConfiguration: this.buildDefaultModelConfiguration(), github: { // Default GitHub API token is empty; will be loaded from environment variable apiToken: "", }, allowedOutputPaths: [], mcpConfig: { // Initialize MCP config host: "localhost", port: 8080, connectionToken: "", // Must be set via env clientId: "gemini-sdk-client", logLevel: "info", transport: "stdio", }, urlContext: { // Initialize URL context config with secure defaults enabled: false, // Disabled by default for security maxUrlsPerRequest: 20, defaultMaxContentKb: 100, defaultTimeoutMs: 10000, allowedDomains: ["*"], // Allow all by default (can be restricted) blocklistedDomains: [], // Empty by default convertToMarkdown: true, includeMetadata: true, enableCaching: true, cacheExpiryMinutes: 15, maxCacheSize: 1000, rateLimitPerDomainPerMinute: 10, userAgent: "MCP-Gemini-Server/1.0 (+https://github.com/bsmi021/mcp-gemini-server)", }, // Initialize other service configs with defaults: // yourService: { // someSetting: 'default value', // retryCount: 3, // }, }; const migrationService = ModelMigrationService.getInstance(); migrationService.migrateEnvironmentVariables(); const validation = migrationService.validateConfiguration(); if (!validation.isValid) { logger.error("[ConfigurationManager] Configuration validation failed", { errors: validation.errors, }); } const deprecated = migrationService.getDeprecatedFeatures(); if (deprecated.length > 0) { logger.warn("[ConfigurationManager] Deprecated features detected", { deprecated, }); } this.validateRequiredEnvVars(); this.loadEnvironmentOverrides(); this.config.modelConfiguration = this.parseModelConfiguration(); FileSecurityService.configureFromEnvironment(); } private validateRequiredEnvVars(): void { // Skip validation in test environment if (process.env.NODE_ENV === "test") { logger.info( "Skipping environment variable validation in test environment" ); return; } // Always require Gemini API key const requiredVars = ["GOOGLE_GEMINI_API_KEY"]; // Check transport type to determine if MCP server variables are required const transportType = process.env.MCP_TRANSPORT || process.env.MCP_TRANSPORT_TYPE || "stdio"; // Only require MCP server variables for HTTP/SSE transport modes // Note: MCP_CLIENT_ID is not required as it's optional with a default value if ( transportType === "http" || transportType === "sse" || transportType === "streamable" ) { requiredVars.push( "MCP_SERVER_HOST", "MCP_SERVER_PORT", "MCP_CONNECTION_TOKEN" ); } const missingVars = requiredVars.filter((varName) => !process.env[varName]); if (missingVars.length > 0) { throw new Error( `Missing required environment variables: ${missingVars.join(", ")}` ); } } /** * Get the singleton instance of ConfigurationManager. * Basic lock to prevent race conditions during initial creation. */ public static getInstance(): ConfigurationManager { if (!ConfigurationManager.instance) { if (!ConfigurationManager.instanceLock) { ConfigurationManager.instanceLock = true; // Lock try { ConfigurationManager.instance = new ConfigurationManager(); } finally { ConfigurationManager.instanceLock = false; // Unlock } } else { // Basic busy wait if locked (consider a more robust async lock if high contention is expected) while (ConfigurationManager.instanceLock) { // Small delay to prevent tight loop const now = Date.now(); while (Date.now() - now < 10) { // Intentional minimal delay } } // Re-check instance after wait if (!ConfigurationManager.instance) { // This path is less likely but handles edge cases if lock logic needs refinement return ConfigurationManager.getInstance(); } } } return ConfigurationManager.instance; } // --- Getters for specific configurations --- public getExampleServiceConfig(): Required<ExampleServiceConfig> { // Return a copy to prevent accidental modification of the internal state return { ...this.config.exampleService }; } public getGeminiServiceConfig(): GeminiServiceConfig { // Return a copy to prevent accidental modification return { ...this.config.geminiService }; } // Getter for MCP Configuration public getMcpConfig(): Required<ManagedConfigs["mcpConfig"]> { // Return a copy to ensure type safety and prevent modification // Cast to Required because we validate essential fields are set from env vars. // Optional fields will have their defaults. return { ...this.config.mcpConfig } as Required< ManagedConfigs["mcpConfig"] >; } // Getter specifically for the default model name public getDefaultModelName(): string | undefined { return this.config.geminiService.defaultModel; } public getModelConfiguration(): ModelConfiguration { return { ...this.config.modelConfiguration }; } /** * Returns the GitHub API token for GitHub API requests * @returns The configured GitHub API token or undefined if not set */ public getGitHubApiToken(): string | undefined { return this.config.github.apiToken || undefined; } /** * Returns the list of allowed output paths for file writing * @returns A copy of the configured allowed output paths array */ public getAllowedOutputPaths(): string[] { // Return a copy to prevent accidental modification return [...this.config.allowedOutputPaths]; } /** * Returns the URL context configuration * @returns A copy of the URL context configuration */ public getUrlContextConfig(): Required<ManagedConfigs["urlContext"]> { return { ...this.config.urlContext }; } // Add getters for other service configs: // public getYourServiceConfig(): Required<YourServiceConfig> { // return { ...this.config.yourService }; // } // --- Updaters for specific configurations (if runtime updates are needed) --- public updateExampleServiceConfig( update: Partial<ExampleServiceConfig> ): void { this.config.exampleService = { ...this.config.exampleService, ...update, }; // Optional: Notify relevant services about the config change } // Add updaters for other service configs: // public updateYourServiceConfig(update: Partial<YourServiceConfig>): void { // this.config.yourService = { // ...this.config.yourService, // ...update, // }; // } /** * Example method to load configuration overrides from environment variables. * Call this in the constructor. */ private loadEnvironmentOverrides(): void { // Example for ExampleService if (process.env.EXAMPLE_GREETING) { this.config.exampleService.greeting = process.env.EXAMPLE_GREETING; } if (process.env.EXAMPLE_ENABLE_LOGS) { this.config.exampleService.enableDetailedLogs = process.env.EXAMPLE_ENABLE_LOGS.toLowerCase() === "true"; } // Load GitHub API token if provided if (process.env.GITHUB_API_TOKEN) { this.config.github.apiToken = process.env.GITHUB_API_TOKEN; logger.info("[ConfigurationManager] GitHub API token configured"); } else { logger.warn( "[ConfigurationManager] GITHUB_API_TOKEN environment variable not set. GitHub code review features may not work properly." ); } // Add logic for other services based on their environment variables // if (process.env.YOUR_SERVICE_RETRY_COUNT) { // const retryCount = parseInt(process.env.YOUR_SERVICE_RETRY_COUNT, 10); // if (!isNaN(retryCount)) { // this.config.yourService.retryCount = retryCount; // } // } // Load Gemini API Key (using the name from .env) if (process.env.GOOGLE_GEMINI_API_KEY) { this.config.geminiService.apiKey = process.env.GOOGLE_GEMINI_API_KEY; } else { // Log a warning if the key is missing, the service constructor will throw logger.warn( "[ConfigurationManager] WARNING: GOOGLE_GEMINI_API_KEY environment variable not set." ); } // Load Default Gemini Model Name if (process.env.GOOGLE_GEMINI_MODEL) { this.config.geminiService.defaultModel = process.env.GOOGLE_GEMINI_MODEL; logger.info( `[ConfigurationManager] Default Gemini model set to: ${this.config.geminiService.defaultModel}` ); } else { logger.info( "[ConfigurationManager] GOOGLE_GEMINI_MODEL environment variable not set. No default model configured." ); } // Load image-specific settings if provided if (process.env.GOOGLE_GEMINI_IMAGE_RESOLUTION) { const resolution = process.env.GOOGLE_GEMINI_IMAGE_RESOLUTION; if (["512x512", "1024x1024", "1536x1536"].includes(resolution)) { this.config.geminiService.defaultImageResolution = resolution as | "512x512" | "1024x1024" | "1536x1536"; logger.info( `[ConfigurationManager] Default image resolution set to: ${resolution}` ); } else { logger.warn( `[ConfigurationManager] Invalid image resolution '${resolution}' specified in GOOGLE_GEMINI_IMAGE_RESOLUTION. Using default.` ); } } if (process.env.GOOGLE_GEMINI_MAX_IMAGE_SIZE_MB) { const sizeMB = parseInt(process.env.GOOGLE_GEMINI_MAX_IMAGE_SIZE_MB, 10); if (!isNaN(sizeMB) && sizeMB > 0) { this.config.geminiService.maxImageSizeMB = sizeMB; logger.info( `[ConfigurationManager] Maximum image size set to: ${sizeMB}MB` ); } else { logger.warn( `[ConfigurationManager] Invalid max image size '${process.env.GOOGLE_GEMINI_MAX_IMAGE_SIZE_MB}' specified. Using default.` ); } } if (process.env.GOOGLE_GEMINI_SUPPORTED_IMAGE_FORMATS) { try { const formats = JSON.parse( process.env.GOOGLE_GEMINI_SUPPORTED_IMAGE_FORMATS ); if ( Array.isArray(formats) && formats.every((f) => typeof f === "string") ) { this.config.geminiService.supportedImageFormats = formats; logger.info( `[ConfigurationManager] Supported image formats set to: ${formats.join(", ")}` ); } else { throw new Error("Invalid format array"); } } catch (error) { logger.warn( `[ConfigurationManager] Invalid image formats specified in GOOGLE_GEMINI_SUPPORTED_IMAGE_FORMATS: '${process.env.GOOGLE_GEMINI_SUPPORTED_IMAGE_FORMATS}'. Using default.` ); } } // Load default thinking budget if provided if (process.env.GOOGLE_GEMINI_DEFAULT_THINKING_BUDGET) { const budget = parseInt( process.env.GOOGLE_GEMINI_DEFAULT_THINKING_BUDGET, 10 ); if (!isNaN(budget) && budget >= 0 && budget <= 24576) { this.config.geminiService.defaultThinkingBudget = budget; logger.info( `[ConfigurationManager] Default thinking budget set to: ${budget} tokens` ); } else { logger.warn( `[ConfigurationManager] Invalid thinking budget '${process.env.GOOGLE_GEMINI_DEFAULT_THINKING_BUDGET}' specified. Must be between 0 and 24576. Not using default thinking budget.` ); } } // Load MCP Configuration if (process.env.MCP_SERVER_HOST) { this.config.mcpConfig.host = process.env.MCP_SERVER_HOST; } if (process.env.MCP_SERVER_PORT) { const port = parseInt(process.env.MCP_SERVER_PORT, 10); if (!isNaN(port) && port > 0 && port < 65536) { this.config.mcpConfig.port = port; } else { logger.warn( `[ConfigurationManager] Invalid MCP_SERVER_PORT: '${process.env.MCP_SERVER_PORT}'. Using default ${this.config.mcpConfig.port}.` ); } } if (process.env.MCP_CONNECTION_TOKEN) { this.config.mcpConfig.connectionToken = process.env.MCP_CONNECTION_TOKEN; } if (process.env.MCP_CLIENT_ID) { this.config.mcpConfig.clientId = process.env.MCP_CLIENT_ID; } if (process.env.MCP_LOG_LEVEL) { const logLevel = process.env.MCP_LOG_LEVEL.toLowerCase(); if (["debug", "info", "warn", "error"].includes(logLevel)) { this.config.mcpConfig.logLevel = logLevel as | "debug" | "info" | "warn" | "error"; } else { logger.warn( `[ConfigurationManager] Invalid MCP_LOG_LEVEL: '${process.env.MCP_LOG_LEVEL}'. Using default '${this.config.mcpConfig.logLevel}'.` ); } } if (process.env.MCP_TRANSPORT) { const transport = process.env.MCP_TRANSPORT.toLowerCase(); if (["stdio", "sse"].includes(transport)) { this.config.mcpConfig.transport = transport as "stdio" | "sse"; } else { logger.warn( `[ConfigurationManager] Invalid MCP_TRANSPORT: '${process.env.MCP_TRANSPORT}'. Using default '${this.config.mcpConfig.transport}'.` ); } } if (process.env.MCP_ENABLE_STREAMING) { this.config.mcpConfig.enableStreaming = process.env.MCP_ENABLE_STREAMING.toLowerCase() === "true"; logger.info( `[ConfigurationManager] MCP streaming enabled: ${this.config.mcpConfig.enableStreaming}` ); } if (process.env.MCP_SESSION_TIMEOUT) { const timeout = parseInt(process.env.MCP_SESSION_TIMEOUT, 10); if (!isNaN(timeout) && timeout > 0) { this.config.mcpConfig.sessionTimeoutSeconds = timeout; logger.info( `[ConfigurationManager] MCP session timeout set to: ${timeout} seconds` ); } else { logger.warn( `[ConfigurationManager] Invalid MCP_SESSION_TIMEOUT: '${process.env.MCP_SESSION_TIMEOUT}'. Using default.` ); } } logger.info("[ConfigurationManager] MCP configuration loaded."); // Load URL Context Configuration if (process.env.GOOGLE_GEMINI_ENABLE_URL_CONTEXT) { this.config.urlContext.enabled = process.env.GOOGLE_GEMINI_ENABLE_URL_CONTEXT.toLowerCase() === "true"; logger.info( `[ConfigurationManager] URL context feature enabled: ${this.config.urlContext.enabled}` ); } if (process.env.GOOGLE_GEMINI_URL_MAX_COUNT) { const maxCount = parseInt(process.env.GOOGLE_GEMINI_URL_MAX_COUNT, 10); if (!isNaN(maxCount) && maxCount > 0 && maxCount <= 20) { this.config.urlContext.maxUrlsPerRequest = maxCount; logger.info(`[ConfigurationManager] URL max count set to: ${maxCount}`); } else { logger.warn( `[ConfigurationManager] Invalid URL max count '${process.env.GOOGLE_GEMINI_URL_MAX_COUNT}'. Must be between 1 and 20.` ); } } if (process.env.GOOGLE_GEMINI_URL_MAX_CONTENT_KB) { const maxKb = parseInt(process.env.GOOGLE_GEMINI_URL_MAX_CONTENT_KB, 10); if (!isNaN(maxKb) && maxKb > 0 && maxKb <= 1000) { this.config.urlContext.defaultMaxContentKb = maxKb; logger.info( `[ConfigurationManager] URL max content size set to: ${maxKb}KB` ); } else { logger.warn( `[ConfigurationManager] Invalid URL max content size '${process.env.GOOGLE_GEMINI_URL_MAX_CONTENT_KB}'. Must be between 1 and 1000 KB.` ); } } if (process.env.GOOGLE_GEMINI_URL_FETCH_TIMEOUT_MS) { const timeout = parseInt( process.env.GOOGLE_GEMINI_URL_FETCH_TIMEOUT_MS, 10 ); if (!isNaN(timeout) && timeout >= 1000 && timeout <= 30000) { this.config.urlContext.defaultTimeoutMs = timeout; logger.info( `[ConfigurationManager] URL fetch timeout set to: ${timeout}ms` ); } else { logger.warn( `[ConfigurationManager] Invalid URL fetch timeout '${process.env.GOOGLE_GEMINI_URL_FETCH_TIMEOUT_MS}'. Must be between 1000 and 30000 ms.` ); } } if (process.env.GOOGLE_GEMINI_URL_ALLOWED_DOMAINS) { try { const domains = this.parseStringArray( process.env.GOOGLE_GEMINI_URL_ALLOWED_DOMAINS ); this.config.urlContext.allowedDomains = domains; logger.info( `[ConfigurationManager] URL allowed domains set to: ${domains.join(", ")}` ); } catch (error) { logger.warn( `[ConfigurationManager] Invalid URL allowed domains format: ${error}` ); } } if (process.env.GOOGLE_GEMINI_URL_BLOCKLIST) { try { const domains = this.parseStringArray( process.env.GOOGLE_GEMINI_URL_BLOCKLIST ); this.config.urlContext.blocklistedDomains = domains; logger.info( `[ConfigurationManager] URL blocklisted domains set to: ${domains.join(", ")}` ); } catch (error) { logger.warn( `[ConfigurationManager] Invalid URL blocklist format: ${error}` ); } } if (process.env.GOOGLE_GEMINI_URL_CONVERT_TO_MARKDOWN) { this.config.urlContext.convertToMarkdown = process.env.GOOGLE_GEMINI_URL_CONVERT_TO_MARKDOWN.toLowerCase() === "true"; logger.info( `[ConfigurationManager] URL markdown conversion enabled: ${this.config.urlContext.convertToMarkdown}` ); } if (process.env.GOOGLE_GEMINI_URL_INCLUDE_METADATA) { this.config.urlContext.includeMetadata = process.env.GOOGLE_GEMINI_URL_INCLUDE_METADATA.toLowerCase() === "true"; logger.info( `[ConfigurationManager] URL metadata inclusion enabled: ${this.config.urlContext.includeMetadata}` ); } if (process.env.GOOGLE_GEMINI_URL_ENABLE_CACHING) { this.config.urlContext.enableCaching = process.env.GOOGLE_GEMINI_URL_ENABLE_CACHING.toLowerCase() === "true"; logger.info( `[ConfigurationManager] URL caching enabled: ${this.config.urlContext.enableCaching}` ); } if (process.env.GOOGLE_GEMINI_URL_USER_AGENT) { this.config.urlContext.userAgent = process.env.GOOGLE_GEMINI_URL_USER_AGENT; logger.info( `[ConfigurationManager] URL user agent set to: ${this.config.urlContext.userAgent}` ); } logger.info("[ConfigurationManager] URL context configuration loaded."); this.config.allowedOutputPaths = []; const allowedOutputPathsEnv = process.env.ALLOWED_OUTPUT_PATHS; if (allowedOutputPathsEnv && allowedOutputPathsEnv.trim().length > 0) { const pathsArray = allowedOutputPathsEnv .split(",") .map((p) => p.trim()) // Trim whitespace from each path .filter((p) => p.length > 0); // Filter out any empty strings resulting from split if (pathsArray.length > 0) { this.config.allowedOutputPaths = pathsArray.map((p) => path.resolve(p)); // Resolve to absolute paths logger.info( `[ConfigurationManager] Allowed output paths configured: ${this.config.allowedOutputPaths.join( ", " )}` ); } else { // This case handles if ALLOWED_OUTPUT_PATHS was something like ",," or " , " logger.warn( "[ConfigurationManager] ALLOWED_OUTPUT_PATHS environment variable was provided but contained no valid paths after trimming. File writing might be restricted." ); } } else { logger.warn( "[ConfigurationManager] ALLOWED_OUTPUT_PATHS environment variable not set or is empty. File writing might be restricted or disabled." ); } } private buildDefaultModelConfiguration(): ModelConfiguration { return { default: "gemini-2.5-flash-preview-05-20", textGeneration: [ "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash-preview-05-20", "gemini-2.0-flash", "gemini-1.5-pro", "gemini-1.5-flash", ], imageGeneration: [ "imagen-3.0-generate-002", "gemini-2.0-flash-preview-image-generation", ], videoGeneration: ["veo-2.0-generate-001"], codeReview: [ "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash-preview-05-20", "gemini-2.0-flash", ], complexReasoning: [ "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash-preview-05-20", ], capabilities: this.buildCapabilitiesMap(), routing: { preferCostEffective: false, preferSpeed: false, preferQuality: true, }, }; } private buildCapabilitiesMap(): ModelCapabilitiesMap { return { "gemini-2.5-pro-preview-05-06": { textGeneration: true, imageInput: true, videoInput: true, audioInput: true, imageGeneration: false, videoGeneration: false, codeExecution: "excellent", complexReasoning: "excellent", costTier: "high", speedTier: "medium", maxTokens: 65536, contextWindow: 1048576, supportsFunctionCalling: true, supportsSystemInstructions: true, supportsCaching: true, }, "gemini-2.5-flash-preview-05-20": { textGeneration: true, imageInput: true, videoInput: true, audioInput: true, imageGeneration: false, videoGeneration: false, codeExecution: "excellent", complexReasoning: "excellent", costTier: "medium", speedTier: "fast", maxTokens: 65536, contextWindow: 1048576, supportsFunctionCalling: true, supportsSystemInstructions: true, supportsCaching: true, }, "gemini-2.0-flash": { textGeneration: true, imageInput: true, videoInput: true, audioInput: true, imageGeneration: false, videoGeneration: false, codeExecution: "good", complexReasoning: "good", costTier: "medium", speedTier: "fast", maxTokens: 8192, contextWindow: 1048576, supportsFunctionCalling: true, supportsSystemInstructions: true, supportsCaching: true, }, "gemini-2.0-flash-preview-image-generation": { textGeneration: true, imageInput: true, videoInput: false, audioInput: false, imageGeneration: true, videoGeneration: false, codeExecution: "basic", complexReasoning: "basic", costTier: "medium", speedTier: "medium", maxTokens: 8192, contextWindow: 32000, supportsFunctionCalling: false, supportsSystemInstructions: true, supportsCaching: false, }, "gemini-1.5-pro": { textGeneration: true, imageInput: true, videoInput: true, audioInput: true, imageGeneration: false, videoGeneration: false, codeExecution: "good", complexReasoning: "good", costTier: "high", speedTier: "medium", maxTokens: 8192, contextWindow: 2000000, supportsFunctionCalling: true, supportsSystemInstructions: true, supportsCaching: true, }, "gemini-1.5-flash": { textGeneration: true, imageInput: true, videoInput: true, audioInput: true, imageGeneration: false, videoGeneration: false, codeExecution: "basic", complexReasoning: "basic", costTier: "low", speedTier: "fast", maxTokens: 8192, contextWindow: 1000000, supportsFunctionCalling: true, supportsSystemInstructions: true, supportsCaching: true, }, "imagen-3.0-generate-002": { textGeneration: false, imageInput: false, videoInput: false, audioInput: false, imageGeneration: true, videoGeneration: false, codeExecution: "none", complexReasoning: "none", costTier: "medium", speedTier: "medium", maxTokens: 0, contextWindow: 0, supportsFunctionCalling: false, supportsSystemInstructions: false, supportsCaching: false, }, "veo-2.0-generate-001": { textGeneration: false, imageInput: true, videoInput: false, audioInput: false, imageGeneration: false, videoGeneration: true, codeExecution: "none", complexReasoning: "none", costTier: "high", speedTier: "slow", maxTokens: 0, contextWindow: 0, supportsFunctionCalling: false, supportsSystemInstructions: true, supportsCaching: false, }, }; } private parseModelConfiguration(): ModelConfiguration { const textModels = this.parseModelArray("GOOGLE_GEMINI_MODELS") || this.parseModelArray("GOOGLE_GEMINI_TEXT_MODELS") || [ process.env.GOOGLE_GEMINI_MODEL || "gemini-2.5-flash-preview-05-20", ]; const imageModels = this.parseModelArray("GOOGLE_GEMINI_IMAGE_MODELS") || [ "imagen-3.0-generate-002", "gemini-2.0-flash-preview-image-generation", ]; const videoModels = this.parseModelArray("GOOGLE_GEMINI_VIDEO_MODELS") || [ "veo-2.0-generate-001", ]; const codeModels = this.parseModelArray("GOOGLE_GEMINI_CODE_MODELS") || [ "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash-preview-05-20", "gemini-2.0-flash", ]; return { default: process.env.GOOGLE_GEMINI_DEFAULT_MODEL || textModels[0], textGeneration: textModels, imageGeneration: imageModels, videoGeneration: videoModels, codeReview: codeModels, complexReasoning: textModels.filter((m) => this.isHighReasoningModel(m)), capabilities: this.buildCapabilitiesMap(), routing: this.parseRoutingPreferences(), }; } private parseModelArray(envVarName: string): string[] | null { const envValue = process.env[envVarName]; if (!envValue) return null; try { const parsed = JSON.parse(envValue); if ( Array.isArray(parsed) && parsed.every((item) => typeof item === "string") ) { return parsed; } logger.warn( `[ConfigurationManager] Invalid ${envVarName} format: expected JSON array of strings` ); return null; } catch (error) { logger.warn( `[ConfigurationManager] Failed to parse ${envVarName}: ${error}` ); return null; } } private isHighReasoningModel(modelName: string): boolean { const highReasoningModels = [ "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash-preview-05-20", "gemini-1.5-pro", ]; return highReasoningModels.includes(modelName); } private parseRoutingPreferences(): ModelConfiguration["routing"] { return { preferCostEffective: process.env.GOOGLE_GEMINI_ROUTING_PREFER_COST?.toLowerCase() === "true", preferSpeed: process.env.GOOGLE_GEMINI_ROUTING_PREFER_SPEED?.toLowerCase() === "true", preferQuality: process.env.GOOGLE_GEMINI_ROUTING_PREFER_QUALITY?.toLowerCase() === "true" || (!process.env.GOOGLE_GEMINI_ROUTING_PREFER_COST && !process.env.GOOGLE_GEMINI_ROUTING_PREFER_SPEED), }; } /** * Parse a comma-separated string or JSON array into a string array */ private parseStringArray(value: string): string[] { if (!value || value.trim() === "") { return []; } // Try to parse as JSON first if (value.trim().startsWith("[")) { try { const parsed = JSON.parse(value); if ( Array.isArray(parsed) && parsed.every((item) => typeof item === "string") ) { return parsed; } throw new Error("Not a string array"); } catch (error) { throw new Error(`Invalid JSON array format: ${error}`); } } // Parse as comma-separated string return value .split(",") .map((item) => item.trim()) .filter((item) => item.length > 0); } }

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/bsmi021/mcp-gemini-server'

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