UNPKG

@graisol/gpt-image-mcp

Version:

A Model Context Protocol (MCP) server for OpenAI GPT-Image-1 image generation and editing

179 lines 6.73 kB
"use strict"; /** * OpenAI Client wrapper for GPT-Image-1 API integration */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenAIClient = void 0; const openai_1 = __importDefault(require("openai")); class OpenAIClient { client; config; constructor(config) { this.config = config; this.client = new openai_1.default({ apiKey: config.openai_api_key, organization: config.openai_org_id, }); } /** * Generate images using OpenAI's GPT-Image-1 model */ async generateImage(request) { try { // Build parameters explicitly for GPT-Image-1 const params = { model: 'gpt-image-1', prompt: request.prompt, n: 1, // GPT-Image-1 supports only 1 image per request }; // Add optional parameters only if they're specified if (request.size) { params.size = request.size; } if (request.quality) { params.quality = request.quality; } if (request.moderation) { params.moderation = request.moderation; } console.log('Sending params to OpenAI:', JSON.stringify(params, null, 2)); const response = await this.client.images.generate(params); const imageData = response.data?.[0]; if (!imageData) { throw new Error('No image data received from OpenAI API'); } const metadata = { id: this.generateId(request.prompt), created: Date.now(), prompt: request.prompt, revised_prompt: imageData.revised_prompt, size: request.size || this.config.default_size, quality: request.quality || this.config.default_quality, url: imageData.url, b64_json: imageData.b64_json, }; return metadata; } catch (error) { throw this.handleOpenAIError(error); } } /** * Edit images using OpenAI's GPT-Image-1 model */ async editImage(request) { try { // Note: Image editing with GPT-Image-1 might use the chat completions API // with vision capabilities rather than the traditional image edit endpoint // This is a simplified implementation - actual implementation may vary // For now, we'll use a placeholder implementation since GPT-Image-1 editing // may work differently than traditional DALL-E editing throw new Error('Image editing with GPT-Image-1 is not yet fully supported. Please use generation instead.'); } catch (error) { throw this.handleOpenAIError(error); } } /** * Validate image prompt for content policy compliance */ async validatePrompt(prompt) { try { const response = await this.client.moderations.create({ input: prompt, }); return !response.results[0].flagged; } catch (error) { console.error('Error validating prompt:', error); return false; } } /** * Generate a unique ID for image metadata based on prompt */ generateId(prompt) { const sanitizedPrompt = this.sanitizePromptForFilename(prompt); const timestamp = Date.now(); return `img_${timestamp}_${sanitizedPrompt}`; } /** * Sanitize prompt text for use in filenames */ sanitizePromptForFilename(prompt) { return prompt .toLowerCase() .replace(/[^a-z0-9\s-]/g, '') // Remove special characters except spaces and hyphens .replace(/\s+/g, '_') // Replace spaces with underscores .substring(0, 50) // Limit to 50 characters .replace(/^_+|_+$/g, ''); // Remove leading/trailing underscores } /** * Handle OpenAI API errors and convert them to our error format */ handleOpenAIError(error) { if (error.response) { const { status, data } = error.response; switch (status) { case 400: return new Error(`Bad Request: ${data.error?.message || 'Invalid request parameters'}`); case 401: return new Error('Unauthorized: Invalid API key'); case 403: return new Error('Forbidden: Access denied or organization verification required'); case 404: return new Error('Not Found: Model or endpoint not found'); case 429: return new Error('Rate Limited: Too many requests. Please try again later.'); case 500: return new Error('Internal Server Error: OpenAI service is temporarily unavailable'); case 503: return new Error('Service Unavailable: OpenAI service is overloaded'); default: return new Error(`OpenAI API Error (${status}): ${data.error?.message || 'Unknown error'}`); } } if (error.code === 'ECONNREFUSED') { return new Error('Connection Error: Unable to connect to OpenAI API'); } if (error.code === 'ETIMEDOUT') { return new Error('Timeout Error: Request to OpenAI API timed out'); } return new Error(`OpenAI Client Error: ${error.message || 'Unknown error occurred'}`); } /** * Test the OpenAI API connection */ async testConnection() { try { await this.client.models.list(); return true; } catch (error) { console.error('OpenAI connection test failed:', error); return false; } } /** * Get current usage information (if available) */ async getUsageInfo() { try { // Note: Usage information retrieval depends on OpenAI's API capabilities // This is a placeholder implementation return { message: 'Usage information not available through current OpenAI API', timestamp: new Date().toISOString(), }; } catch (error) { console.error('Error getting usage info:', error); return null; } } } exports.OpenAIClient = OpenAIClient; //# sourceMappingURL=openai-client.js.map