@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
JavaScript
;
/**
* 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